搜索空间,连字符,套管和标点符号的各种组合

时间:2015-04-21 21:14:38

标签: solr lucene string-matching solrj textmatching

我的架构:

<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>

我想要工作的组合:

  

&#34; Walmart&#34;,&#34; WalMart&#34;,&#34; Wal Mart&#34;,&#34; Wal-Mart&#34;,&#34; Wal-mart&# 34;

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

因此,下面给出了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"

分析仪的屏幕截图:

Analyzer screenshot using initial schema

我尝试了各种过滤器组合试图解决这些限制,因此我在以下问题时遇到了问题:Solr - case-insensitive search do not work

虽然它似乎克服了我的一个限制(参见#5 WalMart - &gt;沃尔玛),但它总体上比我之前更糟糕。现在它不适用于以下情况:

(Wal Mart,WalMart), 
(Wal-Mart,WalMart), 
(Wal-mart,WalMart), 
(WalMart,Wal Mart)
besides cases 1 to 4 as mentioned above
架构更改后

分析器: enter image description here

问题:

  1. 为什么&#34; WalMart&#34;不匹配&#34;沃尔玛&#34;用我的初始架构? Solr分析器清楚地告诉我它在索引时间内产生了3个令牌:walmartwalmart。在查询期间:它生成了1个令牌:walmart(虽然它不清楚为什么它只生成1个令牌),但我不明白为什么它不匹配,因为walmart是包含在查询和索引标记中。

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

      

    带撇号的单词:&#34; Mc Donalds&#34;,&#34; Mc Donald&#34;,&#34;麦当劳&#34;,&#34; Mc donalds& #34;,&#34; Mc donald&#34;,&#34; Mcdonald&#34;

         

    带有不同标点符号的单词:&#34; Mc-Donald Engineering Company,Inc。&#34;

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

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

4 个答案:

答案 0 :(得分:4)

我们将带连字符的单词视为一种特殊情况,并编写了一个在索引时使用的自定义分析器来创建此令牌的三个版本,因此在您的情况下,沃尔玛将成为沃尔玛,沃尔玛和沃尔玛。这些同义词中的每一个都是使用自定义的SynonymFilter写出来的,该定制最初是根据Lucene in Action手册中的一个例子改编的。 SynonymFilter位于Whitespace tokenizer和Lowercase tokenizer之间。

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

答案 1 :(得分:4)

  

为什么&#34; WalMart&#34;不匹配&#34;沃尔玛&#34;我的初始架构?

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

因为您使用相同的分析器进行查询和索引时间。您的搜索字词&#34; WalMart&#34;分为3个令牌(单词)。即这些是&#34; wal&#34;,&#34; mart&#34;和&#34;沃尔玛&#34;。 Solr现在将在计算<str name="mm">100%</str> *。

时单独处理每个单词

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

您可以使用LocalParams覆盖此问题,您可以像{!mm=1}WalMart一样重新查询您的查询。

  

还有一些稍微复杂的东西,如[...]&#34;麦当劳&#34; [匹配]带有不同标点符号的单词:&#34; Mc-Donald Engineering Company,Inc。&#34;

此处还使用mm参数有帮助。

  

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

在这里,我同意Sujit Pal,你应该去实现SynonymFilter的自己的副本。为什么?因为它与其他过滤器和标记器的工作方式不同。它创建了令牌,用于索引单词的偏移量。

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

  

但是我们缺少一个好的synonyms.txt并且无法使其保持最新状态。

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

更新我认为您也可以尝试使用PatternCaptureGroupTokenFilter,但使用正则表达式处理公司名称可能很快就会面临其问题。限制。我稍后会看一下。

*您可以在 solrconfig.xml 中找到此信息,查看您的<requestHandler ... />

答案 2 :(得分:2)

我将冒昧首先对分析仪进行一些调整。我认为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执行所有的令牌化,但这会导致其他问题(尤其是处理更长,更复杂的文本,例如你的“麦克唐纳德工程公司”公司的例子就会有问题。)

相反,我建议使用ShingleFilter。这允许您将相邻的令牌组合成单个令牌以进行搜索。这意味着,当索引“沃尔玛”时,它将采用代币“沃尔玛”和“市场”,并将术语“沃尔玛”编入索引。通常,它也会插入分隔符,但在这种情况下,您需要覆盖该行为,并指定""的分隔符。

我们现在将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个连续令牌(以及原始单个令牌),因此我假设您不需要匹配更多(如果您需要“doremi”匹配“ Do Re Mi“,例如)。但是对于给出的例子,这在我的测试中起作用。

答案 3 :(得分:2)

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