测试表明它有效,但我不完全明白为什么.
如果有人能够阐明语义,我将不胜感激.特别是,在(*)中,恢复状态是不是意味着我们要么覆盖当前的令牌或者在捕获状态之前创建的令牌?
这大概是我做的
private final LinkedList<String> extraTokens = new LinkedList<String>(); private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); private State savedState; @Override public boolean incrementToken() throws IOException { if (!extraTokens.isEmpty()) { // Do we not loose/overwrite the current termAtt token here? (*) restoreState(savedState); termAtt.setEmpty().append(extraTokens.remove()); return true; } if (input.incrementToken()) { if (/* condition */) { extraTokens.add("fo"); savedState = captureState(); } return true; } return false; }
这是否意味着,对于空格的输入流标记字符串“a b c”
(a) -> (b) -> (c) -> ...
其中bb是b的新同义词,那么在使用restoreState时,该图将被构造为这样?
(a) / \ (b) (bb) \ / (c) | ...
给定文本foo bar baz与fo是foo和qux的句柄是bar baz的同义词,我是否构建了正确的属性表?
+--------+---------------+-----------+--------------+-----------+ | Term | startOffset | endOffset | posIncrement | posLenght | +--------+---------------+-----------+--------------+-----------+ | foo | 0 | 3 | 1 | 1 | | fo | 0 | 3 | 0 | 1 | | qux | 4 | 11 | 0 | 2 | | bar | 4 | 7 | 1 | 1 | | baz | 8 | 11 | 1 | 1 | +--------+---------------+-----------+--------------+-----------+
解决方法
基于属性的API如何工作,分析器链中的每个TokenStream都会以每次调用incrementToken()的方式修改某些Attributes的状态.你的链中的最后一个元素产生最终的令牌.
每当分析器链的客户端调用incrementToken()时,最后一个TokenStream会将某些属性的状态设置为表示下一个标记所需的任何值.如果不能这样做,它可能会在其输入上调用incrementToken(),以使之前的TokenStream工作.这一直持续到最后一个TokenStream返回false,表明没有更多的令牌可用.
一个captureState将调用TokenStream的所有属性的状态复制到一个状态,一个restoreState会覆盖每个Attribute的状态,以前被捕获的(作为一个参数给出).
令牌过滤器的工作方式是,它将调用input.incrementToken(),以便之前的TokenStream将将Attributes的状态设置为下一个令牌.然后,如果你定义的条件成立(比如说termAtt是“b”),那么它会将一个“bb”添加到一个堆栈中,将这个状态保存在某个地方并返回true,以便客户端可以使用该令牌.在incrementToken()的下一次调用中,它不会使用input.incrementToken().无论当前状态如何,它代表以前已经消耗的令牌.然后,过滤器恢复状态,使得一切都与之前完全一样,然后生成“bb”作为当前令牌并返回true,以便客户端可以使用令牌.只有在下一次呼叫时,它将(再次)从上一个过滤器中消耗下一个令牌.
这实际上不会产生您显示的图形,但在“b”之后插入“bb”,所以它是真的
(a) -> (b) -> (bb) -> (c)
那么,为什么你先把国家拯救出来呢?
当产生令牌时,你想确保,例如短语查询或突出显示将正常工作.当您的文字“a b c”和“bb”是“b”的同义词时,您会希望短语查询“b c”工作,以及“bb c”.你必须告诉索引,“b”和“bb”都在同一个位置. Lucene使用位置增量为每个默认值,位置增量为1,这意味着每个新的令牌(读取,调用incrementToken())都在前一个位置之后.所以,在最终的位置,生产流是
(a:1) -> (b:2) -> (bb:3) -> (c:4)
而你真的想要的
(a:1) — -> (b:2) -> — (c:3) \ / -> (bb:2) ->
因此,为了生成图形,您必须将插入的“bb”的位置增量设置为0
private final PositionIncrementAttribute posIncAtt = addAttribute(PositionIncrementAttribute.class); // later in incrementToken restoreState(savedState); posIncAtt.setPositionIncrement(0); termAtt.setEmpty().append(extraTokens.remove());
restoreState确保保留其他属性,如偏移,令牌类型等,并且您只需更改用例所需的其他属性.
是的,你正在覆盖restoreState之前的任何状态,所以你有责任在正确的地方使用它.只要你不调用input.incrementToken(),你不会提前输入流,所以你可以做任何你想要的状态.
2.
干扰器只会更改令牌,通常不会产生新的令牌,也不会更改位置增量或偏移量.
此外,当位置增量意味着当前术语应该位于前一个令牌之后的位置增量位置时,应该使用增量为1的qux,因为它是下一个令牌,而bar应该增加0,因为它是在与qux相同的位置.桌子宁愿看起来像
+--------+---------------+-----------+--------------+-----------+ | Term | startOffset | endOffset | posIncrement | posLenght | +--------+---------------+-----------+--------------+-----------+ | fo | 0 | 3 | 1 | 1 | | qux | 4 | 11 | 1 | 2 | | bar | 4 | 7 | 0 | 1 | | baz | 8 | 11 | 1 | 1 | +--------+---------------+-----------+--------------+-----------+
作为基本规则,对于多项同义词,“ABC”是“a b c”的同义词,您应该看到
> positionIncrement(“ABC”)> 0(第一个令牌的增量)
> positionIncrement(*)> = 0(位置不能倒退)
startOffset(“ABC”)== startOffset(“a”)和endOffset(“ABC”)== endOffset(“c”)
实际上,相同(开始|结束)位置的令牌必须具有相同的(start | end)偏移量
希望这有助于揭开一些光.