示例#1
0
        /// <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);
                    }
                }
            }
        }
示例#2
0
        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));
            }
        }
示例#3
0
 public override IAsyncEnumerable <IndexNode> Execute(IndexService indexer, CollectionIndex index)
 {
     throw new NotImplementedException();
 }
示例#4
0
 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);
        }
示例#6
0
        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));
            }
        }
示例#7
0
 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)));
 }