使用 solr,我尝试使用 hl.formatter 选项和 hl.simple.pre/post 突出显示一些文本。

我的问题是 hl.simple.pre/post 代码有时没有出现在突出显示的结果中,我不明白为什么。

例如,我称这个 URL 为:

http://localhost:8080/solr/Employees/select?q=lastName:anthan&fl=lastName&wt=json&indent=true&hl=true&hl.fl=lastName&hl.simple.pre=<em>&hl.simple.post=</em>

我得到:
 ..."highlighting": {
    "NB0094418": {
      "lastName": [
        "Yogan<em>anthan</em>" => OK
      ]
    },
    "NB0104046": {
      "lastName": [
        "Vijayakanthan" => KO, I want Vijayak<em>anthan</em>
      ]
    },
    "NB0144981": {
      "lastName": [
        "Parmananthan" => KO, I want Parman<em>anthan</em>
      ]
    },...

有人知道为什么我有这种行为吗?

我的配置:

架构.xml
<fieldType name="nameType" class="solr.TextField">
    <analyzer type="index">
        <tokenizer class="solr.NGramTokenizerFactory" minGramSize="2" maxGramSize="50" />
        <filter class="solr.LowerCaseFilterFactory" />
        <filter class="solr.ASCIIFoldingFilterFactory" />
        <filter class="solr.TrimFilterFactory" />
        <filter class="solr.PatternReplaceFilterFactory" pattern="([^a-z])" replacement="" replace="all" />
    </analyzer>

    <analyzer type="query">
        <tokenizer class="solr.KeywordTokenizerFactory" />
        <filter class="solr.LowerCaseFilterFactory" />
        <filter class="solr.ASCIIFoldingFilterFactory" />
        <filter class="solr.TrimFilterFactory" />
        <filter class="solr.PatternReplaceFilterFactory" pattern="([^a-z])" replacement="" replace="all" />
    </analyzer>
</fieldType>

...
<fields>
    <field name="lastName" type="nameType" indexed="true" stored="true" required="true" />
</fields>

配置文件
<requestHandler name="standard" class="solr.SearchHandler" default="true">
    <lst name="defaults">
        <str name="echoParams">explicit</str>
    </lst>
</requestHandler>

...

<searchComponent class="solr.HighlightComponent" name="highlight">
    <highlighting>
        <fragmenter name="gap" default="true" class="solr.highlight.GapFragmenter">
            <lst name="defaults">
                <int name="hl.fragsize">100</int>
            </lst>
        </fragmenter>

        <fragmenter name="regex" class="solr.highlight.RegexFragmenter">
            <lst name="defaults">
                <int name="hl.fragsize">70</int>
                <float name="hl.regex.slop">0.5</float>
                <str name="hl.regex.pattern">[-\w ,/\n\&quot;&apos;]{20,200}</str>
            </lst>
        </fragmenter>

        <formatter name="html" default="true" class="solr.highlight.HtmlFormatter">
            <lst name="defaults">
                <str name="hl.simple.pre"><![CDATA[<em>]]></str>
                <str name="hl.simple.post"><![CDATA[</em>]]></str>
            </lst>
        </formatter>

        <encoder name="html" default="true" class="solr.highlight.HtmlEncoder" />

        <fragListBuilder name="simple" default="true" class="solr.highlight.SimpleFragListBuilder" />
        <fragListBuilder name="single" class="solr.highlight.SingleFragListBuilder" />
        <fragmentsBuilder name="default" default="true" class="solr.highlight.ScoreOrderFragmentsBuilder">
        </fragmentsBuilder>

        <fragmentsBuilder name="colored" class="solr.highlight.ScoreOrderFragmentsBuilder">
            <lst name="defaults">
                <str name="hl.tag.pre"><![CDATA[
                <b style="background:yellow">,<b style="background:lawgreen">,
                <b style="background:aquamarine">,<b style="background:magenta">,
                <b style="background:palegreen">,<b style="background:coral">,
                <b style="background:wheat">,<b style="background:khaki">,
                <b style="background:lime">,<b style="background:deepskyblue">]]></str>
                <str name="hl.tag.post"><![CDATA[</b>]]></str>
            </lst>
        </fragmentsBuilder>
    </highlighting>
</searchComponent>

最佳答案

直到昨天我都在处理一个非常相似的问题。我反复尝试了许多不同的解决方案,所以我最终得到的一些细节可能没有必要。但我会描述我最终的工作。简短的回答,我认为荧光笔无法在更长的字段上找到它需要的术语位置信息。

首先,我看到的症状:有时会出现搜索词高亮,有时整个字段会出现在高亮部分,但没有高亮信息。该模式最终基于字段的长度和搜索词的长度。我发现字段越长(实际上是 ngrammed 的标记),可以成功突出显示的搜索词越短。不过,这不是一对一。我发现对于包含 11 个或更少字符的字段,突出显示在所有情况下都可以正常工作。如果该字段有 12 个字符,则不会突出显示超过 9 个字符的 ngram。对于包含 15 个字符的字段,不会突出显示超过 7 个字符的 ngram。对于超过 18 个字符的字段,不会突出显示超过 6 个字符的 ngram。对于长度超过 21 个字符的字段,不会突出显示超过 5 个字符的 ngram,超过 24 个字符的字段不会突出显示超过 4 个字符。 (看起来,从上面的示例中,您看到的具体尺寸并不完全相同,但我确实注意到文档中突出显示不起作用的名称比突出显示的名称长。 )

所以,这就是最终的工作:

  • 我从使用 WhitespaceTokenizerNGramFilterFactory 切换到使用 NGramTokenizerFactory。 (您已经在使用它,稍后我会遇到更多关于这给我带来的困难。)但是,这还不足以解决问题,因为术语位置仍然没有被存储。
  • 我开始使用 FastVectorHighlighter 。这迫使我的模式字段的索引方式发生了一些变化(包括存储术语向量、位置和偏移量),而且我还必须将我的前指示符和后指示符标记配置从 hl.simple.pre 更改为 hl.tag.pre(对于 *post 也是如此) .

  • 一旦我进行了这些更改,突出显示就开始一致地工作。但是,这具有副作用,即删除了我从 WhitespaceTokenizer 中获得的行为。如果我有一个包含短语“这是一个测试”的字段,我最终会得到包含“s is a”、“a tes”等的 ngrams,我真的只想要单个单词的 ngrams,而不是整个短语。 NGramTokenizer JavaDocs 中有一条说明,您可以覆盖 NGramTokenizer.isTokenChar() 以提供预标记化,但我在网上找不到这样的示例。我将在下面包括一个。

    最终结果:

    WhitespaceSplittingNGramTokenizer.java:
    package info.jwismar.solr.plugin;
    
    import java.io.Reader;
    
    import org.apache.lucene.analysis.ngram.NGramTokenizer;
    import org.apache.lucene.util.Version;
    
    public class WhitespaceSplittingNGramTokenizer extends NGramTokenizer {
    
        public WhitespaceSplittingNGramTokenizer(Version version, Reader input, int minGram, int maxGram) {
            super(version, input, minGram, maxGram);
        }
    
        public WhitespaceSplittingNGramTokenizer(Version version, AttributeFactory factory, Reader input, int minGram,
                int maxGram) {
            super(version, factory, input, minGram, maxGram);
        }
    
        public WhitespaceSplittingNGramTokenizer(Version version, Reader input) {
            super(version, input);
        }
    
        @Override
        protected boolean isTokenChar(int chr) {
            return !Character.isWhitespace(chr);
        }
    }
    

    WhitespaceSplittingNGramTokenizerFactory.java:
    package info.jwismar.solr.plugin;
    
    import java.io.Reader;
    import java.util.Map;
    
    import org.apache.lucene.analysis.Tokenizer;
    import org.apache.lucene.analysis.ngram.NGramTokenizer;
    import org.apache.lucene.analysis.util.TokenizerFactory;
    import org.apache.lucene.util.AttributeSource.AttributeFactory;
    
    public class WhitespaceSplittingNGramTokenizerFactory extends TokenizerFactory {
    
        private final int maxGramSize;
        private final int minGramSize;
    
        /** Creates a new WhitespaceSplittingNGramTokenizer */
        public WhitespaceSplittingNGramTokenizerFactory(Map<String, String> args) {
            super(args);
            minGramSize = getInt(args, "minGramSize", NGramTokenizer.DEFAULT_MIN_NGRAM_SIZE);
            maxGramSize = getInt(args, "maxGramSize", NGramTokenizer.DEFAULT_MAX_NGRAM_SIZE);
            if (!args.isEmpty()) {
                throw new IllegalArgumentException("Unknown parameters: " + args);
            }
        }
    
        @Override
        public Tokenizer create(AttributeFactory factory, Reader reader) {
            return new WhitespaceSplittingNGramTokenizer(luceneMatchVersion, factory, reader, minGramSize, maxGramSize);
        }
    }
    

    这些需要打包成 .jar 并安装在 SOLR 可以找到它的地方。一种选择是在 solrconfig.xml 中添加一个 lib 指令来告诉 SOLR 在哪里查找。 (我调用我的 solr-ngram-plugin.jar 并将其安装在 /opt/solr-ngram-plugin/ 中。)

    在 solrconfig.xml 中:
    <lib path="/opt/solr-ngram-plugin/solr-ngram-plugin.jar" />
    

    schema.xml(字段类型定义):
    <fieldType name="any_token_ngram" class="solr.TextField">
        <analyzer type="index">
            <tokenizer class="info.jwismar.solr.plugin.WhitespaceSplittingNGramTokenizerFactory" maxGramSize="30" minGramSize="2"/>
            <filter class="solr.LowerCaseFilterFactory" />
        </analyzer>
        <analyzer type="query">
            <tokenizer class="solr.StandardTokenizerFactory" />
            <filter class="solr.LowerCaseFilterFactory" />
            <filter class="solr.PatternReplaceFilterFactory"
                pattern="^(.{30})(.*)?" replacement="$1" replace="all" />
        </analyzer>
    </fieldType>
    

    schema.xml(字段定义):
    <fields>
        <field name="property_address_full" type="string" indexed="false" stored="true" />
        <field name="property_address_full_any_ngram" type="any_token_ngram" indexed="true"
            stored="true" omitNorms="true" termVectors="true" termPositions="true"
            termOffsets="true"/>
    </fields>
    <copyField source="property_address_full" dest="property_address_full_any_ngram" />
    

    solrconfig.xml(请求处理程序(如果您愿意,您可以在普通的选择 URL 中传递这些参数):
    <!-- request handler to return typeahead suggestions -->
    <requestHandler name="/suggest" class="solr.SearchHandler">
        <lst name="defaults">
            <str name="echoParams">explicit</str>
            <str name="defType">edismax</str>
            <str name="rows">10</str>
            <str name="mm">2</str>
            <str name="fl">*,score</str>
            <str name="qf">
                property_address_full^100.0
                property_address_full_any_ngram^10.0
            </str>
            <str name="sort">score desc</str>
            <str name="hl">true</str>
            <str name="hl.fl">property_address_full_any_ngram</str>
            <str name="hl.tag.pre">|-&gt;</str>
            <str name="hl.tag.post">&lt;-|</str>
            <str name="hl.fragsize">1000</str>
            <str name="hl.mergeContinuous">true</str>
            <str name="hl.useFastVectorHighlighter">true</str>
        </lst>
    </requestHandler>
    

    10-08 19:45