/// <summary> /// INCLUDE: Do include in result document according path expression - Works only with DocumentLookup /// </summary> protected IEnumerable <BsonDocument> Include(IEnumerable <BsonDocument> source, BsonExpression path) { // cached services string last = null; Snapshot snapshot = null; IndexService indexer = null; DataService data = null; CollectionIndex index = null; IDocumentLookup lookup = null; foreach (var doc in source) { foreach (var value in path.Execute(doc, _pragmas.Collation) .Where(x => x.IsDocument || x.IsArray) .ToList()) { // if value is document, convert this ref document into full document (do another query) if (value.IsDocument) { DoInclude(value.AsDocument); } else { // if value is array, do same per item foreach (var item in value.AsArray .Where(x => x.IsDocument) .Select(x => x.AsDocument)) { DoInclude(item); } } } yield return(doc); } void DoInclude(BsonDocument value) { // works only if is a document var refId = value["$id"]; var refCol = value["$ref"]; // if has no reference, just go out if (refId.IsNull || !refCol.IsString) { return; } // do some cache re-using when is same $ref (almost always is the same $ref collection) if (last != refCol.AsString) { last = refCol.AsString; // initialize services snapshot = _transaction.CreateSnapshot(LockMode.Read, last, false); indexer = new IndexService(snapshot, _pragmas.Collation); data = new DataService(snapshot); lookup = new DatafileLookup(data, _pragmas.UtcDate, null); index = snapshot.CollectionPage?.PK; } // fill only if index and ref node exists if (index != null) { var node = indexer.Find(index, refId, false, Query.Ascending); if (node != null) { // load document based on dataBlock position var refDoc = lookup.Load(node); //do not remove $id value.Remove("$ref"); // copy values from refDocument into current documet (except _id - will keep $id) foreach (var element in refDoc.Where(x => x.Key != "_id")) { value[element.Key] = element.Value; } } else { // set in ref document that was not found value.Add("$missing", true); } } } }
public override IEnumerable <IndexNode> Execute(IndexService indexer, CollectionIndex index) { // if order are desc, swap start/end values var start = this.Order == Query.Ascending ? _start : _end; var end = this.Order == Query.Ascending ? _end : _start; var startEquals = this.Order == Query.Ascending ? _startEquals : _endEquals; var endEquals = this.Order == Query.Ascending ? _endEquals : _startEquals; // find first indexNode (or get from head/tail if Min/Max value) var first = start.Type == BsonType.MinValue ? indexer.GetNode(index.Head) : start.Type == BsonType.MaxValue ? indexer.GetNode(index.Tail) : indexer.Find(index, start, true, this.Order); var node = first; // if startsEquals, return all equals value from start linked list if (startEquals && node != null) { // going backward in same value list to get first value while (!node.GetNextPrev(0, -this.Order).IsEmpty&& ((node = indexer.GetNode(node.GetNextPrev(0, -this.Order))).Key.CompareTo(start) == 0)) { if (node.Key.IsMinValue || node.Key.IsMaxValue) { break; } yield return(node); } node = first; } // returns (or not) equals start value while (node != null) { var diff = node.Key.CompareTo(start); // if current value are not equals start, go out this loop if (diff != 0) { break; } if (startEquals && !(node.Key.IsMinValue || node.Key.IsMaxValue)) { yield return(node); } node = indexer.GetNode(node.GetNextPrev(0, this.Order)); } // navigate using next[0] do next node - if less or equals returns while (node != null) { var diff = node.Key.CompareTo(end); if (endEquals && diff == 0 && !(node.Key.IsMinValue || node.Key.IsMaxValue)) { yield return(node); } else if (diff == -this.Order && !(node.Key.IsMinValue || node.Key.IsMaxValue)) { yield return(node); } else { break; } node = indexer.GetNode(node.GetNextPrev(0, this.Order)); } }
public override IAsyncEnumerable <IndexNode> Execute(IndexService indexer, CollectionIndex index) { throw new NotImplementedException(); }
public override uint GetCost(CollectionIndex index) { return(100); // virtual index is always full scan }
/// <summary> /// Insert a new node index inside an collection index. /// </summary> private IndexNode AddNode(CollectionIndex index, BsonValue key, PageAddress dataBlock, byte level, IndexNode last) { // get a free index page for head note var bytesLength = IndexNode.GetNodeLength(level, key, out var keyLength); // test for index key maxlength if (keyLength > MAX_INDEX_KEY_LENGTH) { throw LiteException.InvalidIndexKey($"Index key must be less than {MAX_INDEX_KEY_LENGTH} bytes."); } var indexPage = _snapshot.GetFreeIndexPage(bytesLength, ref index.FreeIndexPageList); // create node in buffer var node = indexPage.InsertIndexNode(index.Slot, level, key, dataBlock, bytesLength); // now, let's link my index node on right place var cur = this.GetNode(index.Head); // using as cache last IndexNode cache = null; // scan from top left for (int i = index.MaxLevel - 1; i >= 0; i--) { // get cache for last node cache = cache != null && cache.Position == cur.Next[i] ? cache : this.GetNode(cur.Next[i]); // for(; <while_not_this>; <do_this>) { ... } for (; cur.Next[i].IsEmpty == false; cur = cache) { // get cache for last node cache = cache != null && cache.Position == cur.Next[i] ? cache : this.GetNode(cur.Next[i]); // read next node to compare var diff = cache.Key.CompareTo(key, _collation); // if unique and diff = 0, throw index exception (must rollback transaction - others nodes can be dirty) if (diff == 0 && index.Unique) { throw LiteException.IndexDuplicateKey(index.Name, key); } if (diff == 1) { break; } } if (i <= (level - 1)) // level == length { // cur = current (immediately before - prev) // node = new inserted node // next = next node (where cur is pointing) node.SetNext((byte)i, cur.Next[i]); node.SetPrev((byte)i, cur.Position); cur.SetNext((byte)i, node.Position); var next = this.GetNode(node.Next[i]); if (next != null) { next.SetPrev((byte)i, node.Position); } } } // if last node exists, create a double link list if (last != null) { ENSURE(last.NextNode == PageAddress.Empty, "last index node must point to null"); last.SetNextNode(node.Position); } // fix page position in free list slot _snapshot.AddOrRemoveFreeIndexList(node.Page, ref index.FreeIndexPageList); return(node); }
private IEnumerable <IndexNode> ExecuteStartsWith(IndexService indexer, CollectionIndex index) { // find first indexNode var first = indexer.Find(index, _startsWith, true, this.Order); var node = first; // if collection exists but are empty if (first == null) { yield break; } // first, go backward to get all same values while (node != null) { // if current node are edges exit while if (node.Key.IsMinValue || node.Key.IsMaxValue) { break; } var valueString = node.Key.IsString ? node.Key.AsString : node.Key.IsNull ? "" : node.Key.ToString(); if (_equals ? valueString.Equals(_startsWith, StringComparison.OrdinalIgnoreCase) : valueString.StartsWith(_startsWith, StringComparison.OrdinalIgnoreCase)) { // must still testing SqlLike method for rest of pattern - only if exists more to test (avoid slow SqlLike test) if ((_testSqlLike == false) || (_testSqlLike == true && valueString.SqlLike(_pattern, indexer.Collation) == true)) { yield return(node); } } else { break; } node = indexer.GetNode(node.GetNextPrev(0, -this.Order)); } // move fordward node = indexer.GetNode(first.GetNextPrev(0, this.Order)); while (node != null) { // if current node are edges exit while if (node.Key.IsMinValue || node.Key.IsMaxValue) { break; } var valueString = node.Key.IsString ? node.Key.AsString : node.Key.IsNull ? "" : node.Key.ToString(); if (_equals ? valueString.Equals(_pattern, StringComparison.OrdinalIgnoreCase) : valueString.StartsWith(_startsWith, StringComparison.OrdinalIgnoreCase)) { // must still testing SqlLike method for rest of pattern - only if exists more to test (avoid slow SqlLike test) if (node.DataBlock.IsEmpty == false && ((_testSqlLike == false) || (_testSqlLike == true && valueString.SqlLike(_pattern, indexer.Collation) == true))) { yield return(node); } } else { break; } // first, go backward to get all same values node = indexer.GetNode(node.GetNextPrev(0, this.Order)); } }
private IEnumerable <IndexNode> ExecuteLike(IndexService indexer, CollectionIndex index) { return(indexer .FindAll(index, this.Order) .Where(x => x.Key.IsString && x.Key.AsString.SqlLike(_pattern, indexer.Collation))); }