【问题标题】:Weird results with Solr 1.4 and EdgeNGrams - some substrings match, some don'tSolr 1.4 和 EdgeNGrams 的奇怪结果 - 一些子字符串匹配,一些不匹配
【发布时间】:2011-10-19 18:36:00
【问题描述】:

编辑 3:我现在使用的解决方法是从我的查询和索引字段中删除除字母、数字和空格之外的任何内容。这会产生所需的行为,但这在很大程度上是一种解决方法,而不是真正的解决方案,我仍然想了解为什么 Solr 正在做它正在做的事情......所以仍然对答案感兴趣,如果有人有答案的话。 结束编辑 3

我有一个名为“TT-14B”的文档,由 Solr 1.4 索引(通过 Django/Haystack)。当我在 content_auto 字段中查询“tt-1”或“tt 14”或“tt 14b”时,我会取回文档;当我查询“tt-14”或“tt-14b”时,我没有得到任何结果。我稍微编辑了 Haystack 生成的 Solr 模式以尝试解决此问题,但无济于事。使用analyze.jsp,在我看来我应该得到“tt-14”的匹配;我当然应该为“tt-14b”买一个。 (编辑:哦,将默认运算符从 AND 更改为 OR 并没有帮助。)

有人可以帮我理解为什么这不起作用吗?谢谢。

...

结果

QUERY  | WORKS
=======|======
tt     | yes
tt-    | yes
tt-1   | yes
tt-14  | no
tt-14b | no
tt 14  | yes
tt 14b | yes

编辑 2

得到了一些比较奇怪的结果,可能有助于调试问题。在这种情况下,测试文档是“abc'def”。

QUERY   | WORKS
========|======
abc     | yes
abc'd   | yes
abc'de  | no
abc'def | no

很明显,相同的模式,但我不明白是什么原因造成的。

结束编辑 2

schema.xml 相关部分(完整文件如下)

<fieldType name="edge_ngram" class="solr.TextField" positionIncrementGap="1">
  <analyzer type="index">
    <tokenizer class="solr.WhitespaceTokenizerFactory" />
    <filter class="solr.LowerCaseFilterFactory" />
    <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" splitOnNumerics="0" preserveOriginal="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
    <filter class="solr.EdgeNGramFilterFactory" minGramSize="1" maxGramSize="15" side="front" />
  </analyzer>
  <analyzer type="query">
    <tokenizer class="solr.WhitespaceTokenizerFactory" />
    <filter class="solr.LowerCaseFilterFactory" />
    <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" splitOnNumerics="0" preserveOriginal="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
  </analyzer>
</fieldType>

schema.xml(完整)

<?xml version="1.0" ?>
<!--
 Licensed to the Apache Software Foundation (ASF) under one or more
 contributor license agreements.  See the NOTICE file distributed with
 this work for additional information regarding copyright ownership.
 The ASF licenses this file to You under the Apache License, Version 2.0
 (the "License"); you may not use this file except in compliance with
 the License.  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
-->

<schema name="default" version="1.1">
  <types>
    <fieldtype name="string"  class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
    <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/>

    <!-- Numeric field types that manipulate the value into
         a string value that isn't human-readable in its internal form,
         but with a lexicographic ordering the same as the numeric ordering,
         so that range queries work correctly. -->
    <fieldType name="sint" class="solr.SortableIntField" sortMissingLast="true" omitNorms="true"/>
    <fieldType name="slong" class="solr.SortableLongField" sortMissingLast="true" omitNorms="true"/>
    <fieldType name="sfloat" class="solr.SortableFloatField" sortMissingLast="true" omitNorms="true"/>
    <fieldType name="sdouble" class="solr.SortableDoubleField" sortMissingLast="true" omitNorms="true"/>

    <fieldType name="date" class="solr.DateField" sortMissingLast="true" omitNorms="true"/>

    <fieldType name="text" class="solr.TextField" positionIncrementGap="100">
      <analyzer type="index">
        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
        <!-- in this example, we will only use synonyms at query time
        <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
        -->
        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" splitOnNumerics="0" preserveOriginal="1" catenateWords="1" catenateNumbers="1" catenateAll="1"/>
        <filter class="solr.LowerCaseFilterFactory"/>
        <filter class="solr.EnglishPorterFilterFactory" protected="protwords.txt"/>
        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
      </analyzer>
      <analyzer type="query">
        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" splitOnNumerics="0" preserveOriginal="1" catenateWords="0" catenateNumbers="0" catenateAll="1"/>
        <filter class="solr.LowerCaseFilterFactory"/>
        <filter class="solr.EnglishPorterFilterFactory" protected="protwords.txt"/>
        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
      </analyzer>
    </fieldType>

    <fieldType name="text_ws" class="solr.TextField" positionIncrementGap="100">
      <analyzer>
        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
      </analyzer>
    </fieldType>

    <fieldType name="ngram" class="solr.TextField" >
      <analyzer type="index">
        <tokenizer class="solr.KeywordTokenizerFactory"/>
        <filter class="solr.LowerCaseFilterFactory"/>
        <filter class="solr.NGramFilterFactory" minGramSize="2" maxGramSize="15" />
      </analyzer>
      <analyzer type="query">
        <tokenizer class="solr.KeywordTokenizerFactory"/>
        <filter class="solr.LowerCaseFilterFactory"/>
      </analyzer>
    </fieldType>

    <fieldType name="edge_ngram" class="solr.TextField" positionIncrementGap="1">
      <analyzer type="index">
        <tokenizer class="solr.WhitespaceTokenizerFactory" />
        <filter class="solr.LowerCaseFilterFactory" />
        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" splitOnNumerics="0" preserveOriginal="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
        <filter class="solr.EdgeNGramFilterFactory" minGramSize="1" maxGramSize="15" side="front" />
      </analyzer>
      <analyzer type="query">
        <tokenizer class="solr.WhitespaceTokenizerFactory" />
        <filter class="solr.LowerCaseFilterFactory" />
        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" splitOnNumerics="0" preserveOriginal="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
      </analyzer>
    </fieldType>
  </types>

  <fields>   
    <!-- general -->
    <field name="id" type="string" indexed="true" stored="true" multiValued="false" required="true"/>
    <field name="django_ct" type="string" indexed="true" stored="true" multiValued="false" />
    <field name="django_id" type="string" indexed="true" stored="true" multiValued="false" />

    <dynamicField name="*_i"  type="sint"    indexed="true"  stored="true"/>
    <dynamicField name="*_s"  type="string"  indexed="true"  stored="true"/>
    <dynamicField name="*_l"  type="slong"   indexed="true"  stored="true"/>
    <dynamicField name="*_t"  type="text"    indexed="true"  stored="true"/>
    <dynamicField name="*_b"  type="boolean" indexed="true"  stored="true"/>
    <dynamicField name="*_f"  type="sfloat"  indexed="true"  stored="true"/>
    <dynamicField name="*_d"  type="sdouble" indexed="true"  stored="true"/>
    <dynamicField name="*_dt" type="date"    indexed="true"  stored="true"/>


    <field name="modelname_exact" type="string" indexed="true" stored="true" multiValued="false" />

    <field name="modelname" type="text" indexed="true" stored="true" multiValued="false" />

    <field name="name" type="text" indexed="true" stored="true" multiValued="false" />

    <field name="text" type="text" indexed="true" stored="true" multiValued="false" />

    <field name="name_exact" type="string" indexed="true" stored="true" multiValued="false" />

    <field name="content_auto" type="edge_ngram" indexed="true" stored="true" multiValued="true" />

  </fields>

  <!-- field to use to determine and enforce document uniqueness. -->
  <uniqueKey>id</uniqueKey>

  <!-- field for the QueryParser to use when an explicit fieldname is absent -->
  <defaultSearchField>text</defaultSearchField>

  <!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
  <solrQueryParser defaultOperator="AND" />
