代码之家  ›  专栏  ›  技术社区  ›  Daniel Schneller

如何在数据库中搜索文本片段

  •  5
  • Daniel Schneller  · 技术社区  · 15 年前

    是否有任何开源或商业工具可用于对数据库内容进行文本片段索引,并可从Java查询?

    :[补充说明这些最初的建议无法解决问题的原因:]

    这就是为什么MySQL内置的全文索引不起作用,Lucene和Sphinx也不起作用,所有这些都是在答案中提出的。我已经看过了这两个,但据我所知,它们都是基于索引的 ,排除停止词,并为真正的全文搜索做各种合理的事情。然而,这并不合适,因为我可能正在寻找一个像“oison”这样的搜索词,它必须匹配“Roisonic Street”和“Toxin Ivy”。这里的关键区别在于搜索词只是一个 ,不需要用任何特殊字符或空格分隔。

    编辑2 :[添加了更多背景信息:] 在此基础上实现的请求功能是在商品管理系统中对商品描述进行非常松散的搜索。用户通常不知道正确的项目编号,但只知道项目名称的一部分。不幸的是,这些描述的质量相当低,它们来自遗留系统,不容易更改。例如,如果人们正在寻找一把大锤,他们会输入“sledge”。如果使用基于单词/标记的索引,则不会查找存储为“sledgehammer”的匹配项,而只查找那些侦听“sledgehammer”的匹配项。有各种各样奇怪的差异需要覆盖,这使得基于令牌的方法不切实际。

    目前我们唯一能做的就是 LIKE '%searchterm%' 查询,有效地禁用任何索引使用,并且需要大量资源和时间。

    理想情况下,任何这样的工具都会创建一个索引,使我能够非常快速地获得类似于此类查询的结果,这样我就可以实现类似聚光灯的搜索,只有在用户选择结果记录时,才可以通过主键从MySQL表中检索“真实”数据。

    如果可能的话,索引应该是可更新的(不需要完全重建),因为数据可能会更改,并且应该可以由其他客户端立即进行搜索。

    我很乐意得到建议和/或经验报告。

    尽管我在这个问题上得到了很多很好的答案,但我想在这里指出,最终我们推出了一款名为“QuickFind”的商业产品,由一家名为“HMB Datentechnik”的德国公司制造和销售。请注意,我是 website

    由于网上没有全面的文档,我将尝试描述我迄今为止的经历。

    他们所做的是基于数据库内容构建自定义索引文件。它们可以通过ODBC进行集成,但据我所知,客户很少这样做。相反,我们可能会这样做,从主数据库生成文本导出(如CSV),并将其提供给索引器。这允许您完全独立于实际的表结构(或任何SQL数据库);事实上,我们从多个表中导出连接在一起的数据。以后可以动态地增量更新索引。

    基于此,他们的服务器(仅250kb左右,作为控制台应用程序或Windows服务运行)侦听TCP端口上的查询。该协议是基于文本的,看起来有点“旧”,但它很简单并且有效。基本上,您只需传递要查询的可用索引和以空格分隔的搜索词(片段)。 有三种输出格式可用,HTML/JavaScript数组、XML或CSV。目前,我正在为有点“过时”的wire协议开发Java包装器。但是结果非常棒:我目前有一个大约500.000条记录的样本数据集,其中有8列被索引,我的测试应用程序触发了对所有8列的JTextField内容的搜索 在编辑过程中,可以实时更新结果显示(JTable)!这种情况发生时,不需要访问数据最初来自的MySQL实例。根据返回的列,您可以通过使用该行的主键(当然需要包含在QuickFind索引中)查询MySQL来请求“原始”记录。

    该索引大约是数据文本导出版本大小的30-40%。索引主要受磁盘I/O速度的限制;我的500000条记录需要大约一两分钟的时间来处理。

    很难描述这一点,因为当我看到一个内部产品演示时,我发现很难相信这一点。他们展示了一个1000万行的地址数据库,搜索姓名、地址和电话号码的片段,当点击“搜索”按钮时,结果在不到一秒钟的时间内返回——所有这些都在笔记本上完成!据我所知,他们经常与SAP或CRM系统集成,以缩短呼叫中心代理仅了解来电者姓名或地址片段时的搜索时间。

    所以不管怎样,我在描述这一点时可能不会有太多的进步。如果你需要这样的东西,你一定要去看看。 Google Translate 把他们的网站从德语翻译成英语做得相当不错,所以这可能是一个好的开始。

    10 回复  |  直到 15 年前
        1
  •  10
  •   JasonTrue    15 年前

    这可能不是您想要听到的,因为我假定您正试图用SQL代码解决这个问题,但是 Lucene 这将是我的第一选择。您还可以使用其他工具建立相当聪明的排名和提升技术。Lucene是用Java编写的,因此它应该为您提供所需的接口。

    如果您是一家Microsoft商店,那么您需要的大部分内容都内置于SQL Server中,并且可以启用通配符,这将使您能够进行部分单词匹配。

    在Lucene和Lucene.Net中,您可以使用 wildcard matches

    我相当肯定,单词两端的通配符搜索本质上是低效的。跳过列表有时被用来提高纯文本搜索的性能,但我认为在grep之类的工具中更可能找到这样的实现,而不是通用的文本索引工具。

    对于您描述的问题,还有其他解决方案,其中一个单词可以拼写为两个,反之亦然。例如,Lucene支持模糊查询。正交和形态变体可以通过提供基于某种贝叶斯机制提供建议的过滤器来处理,也可以通过索引技巧来处理,即,获取频繁变体的语料库并用这些术语填充索引。我甚至看到了从填充到全文引擎中的结构化数据中获得的知识(例如,将城市名称和“酒店”一词添加到酒店表的记录中,以使“巴黎酒店”更有可能包含养老院Caisse des Dpts的记录)。虽然这不是一个小问题,在不破坏基于单词的搜索优势的情况下,它是可管理的。

        2
  •  4
  •   Benjamin Cox    15 年前

    我自己还没有这个具体的要求,但我的经验告诉我Lucene可以做到,尽管可能不是独立的。我肯定会通过Solr使用它,正如Michael Della Bitta在第一个答案中所描述的那样。他给出的链接是现场阅读,了解更多背景信息。

    简单地说,Solr允许您定义自定义字段类型。它们由索引时间分析器和查询时间分析器组成。分析程序会找出如何处理文本,每个分析程序都由一个标记器和零到多个标记过滤器组成。标记器将文本分割成块,然后每个标记过滤器可以添加、减去或修改标记。

    因此,该字段可以索引与原始文本完全不同的内容,必要时包括多个标记。因此,您需要的是原始文本的多标记副本,您可以通过发送Lucene类似于“my_ngram_field:sledge”的内容来查询它。不涉及通配符:-)

    然后遵循与solrconfig.xml文件中提供的前缀搜索类似的模型:

    <fieldType name="prefix_token" class="solr.TextField" positionIncrementGap="1">
        <analyzer type="index">
            <tokenizer class="solr.WhitespaceTokenizerFactory"/>
            <filter class="solr.LowerCaseFilterFactory" />
            <filter class="solr.EdgeNGramFilterFactory" minGramSize="1" maxGramSize="20"/>
        </analyzer>
        <analyzer type="query">
            <tokenizer class="solr.WhitespaceTokenizerFactory"/>
            <filter class="solr.LowerCaseFilterFactory" />
        </analyzer>
    </fieldType>
    

    EdgeNGramFilterFactory是如何为搜索框自动完成实现前缀匹配的。它接受来自前面阶段的标记(单个空格分隔的单词转换成小写),并将它们扇出到前缘的每个子字符串中。大锤=s、sl、sle、大锤、大锤、大锤、大锤等。

    您需要遵循此模式,但将EdgeNGramFilterFactory替换为您自己的工厂,该工厂负责现场的所有NGrams。默认的org.apache.solr.analysis.NGramFilterFactory是一个很好的开始,但是它为拼写检查提供了字母转换。你可以复制它并去掉它——这是一个很容易实现的类。

    一旦您使用自己的MyNGramFilterFactory拥有自己的字段类型(称为ngram_text),只需创建原始字段和ngram字段,如下所示:

        <field name="title" type="text" indexed="true" stored="true"/>
        <field name="title_ngrams" type="ngram_text" indexed="true" stored="false"/>
    

    然后告诉它将原始字段复制到奇特的字段中:

    <copyField source="title" dest="title_ngrams"/>
    

    好的,现在当您搜索“title\n ngrams:sledge”时,您应该会得到包含此内容的文档列表。然后在查询的字段列表中,您只需告诉它检索名为title的字段,而不是字段title。

    这应该是一个足够的轻推,让您可以把事情放在一起,并调整到惊人的性能水平相当容易。在以前的一份工作中,我们有一个数据库,其中包含超过1000万个具有大型HTML描述的产品,并设法让Lucene在一个中型服务器上以不到200毫秒的速度执行标准查询和拼写检查,该服务器同时处理几十个查询。当你有很多用户的时候,缓存就会起作用,让它尖叫起来!

    祝你好运

        4
  •  3
  •   Michael Della Bitta    15 年前

    我会使用ApacheSolr。索引策略是完全可调的(请参阅 http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters ),可以直接从数据库增量读取以填充索引(请参见同一wiki中的DataImportHandler),基本上可以从任何讲HTTP和XML或类似JSON的语言进行查询。

        5
  •  2
  •   dusoft    15 年前

    如何使用上面建议的工具(lucene等)进行全文索引,并进行类似的案例搜索,在这些案例中什么也找不到?(即,仅在全文索引搜索返回零结果后运行LIKE)

        6
  •  2
  •   Bob Aman    15 年前

    LIKE '%searchterm%' 没有大量的自定义代码。相当于 LIKE 'searchterm%' 但这应该是微不足道的。你可以按照你的要求建立一个索引,索引所有可能的部分单词,这些单词不在后面的通配符范围内,但是这会导致一个难以置信的大索引,而且更新速度会异常缓慢。长的代币会导致不好的结果。我可以问一下吗 你需要这个吗?回复:聚光灯。。。你知道聚光灯不能做到这一点,对吗?它是基于令牌的,就像其他所有全文索引器一样。通常,查询扩展是获得不精确匹配的合适方法,如果这是您的目标的话。

    编辑:

    我曾经有过一个完全像这样的项目;各种材料的零件号。我们终于决定了 searchterm* 在Xapian,但我相信Lucene也有类似的功能。您不会找到一个很好的解决方案来处理令牌两侧的通配符搜索,但尾随通配符通常足以满足您的需要,我怀疑您会发现,如果用户能够控制清理数据,他们会很快适应您的系统。将它与查询扩展(甚至是有限的令牌扩展)结合起来,您应该已经准备好了。查询扩展将“sledgehammer”查询转换为“sledgehammer*或(sledge*hammer*)”或类似的查询。并不是每个查询都能工作,但是人们已经经过了很好的训练,可以在某些事情不起作用时尝试相关的查询,只要至少有一两个明显的查询产生了他们期望的结果,您就可以了。您最好的选择仍然是清理数据并更好地组织它。如果您对所有内容进行版本化并实施平等的编辑策略,您会惊讶地发现这是多么容易。也许可以让人们在条目中添加关键字,并确保对这些关键字进行索引,但要限制可以设置的关键字数量。太多,您可能会降低搜索结果。

        7
  •  2
  •   yk4ever    15 年前

    木瓦搜索可以做到这一点。

    http://en.wikipedia.org/wiki/W-shingling

    请注意,3个字符的shingle要求查询中的片段长度至少为5个字符,4个字符的shingle要求7个字符的查询,依此类推。

        8
  •  1
  •   ideasculptor    15 年前

    right here 另一个问题是,对于您的数据大小来说,它是否能够充分发挥作用。

        9
  •  0
  •   Community Mr_and_Mrs_D    7 年前

    我敢肯定Mysql提供了一个全文选项,而且可能还可以使用Lucene。

    有关评论,请参见此处

    Best efficient way to make a fulltext search in MySQL

        10
  •  0
  •   James Anderson    15 年前

    使用部分单词的“真实”全文索引将比源文本大很多倍,虽然搜索速度可能更快,但任何更新或插入处理都将非常缓慢。