【问题标题】:Dynamic search and display动态搜索和显示
【发布时间】:2010-09-13 23:03:34
【问题描述】:

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

我的要求如下:

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

我想出的解决方案的可能方法如下: 我创建了一个包含三个表的数据库(很可能使用 mysql):“Documents”、“Words”和“Word_Docs”。

  • “文档”将包含所有文档的(idDoc、名称、位置)。
  • '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也可以使用这种类型的搜索解决方案来浏览标签?(在主页的右上角)

【问题讨论】:

    标签: algorithm search


    【解决方案1】:

    不确定语法(这是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
    

    也就是说,不使用like。类似的事情要复杂得多。

    【讨论】:

      【解决方案2】:

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

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

      2. 把你找到的所有单词按字母顺序排序;最快的方法是使用Three Way Radix QuickSort。在对字符串进行排序时,这种算法在性能上很难被击败。

      3. 将排序后的列表写入磁盘,一行一个字。

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

      或者,您可以将步骤 (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)。

      【讨论】:

      • 文档内容是静态的(不会改变);有超过 1 Gib 的文件并且它可能会增长。我将不得不研究其余的你的答案。
      【解决方案3】:

      Google Desktop Search 或类似工具可能满足您的要求。

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-01-01
          • 2016-03-25
          • 1970-01-01
          • 1970-01-01
          • 2017-10-11
          • 2016-08-09
          • 2012-02-06
          • 1970-01-01
          相关资源
          最近更新 更多