简单的同义词(wordA = wordB)就可以了。当同义词是一个短语(wordA = wordB**word C)时,则匹配是命中或不命中的。
我有一个简单的测试用例(它是作为一个Ant项目交付的)来说明这个问题。这个测试用例使用的文件和我今天发布的另一个问题相同,但是我在这里给予了相同的描述。
材料
您可以在此处下载测试用例:mydemo.with.libs.zip (5MB)
该归档文件包括我的测试使用的Lucene9.2库;如果您喜欢不包含JAR文件的副本,可以从以下位置下载:mydemo.zip (9KB)
您可以通过将归档文件解压缩到一个空目录并运行Ant命令**ant stsearch
**来运行测试用例
输入
为文档编制索引时,将使用以下同义词列表(根据需要进行排列):
note,notes,notice,notification
subtree,sub tree,sub-tree
我有三份文件,每一份都只有一句话,这三句话是:
These release notes describe a document sub tree in a simple way.
This release note describes a document subtree in a simple way.
This release notice describes a document sub-tree in a simple way.
问题
我认为以下任何搜索都应与所有三个文档匹配:
subtree
sub tree
sub-tree
"document subtree"
"document sub tree"
"document sub-tree"
虽然对subtree
和sub-tree
的搜索正确匹配,但对sub tree
的搜索仅匹配单个文档(字面上包含sub tree
作为两个单词的文档)。
短语搜索不正确:“文档子树”和“文档子树”各匹配一个,而“文档子树”匹配两个。
如果我在短语搜索中添加一个邻近修饰符,如下所示:
"document subtree"~1
"document sub tree"~1
"document sub-tree"~1
第一个和第三个现在匹配所有三个记录,但是“document subtree”~1仍然只匹配一个文档。
把一个两个单词的短语作为一个单词的同义词来配对是行不通的。
下面是我的分析器,包括同义词Map构建器:
public class MyAnalyzer extends Analyzer {
public MyAnalyzer(String synlist) {
this.synlist = synlist;
}
@Override
protected TokenStreamComponents createComponents(String fieldName) {
WhitespaceTokenizer src = new WhitespaceTokenizer();
TokenStream result = new LowerCaseFilter(src);
if (synlist != null) {
result = new SynonymGraphFilter(result, getSynonyms(synlist), Boolean.TRUE);
result = new FlattenGraphFilter(result);
}
return new TokenStreamComponents(src, result);
}
private static SynonymMap getSynonyms(String synlist) {
boolean dedup = Boolean.TRUE;
SynonymMap synMap = null;
SynonymMap.Builder builder = new SynonymMap.Builder(dedup);
int cnt = 0;
try {
BufferedReader br = new BufferedReader(new FileReader(synlist));
String line;
try {
while ((line = br.readLine()) != null) {
processLine(builder,line);
cnt++;
}
} catch (IOException e) {
System.err.println(" caught " + e.getClass() + " while reading synonym list,\n with message " + e.getMessage());
}
System.out.println("Synonym load processed " + cnt + " lines");
br.close();
} catch (Exception e) {
System.err.println(" caught " + e.getClass() + " while loading synonym map,\n with message " + e.getMessage());
}
if (cnt > 0) {
try {
synMap = builder.build();
} catch (IOException e) {
System.err.println(e);
}
}
return synMap;
}
private static void processLine(SynonymMap.Builder builder, String line) {
boolean keepOrig = Boolean.TRUE;
String terms[] = line.split(",");
if (terms.length < 2) {
System.err.println("Synonym input must have at least two terms on a line: " + line);
} else {
String word = terms[0];
String[] synonymsOfWord = Arrays.copyOfRange(terms, 1, terms.length);
addSyns(builder, word, synonymsOfWord, keepOrig);
}
}
private static void addSyns(SynonymMap.Builder builder, String word, String[] syns, boolean keepOrig) {
CharsRefBuilder synset = new CharsRefBuilder();
SynonymMap.Builder.join(syns, synset);
CharsRef wordp = SynonymMap.Builder.join(word.split("\\s+"), new CharsRefBuilder());
builder.add(wordp, synset.get(), keepOrig);
}
private String synlist;
}
我怀疑我必须对synonymsOfWord数组做一些额外的操作,但是我所做的一切都没有效果。
请注意,分析器会在建立索引时包含同义字,而不是在执行查询时。
1条答案
按热度按时间fjnneemd1#
我不知道这是否是最好的解决办法,但这是一个解决办法。
它基本上是一种与answer to this related question非常相似的方法,但是在处理同义词方面进行了增强,其中一些同义词包含多个单词:
在这种情况下,同义词构建器需要使用SynonymMap.html #WORD_SEPARATOR:
它只是一个包含空终止符
\u0000
的char
。因此,你可以做一些快速和肮脏的如下:
现在,
luceneSyns
成为我们使用的数组:这很管用。
问题中列出的所有查询都将找到所有三个文档。
上面的方法并不漂亮--它假设您只需要将空格和破折号作为两个字符来处理,这两个字符需要用空终止符来替换。
另一种更健壮的方法可能是使用
SynonymMap.Parser
,它有一个parse()
方法,用于将提供的同义词文本转换为短语同义词所需的文本。这是一个抽象类,我不知道如何正确地实现
analyze()
方法--但这是我所得到的结果:首先,我创建了
MySynonymParser
类:如前所述,所需的
analyze()
方法缺少其实现。CharsRef
的形式返回新字符串。但假设它被正确实现,那么我假设它将被如下使用:
在上面的代码中,我们必须创建一个分析器传递给
MySynonymParser
,这个分析器和we actually use for indexing一样,但是没有同义词过滤器。然后我们分析每个
word
和synonym
,它们用空终止符替换所有空格。