gpt4 book ai didi

java - 使用 Lucene TokenFilter 将 token 分解为子 token

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:11:27 25 4
gpt4 key购买 nike

我的程序需要使用 Lucene (4.10) 索引非结构化文档,其内容可以是任何内容。所以我的自定义分析器正在使用 ClassicTokenizer 来首先标记文档。

但它并不完全符合我的需求,因为例如我希望能够搜索电子邮件地址的一部分或序列号的一部分(也可以是电话号码或任何包含数字的内容),它们可以写成1234.5678.9012 或 1234-5678-9012,具体取决于被编入索引的文档的作者。

由于此 ClassicTokenizer 识别电子邮件并将点后跟数字视为一个整体标记,因此最终生成的索引包括作为一个整体的电子邮件地址和作为一个整体的序列号,而我还想将这些标记分解成多个部分使用户以后可以搜索这些片段。

让我举一个具体的例子:如果输入文档包含 xyz@gmail.com,ClassicTokenizer 会将其识别为电子邮件,然后将其标记为 xyz@gmail.com。如果用户搜索 xyz,他们将一无所获,而搜索 xyz@gmail.com 将产生预期的结果。

在阅读了大量博客文章或提出问题后,我得出的结论是,一种解决方案可能是使用 TokenFilter 将电子邮件拆分成多个部分(在 @ 符号的每一侧)。请注意,我不想使用 JFlex and co 创建我自己的分词器。

处理电子邮件我写了以下代码,灵感来自 Lucene in action 2nd Edition 的 Synonymfilter :

public class SymbolSplitterFilter extends TokenFilter {

private final CharTermAttribute termAtt;
private final PositionIncrementAttribute posIncAtt;
private final Stack<String> termStack;
private AttributeSource.State current;

public SymbolSplitterFilter(TokenStream in) {
super(in);
termStack = new Stack<>();
termAtt = addAttribute(CharTermAttribute.class);
posIncAtt = addAttribute(PositionIncrementAttribute.class);
}

@Override
public boolean incrementToken() throws IOException {
if (!input.incrementToken()) {
return false;
}

final String currentTerm = termAtt.toString();

System.err.println("The original word was " + termAtt.toString());
final int bufferLength = termAtt.length();

if (bufferLength > 1 && currentTerm.indexOf("@") > 0) { // There must be sth more than just @
// If this is the first pass we fill in the stack with the terms
if (termStack.isEmpty()) {
// We split the token abc@cd.com into abc and cd.com
termStack.addAll(Arrays.asList(currentTerm.split("@")));
// Now we have the constituting terms of the email in the stack
System.err.println("The terms on the stacks are ");
for (int i = 0; i < termStack.size(); i++) {
System.err.println(termStack.get(i));
/** The terms on the stacks are
* xyz
* gmail.com
*/

}

// I am not sure it is the right place for this.
current = captureState();

} else {
// This part seems to never be reached!
// We add the constituents terms as tokens.
String part = termStack.pop();
System.err.println("Current part is " + part);
restoreState(current);
termAtt.setEmpty().append(part);
posIncAtt.setPositionIncrement(0);
}
}

System.err.println("In the end we have " + termAtt.toString());
// In the end we have xyz@gmail.com
return true;

}

请注意:我刚开始使用电子邮件,这就是为什么我只显示了那部分代码,但我必须增强我的代码以管理序列号(如前所述)

然而,堆栈从未被处理过。事实上,尽管我阅读了这个 SO,但我无法弄清楚 incrementToken 方法是如何工作的。问题以及它何时处理来自 TokenStream 的给定 token 。

最后我想要实现的目标是:对于 xyz@gmail.com 作为输入文本,我想生成以下子标记:xyz@gmail.comxyzgmail.com

感谢任何帮助,

最佳答案

您的问题是,当您的 Stack 第一次填满时,输入 TokenStream 已经耗尽。所以 input.incrementToken() 返回 false。在递增输入之前,您应该先检查堆栈是否已满。像这样:

public final class SymbolSplitterFilter extends TokenFilter {

private final CharTermAttribute termAtt;
private final PositionIncrementAttribute posIncAtt;
private final Stack<String> termStack;
private AttributeSource.State current;
private final TypeAttribute typeAtt;

public SymbolSplitterFilter(TokenStream in)
{
super(in);
termStack = new Stack<>();
termAtt = addAttribute(CharTermAttribute.class);
posIncAtt = addAttribute(PositionIncrementAttribute.class);
typeAtt = addAttribute(TypeAttribute.class);
}

@Override
public boolean incrementToken() throws IOException
{
if (!this.termStack.isEmpty()) {
String part = termStack.pop();
restoreState(current);
termAtt.setEmpty().append(part);
posIncAtt.setPositionIncrement(0);
return true;
} else if (!input.incrementToken()) {
return false;
} else {
final String currentTerm = termAtt.toString();
final int bufferLength = termAtt.length();

if (bufferLength > 1 && currentTerm.indexOf("@") > 0) { // There must be sth more than just @
if (termStack.isEmpty()) {
termStack.addAll(Arrays.asList(currentTerm.split("@")));
current = captureState();
}
}
return true;

}

}
}

请注意,您可能还想更正偏移量并在测试显示生成的标记时更改标记的顺序:

 public class SymbolSplitterFilterTest extends BaseTokenStreamTestCase {


@Test
public void testSomeMethod() throws IOException
{
Analyzer analyzer = this.getAnalyzer();
assertAnalyzesTo(analyzer, "hey xyz@example.com",
new String[]{"hey", "xyz@example.com", "example.com", "xyz"},
new int[]{0, 4, 4, 4},
new int[]{3, 19, 19, 19},
new String[]{"word", "word", "word", "word"},
new int[]{1, 1, 0, 0}
);
}

private Analyzer getAnalyzer()
{
return new Analyzer()
{
@Override
protected Analyzer.TokenStreamComponents createComponents(String fieldName)
{
Tokenizer tokenizer = new MockTokenizer(MockTokenizer.WHITESPACE, false);
SymbolSplitterFilter testFilter = new SymbolSplitterFilter(tokenizer);
return new Analyzer.TokenStreamComponents(tokenizer, testFilter);
}
};
}

}

关于java - 使用 Lucene TokenFilter 将 token 分解为子 token ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44397708/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com