protected internal override void performSearch(TKey keyword, KeywordMatchMode matchMode, bool ignoreCase, bool returnLeafIfNotFound, List <KeywordMatch <TKey, TValue> > matches)
        {
            try {
                BPlusTree <TKey, TValue> .LogInfo("Begin performSearch node=" + this.ToString() + " keyword=" + keyword.ToString() + ", returnLeafIfNotFound=" + returnLeafIfNotFound);

                int childIndex = ChildLocations.Count - 1;

                // given a keyword, drill until the containing leaf node is found (return null when not found)
                if (matchMode == KeywordMatchMode.Contains || matchMode == KeywordMatchMode.EndsWith)
                {
                    // Contains and EndsWith must check all leaf nodes since the data is ordered alphabetically
                    // left-to-right.  So we can skip checking keywords because we know we need to end up at the leftmost leaf node
                    childIndex = 0;
                }
                else
                {
                    for (short i = 0; i < Keywords.Count; i++)
                    {
                        int compared = keyword.CompareTo(Keywords[i], matchMode, ignoreCase);
                        if (compared < 0)
                        {
                            // given keyword is smaller than this entry.
                            // follow the left child.
                            childIndex = i;
                            break;
                        }
                        else if (compared == 0)
                        {
                            if (matchMode == KeywordMatchMode.StartsWith)
                            {
                                // special case!  startswith doesn't mean there aren't more values to the left.
                                // so, we go left instead of right.
                                childIndex = i;
                            }
                            else
                            {
                                // given keyword is == this entry.
                                // follow right child.
                                childIndex = i + 1;
                            }
                            break;
                        }
                        else
                        {
                            // keep checking...
                        }
                    }
                }



                // either we found the proper index, or we ran out of keywords and the rightmost index is the proper index.
                // read in that child, split it if needed (if we're inserting), and search down that child
                BPlusTreeNode <TKey, TValue> child = BPlusTreeNode <TKey, TValue> .Read(this.Tree, this, ChildLocations[childIndex]);

                if (!returnLeafIfNotFound)
                {
                    // just continue the search down the child. if the node is full or not makes no difference -- they don't want us
                    // to return the leaf if it's not found anyway.
                    child.performSearch(keyword, matchMode, ignoreCase, returnLeafIfNotFound, matches);
                }
                else
                {
                    if (!child.IsFull)
                    {
                        // just continue the search down the child. we might have to perform an insert somewhere down the line,
                        // but there's still room in this particular node so we shouldn't split it.
                        child.performSearch(keyword, matchMode, ignoreCase, returnLeafIfNotFound, matches);
                    }
                    else
                    {
                        // child node is full and they want us to return a node even if not found -- they're inserting.
                        // split the child, then continue the search down the appropriate one

                        // NOTE: this does pre-emptive node splits -- meaning we split on the way down instead of
                        //       splitting on the way back up.  this is common practice to help increase concurrency.
                        int newKeywordIndex = 0;
                        BPlusTreeNode <TKey, TValue> newChild = this.SplitChild(child, out newKeywordIndex);

                        int compared = keyword.CompareTo(Keywords[newKeywordIndex], matchMode, ignoreCase);
                        if (compared < 0)
                        {
                            // new keyword is less than the keyword that was just copied/pushed into the parent (current) node.
                            // that new keyword belongs in the original child (left child)
                            child.performSearch(keyword, matchMode, ignoreCase, returnLeafIfNotFound, matches);
                        }
                        else
                        {
                            // keep checking...
                            newChild.performSearch(keyword, matchMode, ignoreCase, returnLeafIfNotFound, matches);
                        }
                    }
                }
            } finally {
                BPlusTree <TKey, TValue> .LogInfo("End performSearch node=" + this.ToString() + " keyword=" + keyword.ToString() + ", returnLeafIfNotFound=" + returnLeafIfNotFound);
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Performs a search for the given keyword using the given matchMode/ignoreCase.  For each item that matches, the callback is called, passing the current node, keyword offset, and the given additionalData with it.  This allows us to reuse the somewhat complex search code for searching and updating.
        /// </summary>
        /// <param name="keyword"></param>
        /// <param name="matchMode"></param>
        /// <param name="ignoreCase"></param>
        /// <param name="callback"></param>
        /// <param name="additionalData"></param>
        private void performSearch(string keyword, KeywordMatchMode matchMode, bool ignoreCase, SearchCallback callback, object additionalData)
        {
            int i        = 0;
            int compared = -1;

            // NOTE: We have the switch and as many of the if statements outside the loops as we can so we minimize branching within the loops.
            //       We'll be spinning through possibly millions of times so we're sacrificing cleaner, more intuitive code for performance here

            switch (matchMode)
            {
            case KeywordMatchMode.ExactMatch:
                // exact match.  using String.Compare instead of == allows us to jump out as soon as
                // we find a string "greater than" the search keyword.  If we just used ==, we'd have to always compare the entire
                // set of _keywords in this node if none was ever found.  Since once we find a match we stop, it's okay to put the IsLeaf check within the loop.

                // ExactMatch, regardless of Leafiness, regardless of case sensitivity
                while (i < KeywordCount && compared < 1)
                {
                    compared = String.Compare(Keywords[i], keyword, ignoreCase, CultureInfo.CurrentUICulture);
                    if (compared == 0)
                    {
                        // found our exact match
                        if (IsLeaf)
                        {
                            callback(this, i, additionalData);

                            // there may be multiple entries for a given keyword (unlikely, but possible)
                            // so do NOT jump out.
                        }
                        else
                        {
                            // continue searching the associated child node...
                            BPlusTreeNode node = BPlusTreeNode.Read(_tree, ChildrenByteOffsets[i], true);
                            node.performSearch(keyword, matchMode, ignoreCase, callback, additionalData);
                            // since this node is not a leaf, this means a given keyword will not appear more than once.
                            // so once we drill down, we're done.
                            return;
                        }
                    }
                    else if (compared > 0)
                    {
                        // this keyword is bigger than ours -- i.e. we know it's not in this node.
                        // if we're not a leaf, drill down...
                        if (!IsLeaf)
                        {
                            // continue searching the associated child node...
                            BPlusTreeNode node = BPlusTreeNode.Read(_tree, ChildrenByteOffsets[i], true);
                            node.performSearch(keyword, matchMode, ignoreCase, callback, additionalData);
                        }
                        // we've already passed where it could be an exact match in this node. jump out.
                        return;
                    }
                    else
                    {
                        // this keyword is "less than" our search keyword.  keep looking in this node.
                    }
                    i++;
                }
                if (!IsLeaf && i == KeywordCount)
                {
                    // we ran out of keywords to compare against -- means
                    // all keywords in this node are "less than" than our search keyword.
                    // if there's a right child, inspect it.
                    BPlusTreeNode node = BPlusTreeNode.Read(_tree, ChildrenByteOffsets[i], true);
                    node.performSearch(keyword, matchMode, ignoreCase, callback, additionalData);
                }
                break;

            case KeywordMatchMode.StartsWith:

                if (IsLeaf)
                {
                    // StartsWith, IsLeaf, regardless of case sensitivity (case sensitivity is handled by comparisonType variable)

                    while (i < KeywordCount)
                    {
                        if (Keywords[i].StartsWith(keyword, ignoreCase, CultureInfo.CurrentUICulture))
                        {
                            callback(this, i, additionalData);
                            // since this is a startswith (i.e. fuzzy on right)
                            // we should just inspect the entire node (do not jump out)
                        }
                        i++;
                    }
                }
                else
                {
                    // StartsWith, Is NOT Leaf, regardless of case sensitivity (case sensitivity is handled by comparisonType variable)

                    while (i < KeywordCount)
                    {
                        if (Keywords[i].StartsWith(keyword, ignoreCase, CultureInfo.CurrentUICulture))
                        {
                            BPlusTreeNode node = BPlusTreeNode.Read(_tree, ChildrenByteOffsets[i], true);
                            node.performSearch(keyword, matchMode, ignoreCase, callback, additionalData);
                            // since this is a startswith (i.e. fuzzy on right)
                            // we should just inspect the entire node (do not jump out)
                        }
                        else if (String.Compare(Keywords[i], keyword, ignoreCase, CultureInfo.CurrentUICulture) > 0)
                        {
                            // this keyword doesn't start with our search keyword, but it is "greater than" it.
                            // continue searching children
                            BPlusTreeNode node = BPlusTreeNode.Read(_tree, ChildrenByteOffsets[i], true);
                            node.performSearch(keyword, matchMode, ignoreCase, callback, additionalData);
                            return;
                        }
                        i++;
                    }
                    if (i == KeywordCount && ChildrenByteOffsets[i] > 0)
                    {
                        // we ran off the end. inspect rightmost child.
                        BPlusTreeNode node = BPlusTreeNode.Read(_tree, ChildrenByteOffsets[i], true);
                        node.performSearch(keyword, matchMode, ignoreCase, callback, additionalData);
                        return;
                    }
                }
                break;

            case KeywordMatchMode.EndsWith:

                // Since our index is built by the beginning of the word, we essentially have to do an index scan.
                // we'll use the tree's TraverseLeaves() method to do this efficiently.

                foreach (BPlusTreeNode node in _tree.TraverseLeaves())
                {
                    for (int j = 0; j < node.KeywordCount; j++)
                    {
                        if (node.Keywords[j].EndsWith(keyword, ignoreCase, CultureInfo.CurrentUICulture))
                        {
                            // this does end with what we're looking for...
                            callback(node, j, additionalData);

                            // since this is a endswith (i.e. fuzzy on left)
                            // we should just inspect the entire node (do not jump out)
                        }
                    }
                }


                break;

            case KeywordMatchMode.Contains:

                // Since our index is built by the beginning of the word, we essentially have to do an index scan.
                // we'll use the tree's TraverseLeaves() method to do this efficiently.

                if (ignoreCase)
                {
                    // Contains, Case Insensitive, regardless of leafiness

                    keyword = keyword.ToLower();
                    foreach (BPlusTreeNode node in _tree.TraverseLeaves())
                    {
                        for (int j = 0; j < node.KeywordCount; j++)
                        {
                            if (node.Keywords[j].ToLower().Contains(keyword))
                            {
                                // this does contain what we're looking for...
                                callback(node, j, additionalData);
                                // since this is a endswith (i.e. fuzzy on left)
                                // we should just inspect the entire node (do not jump out)
                            }
                        }
                    }
                }
                else
                {
                    // Contains, Case Sensitive, regardless of leafiness

                    foreach (BPlusTreeNode node in _tree.TraverseLeaves())
                    {
                        for (int j = 0; j < node.KeywordCount; j++)
                        {
                            if (node.Keywords[j].Contains(keyword))
                            {
                                // this does contain what we're looking for...
                                callback(node, j, additionalData);
                                // since this is a endswith (i.e. fuzzy on left)
                                // we should just inspect the entire node (do not jump out)
                            }
                        }
                    }
                }
                break;
            }
        }