Подтвердить что ты не робот

Добавление токенов в токен-поток lucene

Я написал a TokenFilter, который добавляет токены в поток.

1. Тесты показывают, что это работает, но я не совсем понимаю, почему.

Если бы кто-то мог пролить свет на семантику, я был бы благодарен. В частности, при (*), восстанавливая состояние, не означает ли это, что мы либо перезаписываем текущий токен, либо токен, созданный до захвата состояния?

Это примерно то, что я сделал

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)
     |
    ...

2. Атрибуты

Учитывая текст 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     |
+--------+---------------+-----------+--------------+-----------+
4b9b3361

Ответ 1

1.

Как работает API на основе атрибутов, каждый TokenStream в цепочке анализатора каким-то образом изменяет состояние некоторого Attribute при каждом вызове incrementToken(). Последний элемент в вашей цепочке затем выдает финальные токены.

Всякий раз, когда клиент вашей цепи анализатора вызывает incrementToken(), последний TokenStream будет устанавливать состояние некоторого Attribute на все, что необходимо для представления следующего токена. Если он не может этого сделать, он может называть incrementToken() на своем входе, чтобы предыдущий TokenStream выполнял свою работу. Это продолжается до тех пор, пока последний TokenStream не вернет false, указывая, что больше нет токенов.

A captureState копирует состояние всех Attribute вызывающего TokenStream в State, a restoreState перезаписывает каждое состояние Attribute тем, что было ранее (указано в качестве аргумента).

Как работает ваш токен-фильтр, он вызовет input.incrementToken(), так что предыдущий TokenStream установит состояние Attribute s 'в качестве следующего токена. Затем, если ваше определенное условие выполнено (например, терминAtt является "b" ), он добавит "bb" в стек, сохранит это состояние где-то и вернет true, чтобы клиент мог использовать токен. При следующем вызове incrementToken() он не будет использовать input.incrementToken(). Каким бы ни было текущее состояние, он представляет собой предыдущий, уже потребленный токен. Затем фильтр восстанавливает состояние, так что все точно так, как было раньше, а затем производит "bb" в качестве текущего токена и возвращает true, чтобы клиент мог использовать токен. Только при следующем вызове он (снова) потребляет следующий токен из предыдущего фильтра.

Это фактически не приведет к отображению графика, но вставьте "bb" после "b", поэтому он действительно

(a) -> (b) -> (bb) -> (c)

Итак, почему вы сохраняете состояние в первую очередь? При создании жетонов вы хотите убедиться, что, например, фразовые запросы или подсветка будут работать правильно. Когда текст "a b c" и "bb" является синонимом "b", вы ожидаете, что фразовый запрос "b c" будет работать, а также "bb c". Вы должны указать индекс, что оба: "b" и "bb" находятся в одном положении. Lucene использует приращение позиции для этого и по умолчанию, приращение позиции равно 1, а это означает, что каждый новый токен (чтение, вызов incrementToken()) приходит на 1 позицию после предыдущего. Таким образом, с конечными позициями поток создания

(a:1) -> (b:2) -> (bb:3) -> (c:4)

пока вы действительно хотите

(a:1) — -> (b:2)  -> — (c:3)
      \              /
        -> (bb:2) ->

Итак, для вашего фильтра для создания графика вам нужно установить приращение позиции равным 0 для вставленного "bb"

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.

Штокмер только изменяет токен, он обычно не создает новых токенов и не изменяет приращение или смещения позиции. Кроме того, поскольку приращение позиции означает, что текущий член должен наступить positionIncrement после предыдущего токена, вы должны иметь qux с шагом 1, поскольку это следующий токен после of и 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" )
    • Фактически, токены в одной и той же позиции (начало | конец) должны иметь одинаковый (начальный | конец) смещение

Надеюсь, это поможет пролить свет.