【问题标题】:Which Lucene SearchAnalyzer to be used for special character search哪个 Lucene SearchAnalyzer 用于特殊字符搜索
【发布时间】:2014-04-19 21:07:23
【问题描述】:

我在我的 ASP.NET 项目搜索中使用 Lucene.net 标准分析器。但是该搜索不会返回 C#、.NET 等关键字的结果。但如果我键入 C 或 NET(删除 . 和 #),它就可以工作。在 Stackoverflow(也使用 Lucene)上,我注意到当我输入 .NET 时,它会在搜索时将其更改为 [.NET],我得到的链接显示标准分析器无法处理特殊字符搜索,而空白分析器不会为我们工作,因为它没有给出预期的结果。谁能帮助了解 SO 如何管理搜索?

【问题讨论】:

    标签: c# asp.net lucene.net lucene


    【解决方案1】:

    我将在这里更详细地描述 SO 所做的事情:

    虽然我并不真正了解 StackOverflow 的实现细节,但您在搜索“java”或“hibernate”时会注意到相同的行为,即使这些与标准分析器没有问题。它们将被转换为“[java]”和“[hibernate]”。这只是表示标签搜索。在搜索“lucene”或“junit”时不会发生这种情况,因此它可能与标签的受欢迎程度有关。我肯定会怀疑标签标题会以未经分析的形式编入索引。

    举个有趣的例子,试试“j++”。这个死胡同的 java 实现只有 8 个问题,使用 SO 上的 标签,所以它不会触发自动标签搜索。搜索“[j++]”,您会看到那些 8。搜索“j++”,您将很难找到与该特定语言相关的任何内容,但您会找到很多引用

    接下来,解决您的问题:

    是的,StandardAnalyzer 将(说得不准确,请参阅UAX-29 了解确切规则)摆脱所有标点符号。典型的方法是在查询时使用相同的分析器。如果您使用StandardAnalyzer 分析您的查询以及索引文档,您的搜索词将匹配,上面提到的两个查询词将减少为netc,您应该会得到结果。

    但是现在,您可能遇到了StandardAnalyzer 问题的经典示例。这意味着cc++c# 在索引中都将完全相同地表示,如果不匹配其他两个,就无法搜索一个!

    在我看来,有几种方法可以解决这个问题:

    1. 把婴儿和洗澡水一起扔出去:使用WhitespaceAnalyzer 或类似的东西,而失去StandardAnalyzer 为你提供帮助的所有美好、花哨的东西。

    2. 只处理那些小的边缘情况:好的,所以 Lucene 不喜欢标点符号,而且你有一些已知的术语对此有问题。幸运的是,你有String.Replace。将它们替换为对 lucene 更友好的东西,例如“c”、“cplusplus”和“csharp”。同样,确保它在查询和索引时间都完成。 问题是:由于您是在分析器之外执行此操作,因此转换也会影响字段的存储版本,迫使您在向用户显示结果之前反转它。

    3. 与 #2 一样,但更高级:所以 #2 可能工作正常,但您已经让这些分析器处理转换数据以供 lucene 使用,这只会影响字段的索引版本,而不是存储的版本。为什么不使用它们? Analyzer 有一个调用,initReader,您可以在其中将CharFilter 放在分析器堆栈的前面(参见the Analysis package documentation 底部的示例)。通过分析器运行的文本将由CharFilter 转换,然后StandardTokenizer(除去标点符号等)得到它的处理。例如MappingCharFilter

    但是,您不能子类化 StandardAnalyzer,您的想法是您应该实现 Analyzer,而不是子类化它的实现(请参阅 the discussion here,如果您有兴趣更完整地讨论思考过程那里)。因此,假设我们想确保我们在交易中获得绝对所有StandardAnalyzer 的功能,只需复制粘贴源代码,并添加对initReaders 方法的覆盖: p>

    public class ExtraFancyStandardAnalyzer extends StopwordAnalyzerBase {
    
        public static final int DEFAULT_MAX_TOKEN_LENGTH = 255;
    
        private int maxTokenLength = DEFAULT_MAX_TOKEN_LENGTH;
    
        public static final CharArraySet STOP_WORDS_SET = StopAnalyzer.ENGLISH_STOP_WORDS_SET;
    
        public ExtraFancyStandardAnalyzer(Version matchVersion,
                CharArraySet stopWords) {
            super(matchVersion, stopWords);
            buildMap();
        }
    
        public ExtraFancyStandardAnalyzer(Version matchVersion) {
            this(matchVersion, STOP_WORDS_SET);
        }
    
        public ExtraFancyStandardAnalyzer(Version matchVersion, Reader stopwords)
                throws IOException {
            this(matchVersion, loadStopwordSet(stopwords, matchVersion));
        }
    
        public void setMaxTokenLength(int length) {
            maxTokenLength = length;
        }
    
        public int getMaxTokenLength() {
            return maxTokenLength;
        }
    
    
        // The following two methods, and a call to buildMap() in the ctor
        // are the only things changed from StandardAnalyzer
    
        private NormalizeCharMap map;
    
        public void buildMap() {
            NormalizeCharMap.Builder builder = new NormalizeCharMap.Builder();
            builder.add("c++", "cplusplus");
            builder.add("c#", "csharp");
            map = builder.build();
        }
    
        @Override
        protected Reader initReader(String fieldName, Reader reader) {
            return new MappingCharFilter(map, reader);
        }
    
        @Override
        protected TokenStreamComponents createComponents(final String fieldName,
                final Reader reader) {
            final StandardTokenizer src = new StandardTokenizer(matchVersion,
                    reader);
            src.setMaxTokenLength(maxTokenLength);
            TokenStream tok = new StandardFilter(matchVersion, src);
            tok = new LowerCaseFilter(matchVersion, tok);
            tok = new StopFilter(matchVersion, tok, stopwords);
            return new TokenStreamComponents(src, tok) {
                @Override
                protected void setReader(final Reader reader) throws IOException {
                    src.setMaxTokenLength(ExtraFancyStandardAnalyzer.this.maxTokenLength);
                    super.setReader(reader);
                }
            };
        }
    }
    

    注意:这是用 Java Lucene 4.7 版编写和测试的。 C# 实现不应该有太大的不同。复制StandardAnalyzer,构建一个MappingCharFilter(这实际上只是在3.0.3 版本中更简单的处理方式),并将读者用它包裹在initReader 方法的覆盖中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-24
      • 1970-01-01
      • 2011-02-13
      • 1970-01-01
      • 2011-05-03
      • 1970-01-01
      相关资源
      最近更新 更多