blob: 4af7aef4c81e0104c67a70acb0ba1842da490b86 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.search.suggest.tst;
import java.util.*;
/**
* Ternary Search Trie implementation.
*
* @see TernaryTreeNode
*/
public class TSTAutocomplete {
TSTAutocomplete() {}
/**
* Inserting keys in TST in the order middle,small,big (lexicographic measure)
* recursively creates a balanced tree which reduces insertion and search
* times significantly.
*
* @param tokens
* Sorted list of keys to be inserted in TST.
* @param lo
* stores the lower index of current list.
* @param hi
* stores the higher index of current list.
* @param root
* a reference object to root of TST.
*/
public void balancedTree(Object[] tokens, Object[] vals, int lo, int hi,
TernaryTreeNode root) {
if (lo > hi) return;
int mid = (lo + hi) / 2;
root = insert(root, (String) tokens[mid], vals[mid], 0);
balancedTree(tokens, vals, lo, mid - 1, root);
balancedTree(tokens, vals, mid + 1, hi, root);
}
/**
* Inserts a key in TST creating a series of Binary Search Trees at each node.
* The key is actually stored across the eqKid of each node in a successive
* manner.
*
* @param currentNode
* a reference node where the insertion will take currently.
* @param s
* key to be inserted in TST.
* @param x
* index of character in key to be inserted currently.
* @return currentNode The new reference to root node of TST
*/
public TernaryTreeNode insert(TernaryTreeNode currentNode, CharSequence s,
Object val, int x) {
if (s == null || s.length() <= x) {
return currentNode;
}
if (currentNode == null) {
TernaryTreeNode newNode = new TernaryTreeNode();
newNode.splitchar = s.charAt(x);
currentNode = newNode;
if (x < s.length() - 1) {
currentNode.eqKid = insert(currentNode.eqKid, s, val, x + 1);
} else {
currentNode.token = s.toString();
currentNode.val = val;
return currentNode;
}
} else if (currentNode.splitchar > s.charAt(x)) {
currentNode.loKid = insert(currentNode.loKid, s, val, x);
} else if (currentNode.splitchar == s.charAt(x)) {
if (x < s.length() - 1) {
currentNode.eqKid = insert(currentNode.eqKid, s, val, x + 1);
} else {
currentNode.token = s.toString();
currentNode.val = val;
return currentNode;
}
} else {
currentNode.hiKid = insert(currentNode.hiKid, s, val, x);
}
return currentNode;
}
/**
* Auto-completes a given prefix query using Depth-First Search with the end
* of prefix as source node each time finding a new leaf to get a complete key
* to be added in the suggest list.
*
* @param root
* a reference to root node of TST.
* @param s
* prefix query to be auto-completed.
* @param x
* index of current character to be searched while traversing through
* the prefix in TST.
* @return suggest list of auto-completed keys for the given prefix query.
*/
public ArrayList<TernaryTreeNode> prefixCompletion(TernaryTreeNode root,
CharSequence s, int x) {
TernaryTreeNode p = root;
ArrayList<TernaryTreeNode> suggest = new ArrayList<>();
while (p != null) {
if (s.charAt(x) < p.splitchar) {
p = p.loKid;
} else if (s.charAt(x) == p.splitchar) {
if (x == s.length() - 1) {
break;
} else {
x++;
}
p = p.eqKid;
} else {
p = p.hiKid;
}
}
if (p == null) return suggest;
if (p.eqKid == null && p.token == null) return suggest;
if (p.eqKid == null && p.token != null) {
suggest.add(p);
return suggest;
}
if (p.token != null) {
suggest.add(p);
}
p = p.eqKid;
Stack<TernaryTreeNode> st = new Stack<>();
st.push(p);
while (!st.empty()) {
TernaryTreeNode top = st.peek();
st.pop();
if (top.token != null) {
suggest.add(top);
}
if (top.eqKid != null) {
st.push(top.eqKid);
}
if (top.loKid != null) {
st.push(top.loKid);
}
if (top.hiKid != null) {
st.push(top.hiKid);
}
}
return suggest;
}
}