哪个Lucene SearchAnalyzer将用于特殊字符搜索
本文关键字:特殊字符 搜索 用于 Lucene SearchAnalyzer 哪个 | 更新日期: 2023-09-27 17:59:07
我在ASP.net项目搜索中使用Lucene.net Standard Analyzer。但该搜索不会返回C#、.NET等关键字的结果。但如果我键入C或NET(删除.和#),它就可以了。在Stackoverflow(它也使用Lucene)上,我注意到当我键入.NET时,它在搜索时将其更改为[.NET],我得到的链接说Standard Analyzer无法处理特殊字符搜索,而White Space Analyzer将不适用于我们,因为它不会给出预期的结果。有人能帮助SO管理搜索吗?
我将在这里更详细地描述SO正在做的事情:
虽然我并不真正了解StackOverflow的实现细节,但当搜索"java"或"hibernate"时,您会注意到相同的行为,即使这些行为与标准分析器没有问题。它们将被转换为"[java]"answers"[hibernate]"。这只是表示一个标签搜索。在搜索"lucene"或"junit"的地方不会发生这种情况,所以这可能与标签的流行有关。我肯定会怀疑标签标题会以未经分析的形式进行索引
举个有趣的例子,试试"j++"。这个死胡同的java实现在SO上使用j++标记只有8个问题,所以它不会触发自动标记搜索。搜索"[j++]",你会看到这8个。搜索"j++",你会很难找到与该特定语言相关的任何内容,但你会发现很多引用j.的内容
继续,解决您的问题:
是的,StandardAnalyzer
(说得不准确,具体规则请参阅UAX-29)会去掉你所有的标点符号。对此,典型的方法是在查询时使用相同的分析器。如果你使用StandardAnalyzer
来分析你的查询以及索引文档,你搜索到的术语会匹配,上面提到的两个查询术语会减少到net
和c
,你应该会得到结果。
但现在,您可能遇到了StandardAnalyzer
问题的经典示例。这意味着c
、c++
和c#
将在索引中完全相同,如果不匹配其他两个,就无法搜索其中一个!
在我看来,有几种方法可以解决这个问题:
-
把婴儿和洗澡水一起扔出去:使用
WhitespaceAnalyzer
或类似的东西,就会失去StandardAnalyzer
为帮助你而做的所有美好、花哨的事情。 -
只需处理这些小的边缘案例:好吧,Lucene不喜欢标点符号,而且你有一些已知的术语对此有问题。幸运的是,您有
String.Replace
。将它们替换为更友好的lucene,如"c"、"cplusplus"answers"csharp"。同样,请确保在查询和索引时都能完成问题是:由于您在分析器之外执行此操作,转换也会影响字段的存储版本,迫使您在向用户显示结果之前将其反转。 -
做与#2相同的事情,但有点花哨:所以#2可能可以正常工作,但您已经有了这些分析器来处理转换数据以供lucene使用,这只会影响字段的索引版本,而不会影响存储版本。为什么不使用它们?Analyzer有一个调用
initReader
,在该调用中,您可以在分析器堆栈的前面加上一个CharFilter
(请参阅Analysis包文档底部的示例)。通过分析器运行的文本将在StandardTokenizer
(除其他外,它去掉了标点符号)得到它的手之前由CharFilter
进行转换。例如,MappingCharFilter
。
不过,您不能对StandardAnalyzer
进行子类化,因为您的想法是应该实现Analyzer,而不是对其实现进行子类划分(如果您有兴趣在这里对思想过程进行更完整的讨论,请参阅此处的讨论)。因此,假设我们想确保在交易中获得StandardAnalyzer
的所有功能,只需复制粘贴源代码,并添加initReaders
方法的覆盖:
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
方法的覆盖中用它包裹读取器。