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

时间:2009-10-26 22:27:41

标签: mysql database search indexing full-text-search

10 个答案:

答案 0 :(得分:10)

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

如果您是Microsoft商店,那么您所寻找的大部分内容都内置在SQL Server中,并且可以启用通配符,这样您就可以进行部分单词匹配。

在Lucene和Lucene.Net中,如果您愿意,可以使用wildcard matches。但是,不支持将通配符用作搜索中的第一个符号。如果你想要能够使用第一个字符通配符,你可能需要自己实现某种基于trie的索引,因为在很多情况下,将一组术语过滤到合理的类型是一种昂贵的操作全文搜索应用程序最常需要的索引,其中后缀词干通常更有价值。

您可以通过将setAllowLeadingWildcard设置为true来显式更改Lucene中的Query Parser实例以覆盖此规则。

我很确定两端的通配符搜索本身效率低下。跳过列表有时用于通过明文提高此类搜索的性能,但我认为您更有可能在类似grep的实现中找到类似于广义文本索引工具的实现。

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

答案 1 :(得分:4)

我自己没有这个具体要求,但我的经验告诉我Lucene可以做到这一点,虽然可能不是独立的。我肯定会在第一个答案中通过Michael Della Bitta所描述的Solr使用它。他给出的链接是正确的 - 阅读它以获得更多背景。

简而言之,Solr允许您定义自定义FieldTypes。它们由索引时分析器和查询时分析器组成。分析器弄清楚如何处理文本,每个文本都包含一个Tokenizer和零到多个TokenFilters。 Tokenizer将您的文本拆分为块,然后每个TokenFilter可以添加,减去或修改令牌。

因此,字段可以最终索引与原始文本完全不同的内容,包括必要时的多个标记。所以你想要的是原始文本的多重令牌副本,你可以通过向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是如何为搜索框自动完成实现前缀匹配的。它需要来自前一阶段的令牌(单个空格分隔的单词转换为小写)并将它们扇出到前沿的每个子字符串中。 sledgehammer = s,sl,sle,sled,sledg,sledge,sledgeh等。

您需要遵循此模式,但将EdgeNGramFilterFactory替换为您自己的所有NGrams。默认的org.apache.solr.analysis.NGramFilterFactory是一个很好的开始,但它会进行字母转置以进行拼写检查。你可以复制它并删除它 - 这是一个非常简单的实现类。

