/// <summary> /// Create a new index and returns head page address (skip list) /// </summary> public CollectionIndex CreateIndex(string name, string expr, bool unique) { // get how many butes needed fore each head/tail (both has same size) var bytesLength = IndexNode.GetNodeLength(MAX_LEVEL_LENGTH, BsonValue.MinValue, out var keyLength); // get a new empty page (each index contains your own linked nodes) var indexPage = _snapshot.NewPage <IndexPage>(); // create index ref var index = _snapshot.CollectionPage.InsertCollectionIndex(name, expr, unique); // insert head/tail nodes var head = indexPage.InsertIndexNode(index.Slot, MAX_LEVEL_LENGTH, BsonValue.MinValue, PageAddress.Empty, bytesLength); var tail = indexPage.InsertIndexNode(index.Slot, MAX_LEVEL_LENGTH, BsonValue.MaxValue, PageAddress.Empty, bytesLength); // link head-to-tail with double link list in first level head.SetNext(0, tail.Position); tail.SetPrev(0, head.Position); // add this new page in free list (slot 0) index.FreeIndexPageList = indexPage.PageID; indexPage.PageListSlot = 0; index.Head = head.Position; index.Tail = tail.Position; return(index); }
/// <summary> /// Create a new index and returns head page address (skip list) /// </summary> public CollectionIndex CreateIndex(string name, string expr, bool unique) { // get how many butes needed fore each head/tail (both has same size) var bytesLength = IndexNode.GetNodeLength(MAX_LEVEL_LENGTH, BsonValue.MinValue, out var keyLength); // get a free index page for head note (x2 for head + tail) var indexPage = _snapshot.GetFreePage <IndexPage>(bytesLength * 2); // create index ref var index = _snapshot.CollectionPage.InsertCollectionIndex(name, expr, unique); // insert head/tail nodes var head = indexPage.InsertIndexNode(index.Slot, MAX_LEVEL_LENGTH, BsonValue.MinValue, PageAddress.Empty, bytesLength); var tail = indexPage.InsertIndexNode(index.Slot, MAX_LEVEL_LENGTH, BsonValue.MaxValue, PageAddress.Empty, bytesLength); // link head-to-tail with double link list in first level head.SetNext(0, tail.Position); tail.SetPrev(0, head.Position); index.Head = head.Position; index.Tail = tail.Position; return(index); }
/// <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); }