Lucene多字标记与分隔符

时间:2014-10-13 21:21:35

标签: java lucene token analyzer

我刚刚开始使用Lucene,所以它可能是一个初学者的问题。我们正在尝试在数字图书上实现语义搜索,并且已经有了一个概念生成器,所以例如我为新文章生成的上下文可能是: |青豆|春洋葱|烹饪| 我正在使用Lucene仅使用提取的概念(为此目的存储在临时文档中)来创建书籍/文章的索引。现在标准分析仪正在创建单字标记:绿色,豆类,春天,洋葱,烹饪,当然不一样。

我的问题:是否有能够检测令牌周围分隔符的分析器(在我们的示例中为||),或者是能够检测多字构造的分析器?

我担心我们必须创建自己的分析仪,但我不知道从哪里开始。

2 个答案:

答案 0 :(得分:0)

创建分析仪非常简单。分析器只是一个标记器,可选地后跟标记过滤器。在您的情况下,您必须创建自己的标记生成器。幸运的是,您有一个方便的基类:CharTokenizer

您实施isTokenChar方法,并确保它在false字符上返回|,在任何其他字符上返回true。其他所有内容都将被视为令牌的一部分。

一旦你有了标记器,分析仪应该很简单,只需查看任何现有分析仪的源代码,并做同样的事情。

哦,如果你的|字符之间可以有空格,只需在分析仪上添加TrimFilter即可。

答案 1 :(得分:0)

我遇到了这个问题,因为我正在使用我的Lucene机制来创建与排序有关的数据结构,实际上是“劫持”Lucene类。否则我无法想象为什么人们会想要知识标记之间的分隔符(“分隔符”),但是因为它非常棘手,所以我认为我会把它放在这里为了任何可能需要的人的利益。

您必须重写自己的StandardTokenizerStandardTokenizerImpl版本。这些都是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}}子类中,方法TokenAndSeparatorExamineFilteraccept如下所示:

end