使用空格、连字符、大小写和标点符号的各种组合进行搜索
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 个令牌:wal
、mart
、walmart
。在查询期间:它产生了 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 现在将在计入 <str name="mm">100%</str>
* 时单独处理每个单词。
顺便说一句,我已经重现了您的问题,但是在索引 Walmart 时出现问题,但使用 WalMart 进行查询。当反过来执行它时,它工作正常。
您可以使用LocalParams
覆盖它,您可以像!mm=1WalMart
一样重新表述您的查询。
还有一些稍微复杂的,例如 [ ... ] "Mc Donald's" [匹配] 带有不同标点符号的单词:"Mc-Donald Engineering Company, Inc."
在这里也可以使用mm
参数帮助。
一般来说,用这种需求对模式进行建模的最佳方法是什么?
我同意 Sujit Pal 的观点,你应该去实现自己的 SynonymFilter
副本。为什么?因为它与其他过滤器和标记器的工作方式不同。它在索引词的偏移处创建标记。
什么地方?它不会增加查询的令牌数。并且您可以执行后面的连字符(连接两个以空格分隔的单词)。
但我们缺少一个好的 synonyms.txt,无法保持最新。
扩展或复制SynonymFilter
时忽略静态映射。您可以删除映射单词的代码。你只需要偏移处理。
更新我想你也可以试试PatternCaptureGroupTokenFilter
,但是用正则表达式处理公司名称可能很快就会面临它的限制。我稍后会看看这个。
* 你可以在你的 solrconfig.xml 中找到它,看看你的<requestHandler ... />
【讨论】:
【参考方案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 -> Wal Mart
、Wal-mart -> Wal Mart
、WalMart -> Wal Mart
。同样对于麦当劳的其他用例,这些案例也将失败:McDonald's -> Mc Donald's
、McDonald's -> Mc Donalds
、McDonald's -> Mc donald's
、McDonald's -> 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 版本更新代码。以上是关于使用空格、连字符、大小写和标点符号的各种组合进行搜索的主要内容,如果未能解决你的问题,请参考以下文章