</schema>

【问题讨论】:

    标签: search lucene indexing solr django-haystack


    【解决方案1】:

    每个案例的 /admin/analysis.jsp 的屏幕截图会很有趣。

    有什么原因吗,为什么positionIncrementGap="1"被设置为1?

    tt-14btt 14b 的处理方式不同,因为 whitspace 分词器。 这意味着: tt-14b 是一个术语,只要 WordDelimiterFilterFactory 没有被解雇,而 tt 14b 从一开始就是 2 个术语。 positionIncrementGap 使您可以将不同的术语视为一个短语,即使没有邻居,但在下一个“n”位置。所以尝试升positionIncrementGap

    顺便说一句:我在您的 schema.xml 上首先注意到的是在查询时缺少“EdgeNGramFilterFactory”。这应该没问题。但是也有可以理解的原因,为什么“查询和索引时间的相同过滤器”被视为最佳实践。 这取决于每种特殊情况,但在查询时激活此过滤器将是一种尝试。

    【讨论】:

    • 提高 positionIncrementGap 是否会降低匹配的可能性?无论如何,我在 0 和 20 都尝试了它,但两个值都没有解决问题。关于通过 ENGFF 进行查询 - 我认为我特别不希望这样。启用它可以使像“TT-15C”这样的查询匹配文档“TT-14B”,对吧?
    • positionIncrementGap 允许您定义词部分(标记)之间的差距。示例 Wi Fi,其中 Wi 来自 0-1,Fi 来自 2-3。这主要用于同义词。差距为 20,Wi 从 0 到 1,Fi 从 22 到 23。所以有20个“positionBlocks”(不知道,怎么称呼它)“免费”。它们属于 Wi Term,其中可以放置同义词,例如 Wi (0-1) Wireless (2-9) Fi (22-23)。我想,也许 ENGGG 从一个单词中生成了这么多术语,更大的间隙可能就像一个跨度。
    • @rfrankel 查看您的 analysis.jsp:一切看起来都很好。我们看到蓝色背景的字段,我们看到 tt-14b 匹配。但是,正如您所说,通过运行此查询,没有匹配项。顺便说一句:analysis.jsp 显示了应该如何处理查询,具体取决于加载的参数(来自 schema.xml)。它不运行真正的查询。这意味着:如果您更改了 schema.xml,而不重建索引,则搜索结果和 analysis.jsp 之间可能会有不同的结果
    • @rfrankel 您是否在未重建索引的情况下更改了 schema.xml 中的内容?使用ENGFF的原因是什么? (自动建议?)
    • 不,我重建了索引(并重新启动了 Solr 和 Apache,只是为了确保)。是的,自动建议。
    【解决方案2】:

    这个节目有点晚了——但是,如上所述,如果 WhitespaceTokenizerFactory 通过 StandardAnalyzer,它会用连字符分隔单词。我也发现了这一点……:

    Solr NGramTokenizerFactory and PatternReplaceCharFilterFactory - Analyzer results inconsistent with Query Results

    解决方案可能是使用 KeywordAnalyzer -- 它不应该拆分任何内容。

    我在上面的链接中(在 PHP 中)提出了与您的“EDIT 3”类似的解决方法。

    Solr 分析器特别令人沮丧的一点是,它显示一切都很好并且表现符合预期——这真的让我很困惑。

    祝你好运!

    【讨论】:

      猜你喜欢
      • 2011-05-17
      • 1970-01-01
      • 2011-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-12
      • 2018-06-13
      • 1970-01-01
      相关资源
      最近更新 更多