使用空格、连字符、大小写和标点符号的各种组合进行搜索

Posted

技术标签:

【中文标题】使用空格、连字符、大小写和标点符号的各种组合进行搜索【英文标题】:Search with various combinations of space, hyphen, casing and punctuations 【发布时间】:2015-06-29 06:21:57 【问题描述】:

我的架构:

<fieldType name="text" class="solr.TextField" positionIncrementGap="100">
  <analyzer>
    <tokenizer class="solr.WhitespaceTokenizerFactory"/>
    <filter class="solr.StopFilterFactory"
            ignoreCase="true"
            words="stopwords.txt"
            enablePositionIncrements="true"
            />
    <filter class="solr.WordDelimiterFilterFactory"
            generateWordParts="1" generateNumberParts="1"
            catenateWords="1" catenateNumbers="1" catenateAll="0"
            splitOnCaseChange="1" splitOnNumerics="0"/>
    <filter class="solr.LowerCaseFilterFactory"/>
    <filter class="solr.SnowballPorterFilterFactory" language="English"
            protected="protwords.txt"/>
  </analyzer>
</fieldType>

我想使用的组合:

“沃尔玛”、“沃尔玛”、“沃尔玛”、“沃尔玛”、“沃尔玛”

给定这些字符串中的任何一个,我想找到另一个。

所以,有 25 种这样的组合,如下所示:

(第一列表示搜索的输入文本,第二列表示预期匹配)

(Walmart,Walmart)
(Walmart,WalMart)
(Walmart,Wal Mart)
(Walmart,Wal-Mart)
(Walmart,Wal-mart)
(WalMart,Walmart)
(WalMart,WalMart)
(WalMart,Wal Mart)
(WalMart,Wal-Mart)
(WalMart,Wal-mart)
(Wal Mart,Walmart)
(Wal Mart,WalMart)
(Wal Mart,Wal Mart)
(Wal Mart,Wal-Mart)
(Wal Mart,Wal-mart)
(Wal-Mart,Walmart)
(Wal-Mart,WalMart)
(Wal-Mart,Wal Mart)
(Wal-Mart,Wal-Mart)
(Wal-Mart,Wal-mart)
(Wal-mart,Walmart)
(Wal-mart,WalMart)
(Wal-mart,Wal Mart)
(Wal-mart,Wal-Mart)
(Wal-mart,Wal-mart)

我的架构的当前限制:

1. "Wal-Mart" -> "Walmart",
2. "Wal Mart" -> "Walmart",
3. "Walmart"  -> "Wal Mart",
4. "Wal-mart" -> "Walmart",
5. "WalMart"  -> "Walmart"

分析仪截图:

我尝试了各种过滤器组合试图解决这些限制,所以我被以下提供的解决方案绊倒了:Solr - case-insensitive search do not work

