Exemple #1
0
        // Recursively removes a term from the tree
        private void RemoveTerm(T searchItem, char[] partialTerm, int partialTermIndex, SearchIndexNode <T> currentNode)
        {
            char currentCharacter = partialTerm[partialTermIndex];

            SearchIndexNode <T> nextNode = null;

            if (currentNode.ChildNodes.TryGetValue(currentCharacter, out nextNode))
            {
                // If we're down to one character, we've found the node
                if (partialTerm.Length - partialTermIndex == 1)
                {
                    // Remove the item
                    nextNode.SearchItems.Remove(searchItem);

                    // If there are no other items associated with this term, remove the node
                    if (nextNode.SearchItems.Count == 0)
                    {
                        currentNode.ChildNodes.Remove(currentCharacter);

                        // Also remove the mapping from character to node
                        this.nodeMap[currentCharacter].Remove(nextNode);
                        if (this.nodeMap[currentCharacter].Count == 0)
                        {
                            this.nodeMap.Remove(currentCharacter);
                        }
                    }
                }
                // Otherwise, recurse
                else
                {
                    this.RemoveTerm(searchItem, partialTerm, partialTermIndex + 1, nextNode);
                }
            }
        }
Exemple #2
0
        // Recursively adds a term to the tree
        private void AddTerm(T searchItem, char[] partialTerm, int partialTermIndex, SearchIndexNode <T> currentNode)
        {
            char currentCharacter = partialTerm[partialTermIndex];

            SearchIndexNode <T> nextNode = null;

            if (!currentNode.ChildNodes.TryGetValue(currentCharacter, out nextNode))
            {
                // Add a new node to the tree
                nextNode = new SearchIndexNode <T> (currentNode);
                currentNode.ChildNodes[currentCharacter] = nextNode;

                // Keep a mapping of the character to the node, which is used for substring searches
                Dictionary <SearchIndexNode <T>, bool> searchIndexNodes = null;
                if (!this.nodeMap.TryGetValue(currentCharacter, out searchIndexNodes))
                {
                    searchIndexNodes = new Dictionary <SearchIndexNode <T>, bool> ();
                    this.nodeMap[currentCharacter] = searchIndexNodes;
                }
                searchIndexNodes.Add(nextNode, true);
            }

            // If we're down to one character, then we're done - add this search item to the node
            if (partialTerm.Length - partialTermIndex == 1)
            {
                nextNode.SearchItems[searchItem] = true;
            }
            // Otherwise, recurse
            else
            {
                this.AddTerm(searchItem, partialTerm, partialTermIndex + 1, nextNode);
            }
        }
Exemple #3
0
        // Enumerates all descendent nodes as well as the specified node
        private IEnumerable <SearchIndexNode <T> > EnumerateDescendents(SearchIndexNode <T> searchIndexNode)
        {
            yield return(searchIndexNode);

            foreach (SearchIndexNode <T> childNode in searchIndexNode.ChildNodes.Values)
            {
                yield return(childNode);

                foreach (SearchIndexNode <T> descendentNode in this.EnumerateDescendents(childNode))
                {
                    yield return(descendentNode);
                }
            }
        }
Exemple #4
0
        // Recursively searches for a node matching the specified term
        private SearchIndexNode <T> FindSearchIndexNode(char[] partialTerm, int partialTermIndex, SearchIndexNode <T> currentNode)
        {
            // If we have recursed through all the characters in the term, then we have found the node
            if (partialTerm.Length - partialTermIndex == 0)
            {
                return(currentNode);
            }

            // Otherwise, recurse
            SearchIndexNode <T> nextNode = null;

            if (currentNode.ChildNodes.TryGetValue(partialTerm[partialTermIndex], out nextNode))
            {
                return(FindSearchIndexNode(partialTerm, partialTermIndex + 1, nextNode));
            }

            return(null);
        }
Exemple #5
0
        /// <summary>
        /// Finds all search items to which the specified term maps.
        /// </summary>
        /// <param name="term">The term for which to search.</param>
        /// <param name="wholeWord"><c>true</c> to search for whole word matches only, <c>false</c> otherwise.</param>
        /// <returns>An array of objects to which the specified term maps.</returns>
        public T[] FindSearchItems(string term, bool wholeWord)
        {
            lock (this)
            {
                char[]   termCharacters = term.ToLower().ToCharArray();
                List <T> searchResults  = new List <T> ();

                // For whole words, start at the root and only consider at most one node
                if (wholeWord)
                {
                    SearchIndexNode <T> matchingNode = this.FindSearchIndexNode(termCharacters, 0, this.rootNode);
                    if (matchingNode != null)
                    {
                        searchResults.AddRange(matchingNode.SearchItems.Keys);
                    }
                }
                // For substrings, start at all nodes associated with the first character of the search term
                else
                {
                    if (termCharacters.Length > 0)
                    {
                        Dictionary <SearchIndexNode <T>, bool> termStartNodes;
                        if (this.nodeMap.TryGetValue(termCharacters[0], out termStartNodes))
                        {
                            foreach (SearchIndexNode <T> searchIndexNode in termStartNodes.Keys)
                            {
                                SearchIndexNode <T> matchingNode = this.FindSearchIndexNode(termCharacters, 1, searchIndexNode);
                                if (matchingNode != null)
                                {
                                    // Since we are looking for substrings, include all descendants
                                    foreach (SearchIndexNode <T> descendentNode in this.EnumerateDescendents(matchingNode))
                                    {
                                        searchResults.AddRange(descendentNode.SearchItems.Keys);
                                    }
                                }
                            }
                        }
                    }
                }

                return(searchResults.ToArray());
            }
        }
Exemple #6
0
 public SearchIndex()
 {
     this.rootNode = new SearchIndexNode <T> (null);
     this.nodeMap  = new Dictionary <char, Dictionary <SearchIndexNode <T>, bool> > ();
 }
Exemple #7
0
 public SearchIndexNode(SearchIndexNode <T> parent)
 {
     this.parent      = parent;
     this.searchItems = new Dictionary <T, bool> ();
     this.childNodes  = new Dictionary <char, SearchIndexNode <T> > ();
 }