联系人数据库中快速子串搜索的算法

时间:2012-05-26 11:58:49

标签: java algorithm lucene full-text-search search-engine

我有一个拥有大约100万个姓名和地址的数据库。应该公开该数据库,以便在网页上进行“Google Suggest”式即时搜索。我正在寻找一种有效的算法/数据结构,可以帮助我实现这一目标。

使这比使用TrieGeneralized Suffix Tree更困难的是,它必须支持一些遗漏的查询。例如,当用户输入“Elvis Pr”时,应该建议“Elvis Aaron Presley”。

我希望将整个索引放在内存中(我有大约4GB的RAM用于此)。

应用程序是用Java编写的,因此链接到基于Java的库被认为是非常有用的。我一直在寻找LuceneMG4J,但我还没弄清楚我可以使用哪种类型的索引来解决我的问题。

4 个答案:

答案 0 :(得分:2)

也许您真正想要的是一种搜索,其中用户键入的每个单词必须显示为联系人中某个单词的前缀。这比一般子字符串搜索更容易,更快。

  1. 构建属于任何联系人的所有单词的单个排序数组,并在每个单词旁边存储“联系人ID”字段(例如[Aaron / 1,Aleksander / 2,Blomskøld/ 2,Elvis / 1,Presley / 1] )。
  2. 对于用户键入的每个单词,分别使用二进制搜索来查找以该单词开头的名称范围(这必然是数组中连续的索引范围)。由于用户通常每次按键只调整一个单词,因此每次击键时只需要重新计算其中一个单词 - 事实上,在输入附加字母的常见情况下,甚至可以更有效地完成重新计算步骤,因为这只会缩小匹配单词的范围。
  3. 最后,将联系人ID集相交以生成可能性列表。为了显示可能性,您需要第二个数组,按联系人ID索引并包含全名。

答案 1 :(得分:1)

您可以尝试使用带有字符串距离指标的bk-tree,例如levenshtein,另请参阅http://blog.notdot.net/2007/4/Damn-Cool-Algorithms-Part-1-BK-Trees

修改 发明距离度量很难,但我碰巧知道一个可以使用的名为structural entropic distance的基于信息差异的文件。它的工作原理如下:

取两个字符串x =“Elvis Pr”和y =“Elvis Aaron Presley”

每一项工作都是uni和bi-gram的多重集合:

x = {e, l, v, i, s, _, p, r, el, lv, vi, is, s_,  _p, pr}
y = {ex3, lx2, v, i, sx2, _x2, ax2, rx2, o, n, p, y, el, lv, vi, is, s_, _a, aa, ar, ro, on, n_, _p, pr, re, es, sl, le, ey}

现在适用于

中的那些术语
{e, l, v, i, s, _, p, r, el, lv, vi, is, s_, _p, pr}

计算产品(f_x(t) / (f_x(t) + f_y(t)))^{f_x(t)/2} * (f_y(t) / (f_x(t) + f_y(t)))^{f_y(t)/2} 所以

e  = ((1/15) / (1/15 + 3/37))^(1/30) * ((3/37) / (1/15 + 3/37))^(3/74)
l  = ((1/15) / (1/15 + 2/37))^(1/30) * ((2/37) / (1/15 + 2/37))^(2/74)
v  = ((1/15) / (1/15 + 1/37))^(1/30) * ((1/37) / (1/15 + 1/37))^(1/74)
i  = ((1/15) / (1/15 + 1/37))^(1/30) * ((1/37) / (1/15 + 1/37))^(1/74)
s  = ((1/15) / (1/15 + 2/37))^(1/30) * ((2/37) / (1/15 + 2/37))^(2/74)
_  = ((1/15) / (1/15 + 2/37))^(1/30) * ((2/37) / (1/15 + 2/37))^(2/74)
p  = ((1/15) / (1/15 + 1/37))^(1/30) * ((1/37) / (1/15 + 1/37))^(1/74)
r  = ((1/15) / (1/15 + 2/37))^(1/30) * ((2/37) / (1/15 + 2/37))^(2/74)
el = ((1/15) / (1/15 + 1/37))^(1/30) * ((1/37) / (1/15 + 1/37))^(1/74)
lv = ((1/15) / (1/15 + 1/37))^(1/30) * ((1/37) / (1/15 + 1/37))^(1/74)
vi = ((1/15) / (1/15 + 1/37))^(1/30) * ((1/37) / (1/15 + 1/37))^(1/74)
is = ((1/15) / (1/15 + 1/37))^(1/30) * ((1/37) / (1/15 + 1/37))^(1/74)
s_ = ((1/15) / (1/15 + 1/37))^(1/30) * ((1/37) / (1/15 + 1/37))^(1/74)
_p = ((1/15) / (1/15 + 1/37))^(1/30) * ((1/37) / (1/15 + 1/37))^(1/74)
pr = ((1/15) / (1/15 + 1/37))^(1/30) * ((1/37) / (1/15 + 1/37))^(1/74)

将所有这些相乘并且你应该得到[0.5,1]范围内的数字,这样你就可以通过乘以2并减去1来更有效地将其扩展到[0,1]范围。

但是,这不是离散距离指标,因此您必须使用其他指标索引,例如vp-tree

答案 2 :(得分:1)

Solr提供全功能自动提示功能OOTB。 This link提供了一些从热门查询生成的背景信息。但您可以轻松地对其进行调整以构建一些先验知识,例如您描述的场景。

答案 3 :(得分:1)

看看Cleo,看起来你有一个非常相似的用例。他们使用Bloom过滤器搜索前缀,使用Forward索引删除误报。