我有一个包含短语(80-100个字符)的数据库,以及一些冗长的文档(50-100Kb),我想要一个给定文档的排序列表;而不是搜索引擎的通常输出,给定短语的文档列表。
我之前使用过MYSQL全文索引,并查看了lucene,但从未使用它。 它们似乎都适合比较短(搜索词)和长(文档)。
你怎么能得到这个的倒数?
答案 0 :(得分:3)
我使用维基百科标题数据库做了类似的事情,并为每个~50KB的文档设法降低到几百毫秒。对于我的需求来说,这仍然不够快,但也许它可以为你工作。
基本上,这个想法是尽可能地使用哈希值,并且只对可能的匹配进行字符串比较,这种情况非常罕见。
首先,您获取数据库并将其转换为哈希数组。如果你有数十亿的短语,这可能不适合你。在计算哈希值时,请务必通过标记器传递短语,这将删除标点符号和空格。这部分只需要完成一次。
然后,你使用相同的标记器来查看文档,保留最后1,2,...,n个标记的运行列表,哈希。在每次迭代中,您都会对哈希数据库中的哈希值进行二进制搜索。
当您找到匹配项时,您会进行实际的字符串比较,看看是否找到了匹配项。
这里有一些代码,让你尝试一下我的意思,这个例子实际上并没有进行字符串比较:
HashSet<Long> foundHashes = new HashSet<Long>();
LinkedList<String> words = new LinkedList<String>();
for(int i=0; i<params.maxPhrase; i++) words.addLast("");
StandardTokenizer st = new StandardTokenizer(new StringReader(docText));
Token t = new Token();
while(st.next(t) != null) {
String token = new String(t.termBuffer(), 0, t.termLength());
words.addLast(token);
words.removeFirst();
for(int len=params.minPhrase; len<params.maxPhrase; len++) {
String term = Utils.join(new ArrayList<String>(words.subList(params.maxPhrase-len,params.maxPhrase)), " ");
long hash = Utils.longHash(term);
if(params.lexicon.isTermHash(hash)) {
foundHashes.add(hash);
}
}
}
for(long hash : foundHashes) {
if(count.containsKey(hash)) {
count.put(hash, count.get(hash) + 1);
} else {
count.put(hash, 1);
}
}
答案 1 :(得分:0)
将每个短语转换为正则表达式并在文档上运行每个短语,计算出现次数是否会太慢?
如果这不起作用,也许你可以将所有短语组合成一个巨大的正则表达式(使用|
),并编译它。然后,从文档中的每个字符开始运行那个巨大的正则表达式。通过字符计算匹配数。
答案 2 :(得分:0)
短语数据库有多大?我假设它非常大。
我会做以下事情:
通过其中一个词语对短语进行索引。您可以在每个短语中选择最不常见的单词。您可以通过假设该单词至少为例如更好地进行搜索。 5个字符长,如果它更短,则将字填充为5个字符。填充可以是单词之后的空格,后跟后续单词,以减少匹配,或者如果单词出现在短语的末尾,则为某些默认字符(例如“XX”)。
浏览您的文档,在必要时通过填充将每个单词(常见的单词可以丢弃)转换为密钥,然后检索短语。
通过这些关键字检索相关短语。
使用内存中文本搜索查找每个检索到的短语的出现次数。
我假设短语不能越过句子边界。在这种情况下,您可以将文档的每个句子读入数组中的子字符串,并使用子字符串函数在每个句子中搜索每个短语并计算出现次数,并为每个短语保留一个运行总和。
答案 3 :(得分:0)
也许阅读Peter Turney on keyphrase extraction会给你一些想法。总的来说,他的方法与其建议的方法有一些相似之处。