使用自己的MyNGramFilterFactory获得自己的FieldType(称之为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_ngrams:sledge”时,你应该得到一个包含它的文件列表。然后在查询的字段列表中,您只需告诉它检索名为title的字段而不是字段title_ngrams。

这应该足以让你把东西放在一起,并且很容易将它调整到令人惊讶的性能水平。在一份旧工作中,我们拥有一个拥有超过一千万个带有大量HTML描述的产品的数据库,并设法让Lucene在处理几十个同步查询的中型服务器上进行标准查询和200毫秒以下的拼写检查。当你有很多用户时,缓存就会开始并让它尖叫!

哦,增量(虽然不是实时)索引很简单。它甚至可以在高负载下执行它,因为它在后台创建并优化新索引并在交换之前自动进行归档。非常光滑。

祝你好运!

答案 2 :(得分:3)

如果您的表格是MyISAM,您可以使用MySQL的全文搜索功能:http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html

如果没有,“行业标准”是http://www.sphinxsearch.com/

如果您使用InnoDB该怎么做的一些想法:http://www.mysqlperformanceblog.com/2009/09/10/what-to-do-with-mysql-full-text-search-while-migrating-to-innodb/

此外,介绍Sphinx并介绍架构+用法的精彩演示 http://www.scribd.com/doc/2670976/Sphinx-High-Performance-Full-Text-Search-for-MySQL-Presentation

更新
阅读了你对问题的澄清--Sphinx可以进行子串匹配。您需要设置“enable-star”并使用适当的min_infix_length创建中缀索引(1将为您提供所有可能的子字符串,但显然设置越高,索引越小,搜索越快)。有关详细信息,请参阅http://sphinxsearch.com/docs/current.html

答案 3 :(得分:3)

我使用的是Apache Solr。索引策略完全可调(参见http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters),可以直接从数据库中逐步读取以填充索引(请参阅同一维基中的DataImportHandler),并且可以从基本上任何使用HTTP和XML的语言查询像JSON。

答案 4 :(得分:2)

在没有大量自定义代码的情况下,您尝试做的事情不可能比LIKE '%searchterm%'快得多。相当于LIKE 'searchterm%'应该是微不足道的。您可以通过构建所有可能的部分单词的索引来执行您所要求的内容,这些单词不包含在尾随的通配符中,但这会导致索引大小难以置信,并且更新速度异常缓慢。长令牌会导致Bad Things™。我可以问为什么你需要这个吗? Re:Spotlight ...你确实意识到Spotlight没有这样做,对吧?它就像所有其他全文索引器一样基于令牌。通常,查询扩展是获取不精确匹配的适当方法,如果这是您的目标。

修改

我有一个完全像这样的项目;各种东西的部件号。我们终于在Xapian中找到了searchterm*,但我相信Lucene也有同等价值。你不会找到一个很好的解决方案来处理令牌两侧的外卡搜索,但是一张尾随的外卡通常比你想要的更好,我怀疑你会发现用户适应你的如果他们对清理数据有任何控制,那么系统会很快。将它与查询扩展(甚至是有限的令牌扩展)相结合,你应该设置得很好。查询扩展会将“sledgehammer”的查询转换为“sledgehammer * OR(sledge * hammer *)”或类似内容。并非每个查询都能正常运行,但是当某些东西不能正常工作时,人们已经接受过很好的训练来尝试相关的查询,并且只要至少有一两个明显的查询得出他们期望的结果,你应该没问题。您最好的选择仍然是清理数据并更好地组织数据。如果您对所有内容进行版本控制并实施平等主义编辑策略,您会感到很惊讶。也许让人们在条目中添加关键字并确保对这些关键字进行索引,但是对可以设置的数量进行限制。太多,您实际上可能会降低搜索结果。

答案 5 :(得分:2)

如何使用上面提出的工具(lucene等)进行全文索引并对LIKE进行搜索以找不到任何内容? (即仅在全文索引搜索返回零结果后运行LIKE)

答案 6 :(得分:2)

Shingle搜索可以解决问题。

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

例如,如果您使用3个字符的带状疱疹,您可以将“Roisonic”拆分为:“roi”,“son”,“ic”,并存储所有三个值,并将它们与原始条目相关联。搜索“oison”时,首先会搜索“ois”,“iso”,“son”。首先,您通过带状疱疹模糊匹配所有条目(找到带有“儿子”的条目),然后您可以使用精确的字符串匹配来优化搜索。

请注意,3个字符的shingle要求查询中的片段长度至少为5个字符,4-char shingle需要7-char查询等等。

答案 7 :(得分:1)

您问题的确切答案是right here另一个问题是,它是否能够充分发挥数据的大小。

答案 8 :(得分:0)

我非常确定Mysql提供了一个全文选项,也可能使用Lucene。

请参阅此处以获取相关评论

Best efficient way to make a fulltext search in MySQL

答案 9 :(得分:0)

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

你只希望是否存在某种“错误”的模式。你可以对传入的文本应用一组“AI”类型规则并生成文本的形式,然后你可以应用一个完整的规则的一个例子可能是将以锤子结尾的单词分成两个单词s /(\ w?)(hammer)/ \ 1 \ 2 / g或更改“sledg”“sled”和“schledge”你需要在查询文本中应用相同的规则。通过搜索“sledg hammer”可以匹配描述为“大锤”的产品。