private void relocate(BPlusTreeNode parentNode) { // this node has outgrown its allotted space. // we need to move it to the end of the file and update everybody who points to it // so have to update the following: // 1. Parent node's pointer // 2. Left sibling's pointer // 3. Right sibling's pointer // 4. All children's parent pointer (which points at this node) _tree.Log("------------ BEGIN Relocating node ------------"); _tree.Log("BEGIN Relocating node -> ".PadRight(50) + ToString()); // get an abandoned node (or the end of the file, whichever comes first) BPlusAbandonedNode abandonedNode = _tree.GetNextAvailableNodeLocation(this); // mark this node as abandoned in the file (and remove from cache) _tree.AbandonNode(this); //if (this.IsRoot) { // // we're relocating the root. // _tree.Log("TODO: what to do here, if anything?"); //} if (!IsLeaf) { // only leaf nodes should track left/right siblings. LeftSiblingFileOffset = -1; RightSiblingFileOffset = -1; } if (LeftSiblingFileOffset > -1) { BPlusTreeNode originalLeftSibling = BPlusTreeNode.Read(_tree, this.LeftSiblingFileOffset, true); originalLeftSibling.RightSiblingFileOffset = originalLeftSibling.IsLeaf ? abandonedNode.FileOffset : -1; _tree.Log(("Updating left sibling node @ " + originalLeftSibling.FileOffset + " -> ").PadRight(50) + originalLeftSibling.ToString()); originalLeftSibling.Write(null); } if (RightSiblingFileOffset > -1) { BPlusTreeNode originalRightSibling = BPlusTreeNode.Read(_tree, this.RightSiblingFileOffset, true); originalRightSibling.LeftSiblingFileOffset = originalRightSibling.IsLeaf ? abandonedNode.FileOffset : -1; _tree.Log(("Updating right sibling node @ " + originalRightSibling.FileOffset + " -> ").PadRight(50) + originalRightSibling.ToString()); originalRightSibling.Write(null); } if (this.IsRoot) { // special case! no parent means this is the root node. // update the int at the very beginning of the file to point at the new root node _tree.WriteRootNodeOffset(abandonedNode.FileOffset); } else { if (parentNode != null) { for (int i = 0; i < parentNode.KeywordCount + 1; i++) { if (parentNode.ChildrenByteOffsets[i] == FileOffset) { parentNode.ChildrenByteOffsets[i] = abandonedNode.FileOffset; _tree.Log(("Updating parent node @ " + parentNode.FileOffset + " -> ").PadRight(50) + parentNode.ToString()); parentNode.Write(null); // the Root node is cached in the tree object -- udpate it if need be if (parentNode.IsRoot) { _tree.Root = parentNode; } break; } } } } _tree.Writer.BaseStream.Position = abandonedNode.FileOffset; FileOffset = abandonedNode.FileOffset; //_chunkSize = abandonedNode.ByteCount; _tree.Log("END Relocating node (not written yet) -> ".PadRight(50) + ToString()); _tree.Log("------------ END Relocating node ------------"); }
// here's the vernacular: // The _keywords array represents all the keywords in this node. // The _byteRanges array represents all the file offsets in this node for either (a) a keyword or (b) the data itself. // a. If it is located in an index node (i.e. non-Leaf), the byte range represents the start/end offsets in the index file (used for traversing to other nodes) // b. If it is located in a Leaf node, the byte range represents the start/end offsets in the data file (for looking up associated hit(s)) // Think ByteRange = pointer to next node or data, Keyword = item we're searching by // So we always have 1 more pointer (or byterange, BR) than search item (or keyword, K): // // |-------- Node ---------| // | go pi | // | / | | | \ | // | BR1 K1 BR2 K2 BR3 | // |-----------------------| /// <summary> /// Creates a new root node for the tree and makes the original root the first child of the new root. /// </summary> /// <param name="tree"></param> /// <returns></returns> internal static BPlusTreeNode ReplaceRoot(BPlusTree tree, BPlusTreeNode originalRoot, string keyword, long dataByteOffset) { // NOTE: This is a very special case. // replacing the root involves creating 2 new nodes: // 1. the new root node // 2. splitting the existing root node into 2 nodes, one of which is a new one BPlusTreeNode newRoot = null; // create a new root node, mark it as not a leaf, put it in exact same spot as original root newRoot = new BPlusTreeNode { IsLeaf = false, _tree = tree, Keywords = new string[tree.Fanout], ChildrenByteOffsets = new long[tree.Fanout + 1], KeywordCount = 0, FileOffset = -1, LeftSiblingFileOffset = -1, RightSiblingFileOffset = -1, }; tree.Log("------------ BEGIN Replacing root ------------"); tree.Log("BEGIN Replacing root. original node -> ".PadRight(50) + originalRoot.ToString()); tree.Log("BEGIN Replacing root. new node -> ".PadRight(50) + newRoot.ToString()); // add our new root to file, update the root node pointer BPlusAbandonedNode abandoned = tree.GetNextAvailableNodeLocation(originalRoot); newRoot.FileOffset = abandoned.FileOffset; newRoot.Write(null); tree.WriteRootNodeOffset(newRoot.FileOffset); // trickle-down insert the new keyword/data if (!String.IsNullOrEmpty(keyword)) { // split the original BPlusTreeNode newChild = newRoot.SplitChild(0, originalRoot, null); tree.Log("CONTINUE Replacing root. new child node -> ".PadRight(50) + originalRoot.ToString()); if (newRoot.Insert(keyword, dataByteOffset, null)) { // the new root was relocated during insertion (outgrew its allotted space) // the insert should take care of it // re-read it in just in case newRoot = BPlusTreeNode.Read(tree, newRoot.FileOffset, true); } } else { newRoot = BPlusTreeNode.Read(tree, newRoot.FileOffset, true); } tree.Log("END Replacing root. original node -> ".PadRight(50) + originalRoot.ToString()); // _tree.Log("END Replacing root. new child node -> ".PadRight(50) + newChild.ToString()); tree.Log("END Replacing root. new root -> ".PadRight(50) + newRoot.ToString()); tree.Log("------------ END Replacing root ------------"); return(newRoot); }