public void Insert(IEnumerable <KeyValuePair <BsonValue, PageAddress> > items, int order, BufferSlice buffer) { var query = order == Query.Ascending ? items.OrderBy(x => x.Key, _collation) : items.OrderByDescending(x => x.Key, _collation); var offset = 0; foreach (var item in query) { buffer.WriteIndexKey(item.Key, offset); var keyLength = IndexNode.GetKeyLength(item.Key, false); if (keyLength > MAX_INDEX_KEY_LENGTH) { throw LiteException.InvalidIndexKey($"Sort key must be less than {MAX_INDEX_KEY_LENGTH} bytes."); } offset += keyLength; buffer.Write(item.Value, offset); offset += PageAddress.SIZE; _remaining++; } _count = _remaining; }
/// <summary> /// Insert a new node index inside an collection index. Flip coin to know level /// </summary> public IndexNode AddNode(CollectionIndex index, BsonValue key, PageAddress dataBlock, IndexNode last) { // do not accept Min/Max value as index key (only head/tail can have this value) if (key.IsMaxValue || key.IsMinValue) { throw LiteException.InvalidIndexKey($"BsonValue MaxValue/MinValue are not supported as index key"); } // random level (flip coin mode) - return number between 1-32 var level = this.Flip(); // set index collection with max-index level if (level > index.MaxLevel) { // update max level _snapshot.CollectionPage.UpdateCollectionIndex(index.Name).MaxLevel = level; } // call AddNode with key value return(this.AddNode(index, key, dataBlock, level, last)); }
/// <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); }