动态搜索和显示

时间:2008-09-29 08:37:05

标签: algorithm search

我有大量文档,文本文件,我想搜索相关内容。我已经看过一个搜索工具,无法记住在哪里,它实现了一个很好的方法,正如我在下面的要求中描述的那样。

我的要求如下:

  • 我需要一个优化的搜索功能:我为这个搜索功能提供一个用空格分隔的列表(一个或多个)部分完整(或完整)的单词。
  • 然后函数找到所有包含与第一个单词相同的单词的文档,然后使用第二个单词以相同的方式搜索这些找到的文档,依此类推,最后返回包含实际的单词的列表。发现的单词与包含它们的文档(名称和位置)相关联,以获得完整的单词列表。
  • 文档必须包含所有列表中的单词。
  • 我想使用此功能进行按键式搜索,以便我可以实时显示和更新树状结构中的结果。

我提出的解决方案的可能方法如下: 我用三个表创建了一个数据库(最有可能使用mysql):'Documents','Words'和'Word_Docs'。

  • 'Documents'将包含所有文档的(idDoc,Name,Location)。
  • 'Words'将具有(idWord,Word),并且是所有文档中唯一单词的列表(特定单词仅出现一次)。
  • 'Word_Docs'将具有(idWord,idDoc),并且是它出现的每个单词和文档的唯一ID组合列表。

然后在每次击键(空格除外)上使用编辑框的内容调用该函数:

  • 字符串已标记化
  • (这里我的轮子旋转了一下):我确信可以构造一个SQL语句来返回所需的数据集:(actual_words,doc_name,doc_location); (我不是SQL的热门号码),或者是每个令牌的一系列调用,并解析出不重复的idDocs?
  • 然后返回此数据集(/ list / array)

然后显示返回的列表内容:

例如:调用:“seq sta cod” 显示:

sequence - start - code - Counting Sequences [file://docs/sample/con_seq.txt]
         - stop - code - Counting Sequences [file://docs/sample/con_seq.txt]
sequential - statement - code - SQL intro [file://somewhere/sql_intro.doc]

(和那么上)

这是最佳方式吗?该函数需要快速,或者只有在空间被击中时才能调用它? 它应该提供单词完成吗? (得到数据库中的单词)至少这会阻止对不存在的单词的函数的无用调用。 如果单词完成:如何实现?

(也许SO也可以使用这种类型的搜索解决方案来浏览标签?(在主页的右上角))

4 个答案:

答案 0 :(得分:2)

您所谈论的内容被称为inverted index或发布列表,其运作方式与您的建议和Mecki提出的内容类似。关于倒排索引的文献很多;维基百科的文章是一个很好的起点。

更好的是,使用现有的倒排索引实现,而不是尝试自己构建它。 MySQL和最新版本的PostgreSQL默认都有全文索引。您可能还想查看Lucene以获取独立解决方案。编写好的倒排索引时需要考虑很多事情,包括标记化,词干化,多字查询等,预建解决方案将为您完成所有这些。

答案 1 :(得分:1)

最快的方法当然是根本不使用数据库,因为如果您使用优化数据手动搜索,您可以轻松地超越选择的搜索性能。假设文档不经常更改,最快的方法是构建索引文件并使用它们来查找关键字。索引文件的创建方式如下:

  1. 查找文本文件中的所有唯一字词。这是将文本文件按空格分成单词,并将每个单词添加到列表中,除非已在该列表中找到。

  2. 取出您找到的所有单词并按字母顺序排序;最快的方法是使用Three Way Radix QuickSort。排序字符串时,此算法在性能上难以击败。

  3. 将已排序的列表写入磁盘,一行包含一行。

  4. 当您现在想要搜索文档文件时,请完全忽略它,而是将索引文件加载到内存中,并使用二进制搜索来查找单词是否在索引文件中。搜索大型排序列表时,二进制搜索很难被击败。

  5. 或者,您可以在一个步骤中合并步骤(1)和步骤(2)。如果您使用InsertionSort(使用二进制搜索找到正确的插入位置以将新元素插入已排序的列表中),您不仅可以使用快速算法来查明该单词是否已在列表中,以防万一它不是,你立即得到正确的位置来插入它,如果你总是插入这样的新位置,当你到达步骤(3)时,你将自动有一个排序列表。

    问题是你需要在文档发生变化时更新索引......但是,对于数据库解决方案来说也不是这样吗?另一方面,数据库解决方案为您带来了一些优势:您可以使用它,即使文档包含这么多单词,索引文件也不再适合内存(不太可能,因为即使是所有英语单词的列表也会适合任何普通用户PC的内存);但是,如果您需要加载大量文档的索引文件,那么内存可能会成为一个问题。好吧,你可以使用聪明的技巧解决这个问题(例如直接在使用mmap映射到内存的文件中搜索等等),但这些是数据库用来执行快速查找的相同技巧,因此为什么要重新发明轮?此外,您还可以防止在文档更改时搜索单词和更新索引之间的锁定问题(即,如果数据库可以为您执行锁定,或者可以作为原子操作执行更新或更新)。对于使用AJAX调用列表更新的Web解决方案,使用数据库可能是更好的解决方案(如果这是一个用C语言等低级语言编写的本地运行的应用程序,我的第一个解决方案是相当合适的。)

    如果您想在单个选择调用中完成所有操作(这可能不是最佳选择,但是当您使用AJAX动态更新Web内容时,它通常被证明是导致最少麻烦的解决方案),您需要加入所有三个表一起。可能SQL有点生疏,但我会试一试:

    SELECT COUNT(Document.idDoc) AS NumOfHits, Documents.Name AS Name, Documents.Location AS Location 
    FROM Documents INNER JOIN Word_Docs ON Word_Docs.idDoc=Documents.idDoc 
    INNER JOIN Words ON Words.idWord=Words_Docs.idWord
    WHERE Words.Word IN ('Word1', 'Word2', 'Word3', ..., 'WordX')
    GROUP BY Document.idDoc HAVING NumOfHits=X
    

    好吧,也许这不是最快的选择...我想它可以更快完成。无论如何,它会找到包含至少一个单词的所有匹配文档,然后通过ID将所有相同的文档组合在一起,计算已分组到togetehr的数量,最后仅显示NumOfHits(找到IN语句的单词数)的结果等于IN语句中的单词数(如果搜索10个单词,则X为10)。

答案 2 :(得分:0)

不确定语法(这是sql server语法),但是:

-- N is the number of elements in the list

SELECT idDoc, COUNT(1)
FROM Word_Docs wd INNER JOIN Words w on w.idWord = wd.idWord
WHERE w.Word IN ('word1', ..., 'wordN')
GROUP BY wd.idDoc
HAVING COUNT(1) = N

即没有使用喜欢。随着事情变得更加复杂。

答案 3 :(得分:0)

Google Desktop Search或类似工具可能符合您的要求。