我刚刚开始使用Lucene,所以它可能是一个初学者的问题。我们正在尝试在数字图书上实现语义搜索,并且已经有了一个概念生成器,所以例如我为新文章生成的上下文可能是: |青豆|春洋葱|烹饪| 我正在使用Lucene仅使用提取的概念(为此目的存储在临时文档中)来创建书籍/文章的索引。现在标准分析仪正在创建单字标记:绿色,豆类,春天,洋葱,烹饪,当然不一样。
我的问题:是否有能够检测令牌周围分隔符的分析器(在我们的示例中为||),或者是能够检测多字构造的分析器?
我担心我们必须创建自己的分析仪,但我不知道从哪里开始。
答案 0 :(得分:0)
创建分析仪非常简单。分析器只是一个标记器,可选地后跟标记过滤器。在您的情况下,您必须创建自己的标记生成器。幸运的是,您有一个方便的基类:CharTokenizer
。
您实施isTokenChar
方法,并确保它在false
字符上返回|
,在任何其他字符上返回true
。其他所有内容都将被视为令牌的一部分。
一旦你有了标记器,分析仪应该很简单,只需查看任何现有分析仪的源代码,并做同样的事情。
哦,如果你的|
字符之间可以有空格,只需在分析仪上添加TrimFilter
即可。
答案 1 :(得分:0)
我遇到了这个问题,因为我正在使用我的Lucene机制来创建与排序有关的数据结构,实际上是“劫持”Lucene类。否则我无法想象为什么人们会想要知识标记之间的分隔符(“分隔符”),但是因为它非常棘手,所以我认为我会把它放在这里为了任何可能需要的人的利益。
您必须重写自己的StandardTokenizer
和StandardTokenizerImpl
版本。这些都是final
类,因此您无法扩展它们。
SeparatorDeliveringTokeniserImpl (从StandardTokenizerImpl
的来源调整):
3个新领域:
private int startSepPos = 0;
private int endSepPos = 0;
private String originalBufferAsString;
调整这些方法:
public final void getText(CharTermAttribute t) {
t.copyBuffer(zzBuffer, zzStartRead, zzMarkedPos - zzStartRead);
if( originalBufferAsString == null ){
originalBufferAsString = new String( zzBuffer, 0, zzBuffer.length );
}
// startSepPos == -1 is a "flag condition": it means that this token is the last one and it won't be followed by a sep
if( startSepPos != -1 ){
// if the flag is NOT set, record the start pos of the next sep...
startSepPos = zzMarkedPos;
}
}
public final void yyreset(java.io.Reader reader) {
zzReader = reader;
zzAtBOL = true;
zzAtEOF = false;
zzEOFDone = false;
zzEndRead = zzStartRead = 0;
zzCurrentPos = zzMarkedPos = 0;
zzFinalHighSurrogate = 0;
yyline = yychar = yycolumn = 0;
zzLexicalState = YYINITIAL;
if (zzBuffer.length > ZZ_BUFFERSIZE)
zzBuffer = new char[ZZ_BUFFERSIZE];
// reset fields responsible for delivering separator...
originalBufferAsString = null;
startSepPos = 0;
endSepPos = 0;
}
(在getNextToken
内):)
if ((zzAttributes & 1) == 1) {
zzAction = zzState;
zzMarkedPosL = zzCurrentPosL;
if ((zzAttributes & 8) == 8) {
// every occurrence of a separator char leads here...
endSepPos = zzCurrentPosL;
break zzForAction;
}
}
制作新方法:
String getPrecedingSeparator() {
String sep = null;
if( originalBufferAsString == null ){
sep = new String( zzBuffer, 0, endSepPos );
}
else if( startSepPos == -1 || endSepPos <= startSepPos ){
sep = "";
}
else {
sep = originalBufferAsString.substring( startSepPos, endSepPos );
}
if( zzMarkedPos < startSepPos ){
// ... then this is a sign that the next token will be the last one... and will NOT have a trailing separator
// so set a "flag condition" for next time this method is called
startSepPos = -1;
}
return sep;
}
SeparatorDeliveringTokeniser (从StandardTokenizer
的来源调整):
添加:
private String separator;
String getSeparator(){
// normally this delivers a preceding separator... but after incrementToken returns false, if there is a trailing
// separator, it then delivers that...
return separator;
}
(在incrementToken
:)里面
while(true) {
int tokenType = scanner.getNextToken();
// added NB this gives you the separator which PRECEDES the token
// which you are about to get from scanner.getText( ... )
separator = scanner.getPrecedingSeparator();
if (tokenType == SeparatorDeliveringTokeniserImpl.YYEOF) {
// NB at this point sep is equal to the trailing separator...
return false;
}
...
<强>用法强>:
在名为FilteringTokenFilter
的{{1}}子类中,方法TokenAndSeparatorExamineFilter
和accept
如下所示:
end