/// <summary> /// Go first/last occurence of this index value /// </summary> private IndexNode FindBoundary(CollectionIndex index, IndexNode cur, BsonValue value, int order, int level) { var last = cur; while (cur.Key.CompareTo(value) == 0) { last = cur; cur = this.GetNode(cur.NextPrev(0, order)); if (cur.IsHeadTail(index)) { break; } } return(last); }
/// <summary> /// Create a new index and returns head page address (skip list) /// </summary> public CollectionIndex CreateIndex(CollectionPage col) { // get index slot var index = col.GetFreeIndex(); // get a new index page for first index page var page = _pager.NewPage <IndexPage>(); // create a empty node with full max level var head = new IndexNode(IndexNode.MAX_LEVEL_LENGTH) { Key = BsonValue.MinValue, KeyLength = (ushort)BsonValue.MinValue.GetBytesCount(false), Page = page, Position = new PageAddress(page.PageID, 0) }; // add as first node page.Nodes.Add(head.Position.Index, head); // update freebytes + item count (for head) page.UpdateItemCount(); _pager.SetDirty(index.Page); // add indexPage on freelist if has space _pager.AddOrRemoveToFreeList(true, page, index.Page, ref index.FreeIndexPageID); // point the head/tail node to this new node position index.HeadNode = head.Position; // insert tail node var tail = this.AddNode(index, BsonValue.MaxValue, IndexNode.MAX_LEVEL_LENGTH); index.TailNode = tail.Position; return(index); }
/// <summary> /// Insert a new node index inside an collection index. /// </summary> private IndexNode AddNode(CollectionIndex index, BsonValue key, byte level) { // calc key size var keyLength = key.GetBytesCount(false); if (keyLength > MAX_INDEX_LENGTH) { throw LiteException.IndexKeyTooLong(); } // creating a new index node var node = new IndexNode(level) { Key = key, KeyLength = (ushort)keyLength }; // get a free page to insert my index node var page = _pager.GetFreePage <IndexPage>(index.FreeIndexPageID, node.Length); node.Position = new PageAddress(page.PageID, page.Nodes.NextIndex()); node.Page = page; // add index node to page page.Nodes.Add(node.Position.Index, node); // update freebytes + items count page.UpdateItemCount(); // now, let's link my index node on right place var cur = this.GetNode(index.HeadNode); // using as cache last IndexNode cache = null; // scan from top left for (var i = IndexNode.MAX_LEVEL_LENGTH - 1; i >= 0; i--) { // get cache for last node cache = cache != null && cache.Position.Equals(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.Equals(cur.Next[i]) ? cache : this.GetNode(cur.Next[i]); // read next node to compare var diff = cache.Key.CompareTo(key); // if unique and diff = 0, throw index exception (must rollback transaction - others nodes can be dirty) if (diff == 0 && index.Options.Unique) { throw LiteException.IndexDuplicateKey(index.Field, key); } if (diff == 1) { break; } } if (i <= (level - 1)) // level == length { // cur = current (imediatte before - prev) // node = new inserted node // next = next node (where cur is poiting) _pager.SetDirty(cur.Page); node.Next[i] = cur.Next[i]; node.Prev[i] = cur.Position; cur.Next[i] = node.Position; var next = this.GetNode(node.Next[i]); if (next != null) { _pager.SetDirty(next.Page); next.Prev[i] = node.Position; } } } // add/remove indexPage on freelist if has space _pager.AddOrRemoveToFreeList(page.FreeBytes > IndexPage.INDEX_RESERVED_BYTES, page, index.Page, ref index.FreeIndexPageID); return(node); }