【问题标题】:Revelants queries suggestion for autocomplete with SolrRevelants 查询使用 Solr 自动完成的建议
【发布时间】:2017-07-28 22:35:42
【问题描述】:

我将 Solr 6.4 与 Haystack 2.6.1、pySolr 3.6 一起使用:

我正在寻找类似 google 的建议自动完成功能。实际上使用 EdgeNGram 效果很好,但它只返回我不想要的文件标题:

示例:

typing: 'new y'
return:

New york, fabulous city that never sleep
A trip to new york by night
...

这使用户只能选择在建议列表中特别是选择一个文档,并且搜索将仅返回基于建议标题进行搜索的文档。

我想要的是一些相关词的建议,例如:

typing: 'new y'
return:

new york
new york by night
new york city
trip to new york

有一篇文章建议使用返回结果的用户的索引查询,然后将这些查询用作建议: https://lucidworks.com/2009/09/08/auto-suggest-from-popular-queries-using-edgengrams/

这意味着解析 solr 日志或从数据库中保存的一组用户查询中使用数据导入 (DIH)。

实际上这篇文章已经很老了(2009 年),从那时起 Solr 就为我们带来了 Suggester (https://cwiki.apache.org/confluence/display/solr/Suggester)

无论如何,我想知道是否真的有一个很好的教程,介绍如何将 Suggester 与相关查询一起使用,而不是返回我的文档标题,而无需将用户的查询保存在数据库中,通过预定流程导入它们、重新索引等。

我的 search_indexes.py

class ArticleIndex(indexes.SearchIndex, indexes.Indexable): 

    text = indexes.CharField(document=True, use_template=True)
    created = indexes.DateTimeField(model_attr='created')
    rating = indexes.IntegerField(model_attr='rating')
    title = indexes.CharField(model_attr='title', boost=1.125)
    term = indexes.EdgeNgramField(model_attr='title')


    def get_model(self):
            return Article

我的文章_text.txt

{{ object.title }}
{{ object.created }}
{{ object.rating }}

我的 schema.xml

<field name="term" type="text_general" indexed="true" stored="true" />
<field name="weight" type="float" indexed="true" stored="true" />

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

<fieldType name="suggestType" class="solr.TextField" positionIncrementGap="100">
    <analyzer>
        <charFilter class="solr.PatternReplaceCharFilterFactory" pattern="[^a-zA-Z0-9]" replacement=" " />
        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
        <filter class="solr.LowerCaseFilterFactory"/>
    </analyzer>
</fieldType>

我的 solrconfig.xml

<requestHandler name="/suggest" class="solr.SearchHandler" startup="lazy" >
    <lst name="defaults">
        <str name="suggest">true</str>
        <str name="suggest.dictionary">infixSuggester</str>
        <str name="suggest.onlyMorePopular">true</str>
        <str name="suggest.count">10</str>
        <str name="suggest.collate">true</str>
    </lst>
    <arr name="components">
        <str>suggest</str>
    </arr>
</requestHandler>
<searchComponent name="suggest" class="solr.SuggestComponent">
    <lst name="suggester">
        <str name="name">infixSuggester</str>
        <str name="lookupImpl">AnalyzingInfixLookupFactory</str>
        <str name="indexPath">infix_suggestions</str>
        <str name="highlight">false</str>
        <str name="dictionaryImpl">DocumentDictionaryFactory</str>
        <str name="field">term</str>
        <str name="weightField">weight</str>
        <str name="suggestAnalyzerFieldType">suggestType</str>
        <str name="buildOnStartup">false</str>
        <str name="buildOnCommit">false</str>
    </lst>
</searchComponent> 

我使用 pysolr 来查询 Solr,因为 Haystack 还没有实现建议方法:

from pysolr import Solr

solr = Solr(settings.HAYSTACK_CONNECTIONS['default']['URL'], search_handler='/suggest', use_qt_param=False)
raw_results = solr.search('', **{'suggest.q': query_string})

【问题讨论】:

    标签: django solr autocomplete search-suggestion


    【解决方案1】:

    对于您的需要,我建议使用 BlendedInfixLookupFactory 设置如下:

    在 schema.xml 中,创建一个您将用于建议者的字段,然后复制到该字段中:

    <field name="title" type="text_general" indexed="true" stored="true" /> 
    <field name="term_suggest" type="phrase_suggest" indexed="true" stored="true" multiValued="true"/>
    
    <copyField source="title" dest="term_suggest"/>
    
    <fieldType name="phrase_suggest" class="solr.TextField" positionIncrementGap="100">
          <analyzer>
          <tokenizer class="solr.KeywordTokenizerFactory"/>
          <filter class="solr.LowerCaseFilterFactory"/>
        </analyzer>
    
    </fieldType>
      <fieldType name="text_suggest" class="solr.TextField" positionIncrementGap="100">
      <analyzer>
            <tokenizer class="solr.StandardTokenizerFactory"/>
            <filter class="solr.LowerCaseFilterFactory"/>
      </analyzer>
    </fieldType>
    

    然后在solrconfig.xml文件中:

    <searchComponent name="suggest" class="solr.SuggestComponent">
       <lst name="suggester">
          <str name="name">suggest</str>
          <str name="lookupImpl">BlendedInfixLookupFactory</str>
          <str name="blenderType">linear</str>
          <str name="dictionaryimpl">DocumentDictionaryFactory</str>
          <str name="field">term_suggest</str>
          <str name="weightField">weight</str>
          <str name="suggestAnalyzerFieldType">text_suggest</str>
          <str name="queryAnalyzerFieldType">phrase_suggest</str>
          <str name="indexPath">suggest</str>
          <str name="buildOnStartup">false</str>
          <str name="buildOnCommit">false</str>
          <bool name="exactMatchFirst">true</bool>
       </lst> 
    </searchComponent>
    
    <requestHandler name="/suggest" class="solr.SearchHandler" startup="lazy">
      <lst name="defaults">
          <str name="echoParams">explicit</str>
          <str name="wt">json</str>
          <str name="indent">false</str>
        <str name="suggest">true</str>
        <str name="suggest.count">10</str>
      </lst>
      <arr name="components">
        <str>suggest</str>
      </arr>
    </requestHandler>
    

    使用 BlendedInfixLookupFactory,您可以在字段中的任何位置找到“new y”,从而为开始出现的那些赋予更大的权重。将标准标记器用于SuggestAnalyzerFieldType 和关键字标记器用于queryAnalyzerFieldType 的组合将使您可以使用空格进行搜索(查询“new y”将被读取为字符串或关键字)。

    您发布的 confluence wiki 链接很好,最后一次修改是在 2016 年 9 月。

    编辑: 我没有意识到你不想要整个标题。您可以尝试为此使用 shingles,方法是将上述架构中的 phrase_suggest fieldType 更改为:

    <fieldType name="phrase_suggest" class="solr.TextField" positionIncrementGap="100">
        <analyzer>
            <tokenizer class="solr.StandardTokenizerFactory"/>
            <filter class="solr.LowerCaseFilterFactory"/>
            <filter class="solr.TrimFilterFactory"/>
            <filter class="solr.ShingleFilterFactory" 
                minShingleSize="2"
                maxShingleSize="4"
                outputUnigrams="true"
                outputUnigramsIfNoShingles="true"/>
        </analyzer>
    </fieldType>
    

    编辑2: 或者,您可以使用带有用于索引分析器的 shingle 过滤器和用于查询分析器的关键字标记器的标准标记器:

    <fieldType name="phrase_suggest" class="solr.TextField" positionIncrementGap="100">
            <analyzer type="index">
                <tokenizer class="solr.StandardTokenizerFactory"/>
            <filter class="solr.LowerCaseFilterFactory"/>
            <filter class="solr.TrimFilterFactory"/>
            <filter class="solr.ShingleFilterFactory" 
                minShingleSize="2"
                maxShingleSize="4"
                outputUnigrams="true"
                outputUnigramsIfNoShingles="true"/>
            </analyzer>
            <analyzer type="query">
                <tokenizer class="solr.KeywordTokenizerFactory"/>
                <filter class="solr.LowerCaseFilterFactory"/>
                <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
           </analyzer>
    </fieldType>
    

    那么对于建议搜索组件,你只需要:

    <str name="suggestAnalyzerFieldType">phrase_suggest</str>
    

    (并且没有 queryAnalyzerFieldType)。当然,您需要更改 ShingleFilterFactory 设置以满足您的需要。

    【讨论】:

    • 我有 2 个错误 - 第一个线性不存在我将 position_liner 用于 blenderType。然后我必须添加 suggest 以避免“没有配置名为默认的建议者”错误。重新启动 solr 然后 curl /suggest?suggest.build=true 在我尝试的每个查询中给出零结果。 { "responseHeader":{ "status":0, "QTime":0, "params":{ "suggest.q":"new y"}}, "suggest":{"suggest":{ "new y" :{ "numFound":0, "suggestions":[]}}}}
    • 我认为“线性”或“位置线性”取决于您使用的 solr 版本。基本问题 - 但是您是否在更改 schema.xml 文件后重新编制索引?如果您进行简单查询(q=*:*&fl=term_suggest),您是否在 term_suggest 字段中看到数据? (另外,顺便说一句,您可以将字段直接放入您的 Django 模型中,而不是使用 copyField。)
    • 我做了一个 update_index。但是 curl 127.0.0.1:8983/solr/collection1/select/?q=*:*&fl=term_suggest 03*:*term_suggest 如果我省略了 term_suggest 字段,我会得到结果,所以除了 term_suggest 之外的所有内容似乎都已编入索引。
    • 你可能需要rebuild_index。
    • 我做了一个rebuild_index 然后是一个 curl .../suggest?suggest.build=true 但是 .../select/?q=*:*&fl=term_sugges‌​t 仍然是空的
    【解决方案2】:

    经过几个小时的挣扎,我终于得到了一些东西。不完美但足够好。

    根据这篇文章: http://alexbenedetti.blogspot.fr/2015/07/solr-you-complete-me.html

    我使用了 FreeTextLookupFactory

    我的 search_indexes.py

    class ArticleIndex(indexes.SearchIndex, indexes.Indexable): 
    
        text = indexes.CharField(document=True, use_template=True)
        created = indexes.DateTimeField(model_attr='created')
        rating = indexes.IntegerField(model_attr='rating')
        title = indexes.CharField(model_attr='title', boost=1.125)
    
        def get_model(self):
                return Article
    

    我的 schema.xml

    <field name="django_ct" type="string" indexed="true" stored="true" multiValued="false"/>
    <field name="django_id" type="string" indexed="true" stored="true" multiValued="false"/>
    
    
    <field name="text" type="text_en" indexed="true" stored="true" multiValued="false"  termVectors="true" />
    <field name="rating" type="long" indexed="true" stored="true" multiValued="false"/>
    <field name="title" type="text_en" indexed="true" stored="true" multiValued="false"/>
    <field name="created" type="date" indexed="true" stored="true" multiValued="false"/>
    

    我的 Solrconfig.xml

    <searchComponent name="suggest" class="solr.SuggestComponent">
      <lst name="suggester">
        <str name="name">suggest</str>
        <str name="lookupImpl">FreeTextLookupFactory</str> 
        <str name="dictionaryImpl">DocumentDictionaryFactory</str>
        <str name="field">title</str>
        <str name="ngrams">3</str>
        <float name="threshold">0.004</float>
        <str name="highlight">false</str>
        <str name="buildOnCommit">false</str>
        <str name="separator"> </str>
        <str name="suggestFreeTextAnalyzerFieldType">text_general</str>
      </lst>
    </searchComponent>
    
    <requestHandler name="/suggest" class="solr.SearchHandler" startup="lazy" >
      <lst name="defaults">
        <str name="suggest.dictionary">suggest</str>
        <str name="suggest">true</str>
        <str name="suggest.count">10</str>
      </lst>
      <arr name="components">
        <str>suggest</str>
      </arr>
    </requestHandler>
    

    当我使用 Solr 6.4 时,默认情况下它处于托管模式模式(未考虑我在 schema.xml 中的更改),我必须通过添加 solrconfig.xml 来切换到手动编辑模式:

    <schemaFactory class="ClassicIndexSchemaFactory"/>
    

    请看这里:https://cwiki.apache.org/confluence/display/solr/Schema+Factory+Definition+in+SolrConfig#SchemaFactoryDefinitioninSolrConfig-Switchingfromschema.xmltoManagedSchema

    然后重启Solr,使用Haystack和rebuild_index重建索引

    当然还有用 curl 构建建议器: 卷曲http://127.0.0.1:8983/solr/collection1/suggest?suggest.build=true

    最后是结果:

    curl http://127.0.0.1:8983/solr/collection1/suggest?suggest.q=new%20y
    

    我将尝试更多地挖掘 FreeTextLookupFactory 以查看是否可以使其更准确,但它已经令人满意。 希望对您有所帮助。

    PS:请始终关注以下位置的日志: http://127.0.0.1:8983/solr/#/~logging 我强烈建议让它始终在选项卡上打开。它节省了我数小时的痛苦......

    【讨论】:

    • 这只会返回单个单词,而不是您最初想要的短语。您还可以将 FreeTextLookupFactory 与带状疱疹一起使用,如我的答案的第二次编辑中所使用的那样,它将返回短语,而不是单词。
    • 我尝试了您的更改,但仍然返回标题。
    • 嗯。您是否为 term_suggest 使用了 shingled phrase_suggest fieldType(并且它已被重新索引)?
    • 是的,我做了 solr restart 然后重建索引然后构建建议但仍然是标题,我也尝试了替代方案,它给出了一些结果,但不能说它是否更好。我将在这些带状疱疹参数中进行更多挖掘以进行比较。
    猜你喜欢
    • 2013-02-27
    • 2011-12-04
    • 1970-01-01
    • 2016-10-24
    • 1970-01-01
    • 1970-01-01
    • 2014-09-13
    • 2012-08-05
    • 2012-08-19
    相关资源
    最近更新 更多