13

我的架构:

<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

架构更改后的分析器: 在此处输入图像描述

问题:

  1. 为什么“WalMart”与我的初始模式不匹配“Walmart”?Solr 分析器清楚地向我显示它在索引时间内产生了 3 个令牌:wal, mart, walmart. 在查询期间:它产生了 1 个令牌:(walmart虽然不清楚为什么它只会产生 1 个令牌),但我不明白为什么它不匹配,因为它walmart包含在查询和索引令牌中。

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

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

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

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

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

4

4 回答 4

5

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

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

于 2015-04-22T00:58:51.027 回答
4

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

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

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

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

您可以通过使用覆盖它LocalParams,您可以像这样改写您的查询{!mm=1}WalMart

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

这里也使用mm参数帮助。

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

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

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

但是我们缺少一个好的 synonyms.txt 并且不能保持最新。

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

更新我认为您也可以尝试PatternCaptureGroupTokenFilter使用正则表达式处理公司名称,但可能很快就会面临它的限制。稍后我将对此进行研究。


* 你可以在你的solrconfig.xml中找到这个,看看你的<requestHandler ... />

于 2015-05-11T13:45:11.673 回答
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所有标记化,但这会导致其他问题(特别是在处理更长、更复杂的文本时,比如你的“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”来匹配“Do Re Mi “, 例如)。但是对于给出的示例,这在我的测试中有效。

于 2015-04-22T08:28:40.690 回答
2

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

于 2015-05-14T04:49:26.527 回答