我在ASP.NET项目搜索中使用Lucene.net标准分析器。但是该搜索不会返回C#,.NET等关键字的结果。但如果我键入C或NET(删除。和#),它就可以工作。在Stackoverflow(也使用Lucene)上,我注意到当我输入.NET时它会在搜索时将其更改为[.NET],我得到的链接表示标准分析器无法处理特殊字符搜索,而且White Space Analyzer不会为我们工作,因为它没有给出预期的结果。任何人都可以帮助SO管理搜索吗?
答案 0 :(得分:2)
我将在这里更详细地描述SO的作用:
虽然我并不真正了解StackOverflow的实现细节,但在搜索" java"时,您会注意到相同的行为。或者" hibernate",即使它们与标准分析仪没有任何问题。它们将被转化为" [java]"和" [休眠]"。这只是表示标签搜索。在寻找" lucene"的地方不会发生这种情况。或" junit"所以它可能与标签的流行有关。我肯定会怀疑标签标题会以未经分析的形式编入索引。
有趣的例子,请试试" j ++"。这个死胡同的java实现只有8个问题使用SO上的j++标记,因此它不会触发自动标记搜索。搜索" [j ++]",然后你会看到那些8.搜索" j ++",你会发现任何与那种特殊的语言,但你会发现很多引用j。
继续,解决问题:
是的,StandardAnalyzer
将(不准确地说,请参阅UAX-29了解准确的规则)摆脱所有标点符号。典型的方法是在查询时使用相同的分析器。如果您使用StandardAnalyzer
分析您的查询以及索引文档,则您搜索的字词将匹配,上述两个查询字词将缩减为net
和c
,您应该得到结果。
但是现在,您可能会使用StandardAnalyzer
来解决问题的 经典示例。这意味着c
,c++
和c#
将在索引中完全相同地表示,没有办法搜索其中一个而不匹配其他两个!
在我看来,有几种方法可以解决这个问题:
用洗澡水将宝宝扔出去:使用WhitespaceAnalyzer
或其他类似的东西,并失去所有漂亮,花哨的东西StandardAnalyzer
来帮助你。
只需处理那几个小边缘情况:好的,所以Lucene不喜欢标点符号,而且你有一些已知的术语有问题。幸运的是,你有String.Replace
。用一些更友好的东西替换它们,比如" c"," cplusplus"和" csharp"。同样,确保它在查询和索引时都完成。 问题是:由于您在分析仪之外进行此操作,因此转换也会影响字段的存储版本,在您向用户显示结果之前强制您将其反转。
和#2一样,但只是有点发烧友:所以#2可能会正常工作,但是你已经让这些分析器处理转换数据以供消费lucene,它只影响字段的索引版本,而不是存储的字段。为什么不使用它们? Analyzer有一个调用initReader
,你可以在分析器堆栈的前面拍一个CharFilter
(参见the Analysis package documentation底部的示例向下)。通过分析器运行的文本将在CharFilter
之前被StandardTokenizer
转换(除了其他东西之外,它可以摆脱标点符号)。例如MappingCharFilter
。
你不能继承StandardAnalyzer
,但是,你应该实现Analyzer,而不是继承它的实现(参见the discussion here,如果你对它感兴趣的话)对那里的思想过程进行更全面的讨论。因此,假设我们想要确保在交易中完全所有 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
方法时用它包装它