虽然它似乎克服了我的限制之一(参见#5 WalMart -> Walmart),但总体上比我之前的要差。现在它不适用于以下情况:

(Wal Mart,WalMart), 
(Wal-Mart,WalMart), 
(Wal-mart,WalMart), 
(WalMart,Wal Mart)
besides cases 1 to 4 as mentioned above

架构更改后的分析器:

问题:

    为什么“沃尔玛”与我的初始架构不匹配? Solr 分析器清楚地显示它在索引期间生成了 3 个令牌:walmartwalmart。在查询期间:它产生了 1 个令牌:walmart(虽然不清楚为什么它只会产生 1 个令牌),但我不明白为什么它不匹配,因为 walmart 包含在查询和索引令牌中。

    我在这里提到的问题只是一个用例。还有一些稍微复杂的,比如:

    带撇号的单词:“Mc Donalds”、“Mc Donald's”、“McDonald's”、“Mc donalds”、“Mc donald's”、“Mcdonald's”

    带有不同标点符号的单词:“Mc-Donald Engineering Company, Inc.”

一般来说,用这种需求对模式进行建模的最佳方法是什么? NGrams ?索引不同字段(不同格式)中的相同数据并使用 copyField 指令(https://wiki.apache.org/solr/SchemaXml#Indexing_same_data_in_multiple_fields)?这对性能有何影响?

编辑:我的 Solr 架构中的默认运算符是 AND。我无法将其更改为 OR。

【问题讨论】:

【参考方案1】:

在 solrconfig.xml 中升级 Lucene 版本(4.4 到 4.10)神奇地解​​决了这个问题!我不再有任何限制,我的查询分析器也按预期运行。

【讨论】:

从 4.4 升级到 4.10 是一次升级... :)【参考方案2】:

为什么“WalMart”与我的初始架构不匹配“Walmart”?

因为您已将 DisMax/eDismax 处理程序的 mm 参数定义为过高的值。我玩过它。当您将 mm 值定义为 100% 时,您将无法匹配。但为什么呢?

因为您对查询和索引时间使用相同的分析器。您的搜索词“沃尔玛”分为 3 个标记(单词)。即这些是“wal”、“mart”和“walmart”。 Solr 现在将在计入 &lt;str name="mm"&gt;100%&lt;/str&gt;* 时单独处理每个单词。

顺便说一句,我已经重现了您的问题,但是在索引 Walmart 时出现问题,但使用 WalMart 进行查询。当反过来执行它时,它工作正常。

您可以使用LocalParams 覆盖它,您可以像!mm=1WalMart 一样重新表述您的查询。

还有一些稍微复杂的,例如 [ ... ] "Mc Donald's" [匹配] 带有不同标点符号的单词:"Mc-Donald Engineering Company, Inc."

在这里也可以使用mm 参数帮助。

一般来说,用这种需求对模式进行建模的最佳方法是什么?

我同意 Sujit Pal 的观点,你应该去实现自己的 SynonymFilter 副本。为什么?因为它与其他过滤器和标记器的工作方式不同。它在索引词的偏移处创建标记。

什么地方?它不会增加查询的令牌数。并且您可以执行后面的连字符(连接两个以空格分隔的单词)。

但我们缺少一个好的 synonyms.txt,无法保持最新​​。

扩展或复制SynonymFilter 时忽略静态映射。您可以删除映射单词的代码。你只需要偏移处理。

更新我想你也可以试试PatternCaptureGroupTokenFilter,但是用正则表达式处理公司名称可能很快就会面临它的限制。我稍后会看看这个。


* 你可以在你的 solrconfig.xml 中找到它,看看你的&lt;requestHandler ... /&gt;

【讨论】:

【参考方案3】:

我会冒昧地先对分析仪进行一些调整。我认为WordDelimiterFilter 在功能上是第二步标记化,所以让我们把它放在 Tokenizer 之后。之后就不需要维护大小写了,接下来就是小写了。这对您的StopFilter 更好,因为我们不再需要担心忽略大小写。然后添加词干分析器。

<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1" splitOnNumerics="0"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.StopFilterFactory"
        words="stopwords.txt"
        enablePositionIncrements="true"
        />
<filter class="solr.SnowballPorterFilterFactory" language="English" protected="protwords.txt"/>

总而言之,这并不遥远。主要问题是“沃尔玛”与“沃尔玛”。对于其中的每一个,WordDelimiterFilter 都与它无关,它是分词器在这里分裂。 “沃尔玛”被分词器拆分。 “沃尔玛”永远不会被拆分,因为没有人可以合理地知道它应该在哪里拆分。

一种解决方案是改用KeywordTokenizer,并让WordDelimiterFilter 完成所有的标记,但这会导致其他问题(特别是在处理更长的时间时,更复杂的文本,例如您的“Mc-Donald Engineering Company, Inc.”示例将是有问题的)。

相反,我推荐ShingleFilter。这允许您将相邻的标记组合成单个标记以进行搜索。这意味着,在索引“Wal Mart”时,它将采用标记“wal”和“mart”,并索引术语“walmart”。通常,它还会插入一个分隔符,但在这种情况下,您需要覆盖该行为,并指定一个分隔符 ""

我们现在将 ShingleFilter 放在最后(如果你把它放在词干分析器之前,它往往会搞砸词干):

<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1" splitOnNumerics="0"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.StopFilterFactory"
        words="stopwords.txt"
        enablePositionIncrements="true"
        />
<filter class="solr.SnowballPorterFilterFactory" language="English" protected="protwords.txt"/>
<filter class="solr.ShingleFilterFactory" maxShingleSize="2" tokenSeparator=""/>

这只会创建 2 个连续令牌(以及原始的单个令牌)的 shingle,所以我假设您不需要匹配更多(如果您需要“doremi”来匹配“做 Re Mi”,例如)。但是对于给出的示例,这在我的测试中有效。

【讨论】:

虽然这消除了Walmart --> Wal Mart 案例的限制,但总体而言更糟糕的是这 3 个较早通过的案例失败了:Wal-Mart -&gt; Wal MartWal-mart -&gt; Wal MartWalMart -&gt; Wal Mart。同样对于麦当劳的其他用例,这些案例也将失败:McDonald's -&gt; Mc Donald'sMcDonald's -&gt; Mc DonaldsMcDonald's -&gt; Mc donald'sMcDonald's -&gt; Mc donalds 您对分析仪进行更改后是否重新编制索引? 我从头开始,重新启动 Solr 并重新运行我的测试(先进行索引,然后进行查询)。 不知道该告诉你什么。听起来像是某处不匹配的分析仪。我确实尝试了一些这样的案例,它们对我有用。 我可以知道您使用的是什么版本的 Solr 吗?如果重要的话,还有 Lucene 版本?【参考方案4】:

我们将连字符单词视为一种特殊情况,并编写了一个自定义分析器,在索引时使用该分析器来创建此令牌的三个版本,因此在您的情况下,沃尔玛将变为 walmart、wal mart 和 wal-mart。这些同义词中的每一个都是使用自定义 SynonymFilter 编写的,该自定义 SynonymFilter 最初改编自 Lucene in Action 书中的示例。 SynonymFilter 位于 Whitespace 分词器和小写分词器之间。

在搜索时,三个版本中的任何一个都会匹配索引中的同义词之一。

【讨论】:

感谢您花时间回答。如果我有一个好的同义词数据集,SynonymFilters 就可以工作,但不幸的是,我的情况并非如此。 难道不能扫描您的索引以查找带连字符的单词并使用它们吗?这可能并不完美,但它是一个开始。 连字符只是其中一种情况。还有其他类型的标点符号。恐怕我们甚至可以在这种特殊情况下进行扩展:) 我将您的答案与 femtoRgon 的答案结合起来,这正是我想要的。您介意指出一个关于编写自定义 SynonymFilter 的示例以及如何在自定义分析器中使用它吗? 我的代码所基于的示例可以在 Lucene in Action(第 4.6 节)中找到 - 我相信这里的 Lucene 版本是 3.x,这也是我们编写原始代码时所针对的。由于分析 API 在 3.x 和 4.x 之间发生了更改,因此需要针对 4.x 版本更新代码。

以上是关于使用空格、连字符、大小写和标点符号的各种组合进行搜索的主要内容,如果未能解决你的问题,请参考以下文章

比较不包括标点符号和空格的字符串

java :6—20个字符,只能包含大小写,数字,标点(空格除外) 正则表达式怎么写?

如何用Linux 终端指令打开带有空格或特殊符号的目录

大数据项目——互联网精准营销——数据清洗

Java基础部分

知识归纳字体编码