Смещение позиции для запросов фразы в Lucene - PullRequest
0 голосов
/ 30 мая 2011

Я работаю над настройкой плагина Highlighter (используя FVH) для вывода смещения позиции условий запроса для данного поиска. До сих пор я был в состоянии извлечь информацию о смещении для обычных запросов, используя код ниже. Однако для запросов Phrase код возвращает смещение позиции всех условий запроса (т. Е. TermSet), даже если он не является частью запроса Phrase. Поэтому мне интересно, есть ли способ в Lucene получить информацию о смещении только совпадающей фразы для запросов фразы с использованием FVH?

// In DefaultSolrHighlighter.java::doHighlightingByFastVectorHighlighter()

SolrIndexSearcher searcher = req.getSearcher();
TermFreqVector[] tvector = searcher.getReader().getTermFreqVectors(docId);
TermPositionVector tvposition = (TermPositionVector) tvector[0];

 Set<String> termSet = highlighter.getHitTermSet (fieldQuery, fieldName);

 int[] positions;
 List hitOffsetPositions = new ArrayList<String[]>();

 for (String term : termSet)
 {
    int index = tvposition.indexOf(term); 
    positions = tvposition.getTermPositions(index);

    StringBuilder sb = new StringBuilder();
    for (int pos : positions)
    {
        if (!Integer.toString(pos).isEmpty())
            sb.append( pos ).append(',');
    }
    hitOffsetPositions.add(sb.substring(0, sb.length() - 1).toString());
 }

 if( snippets != null && snippets.length > 0 )
{
  docSummaries.add( fieldName, snippets );
  docSummaries.add( "hitOffsetPositions", hitOffsetPositions);
}


// In FastVectorHighlighter.java
// Wrapper function to get query Terms
   public Set<String> getHitTermSet (FieldQuery fieldQuery, String fieldName)
  {
      Set<String> termSet = fieldQuery.getTermSet( fieldName );
      return termSet;
  }

Токовый выход:

<lst name="6H500F0">
  <arr name="name">
  <str> New <em>hard drive</em> 500 GB SATA-300 and old drive 200 GB</str>
</arr>
<arr name="hitOffsetPositions">
    <str>2</str>
    <str>3</str>
    <str>10</str>
</arr>

Ожидаемый результат:

<lst name="6H500F0">
  <arr name="name">
  <str> New <em>hard drive</em> 500 GB SATA-300 and old drive 200 GB</str>
</arr>
<arr name="hitOffsetPositions">
    <str>2</str>
    <str>3</str>
</arr>

Поле, которое я пытаюсь выделить, имеет termVectors = "true" , termPositions = "true" и termOffsets = "true" и использую Lucene 3.1.0.

1 Ответ

0 голосов
/ 31 мая 2011

Мне не удалось заставить FVH правильно обрабатывать запросы фраз, и я вынужден был разработать свой собственный сумматор. Суть моего подхода обсуждается здесь ; в итоге я создал массив объектов, по одному на каждый термин, который я извлек из запросов. Каждый объект содержит индекс слова и его положение, а также был ли он уже использован в каком-либо совпадении. Этими экземплярами являются TermAtPosition в приведенном ниже примере. Затем, учитывая диапазон позиций и массив идентификаторов слов (индексов), соответствующих запросу фразы, я перебирал массив, пытаясь найти соответствие всем индексам терминов в данном интервале. Если я нашел совпадение, я пометил каждый соответствующий термин как использованный и добавил соответствующий диапазон в список совпадений. Затем я мог бы использовать эти совпадения для оценки предложений. Вот соответствующий код:

protected void scorePassage(TermPositionVector v, String[] words, int span, 
                    float score, SentenceScore[] scores, Scorer scorer) {
    TermAtPosition[] order = getTermsInOrder(v, words);
    if (order.length < words.length)
        return;
    int positions[] = new int[words.length];
    List<int[]> matches = new ArrayList<int[]>();
    for(int t=0; t<order.length; t++) {
        TermAtPosition tap = order[t];
        if (tap.consumed)
            continue;

        int p = 0;
        positions[p++] = tap.position;
        for(int u=0; u<words.length; u++) {
            if (u == tap.termIndex)
                continue;
            int nextTermPos = spanContains(order, u, tap.position, span);
            if (nextTermPos == -1)
                break;
            positions[p++] = nextTermPos;
        }
        // got all terms
        if (p == words.length)
            matches.add(recordMatch(order, positions.clone()));
    }
    if (matches.size() > 0)
        for (SentenceScore sentenceScore: scores) {
            for(int[] matchingPositions: matches)
                scorer.scorePassage(sentenceScore, matchingPositions, score);
    }
}


protected int spanContains(TermAtPosition[] order, int targetWord, 
                  int start, int span) {
    for (int i=0; i<order.length; i++) {
        TermAtPosition tap = order[i];
        if (tap.consumed || tap.position <= start || 
                       (tap.position > start + span))
            continue;
        if (tap.termIndex == targetWord)
            return tap.position;
    }
    return -1;
}

Этот подход, кажется, работает, но он жадный. При заданной последовательности «a a b c» он будет соответствовать первому a (оставляя в покое второе a), а затем совпадать с b и c. Я думаю, что немного рекурсивного или целочисленного программирования можно было бы применить, чтобы сделать его менее жадным, но меня это не беспокоило, и я все равно хотел более быстрый, а не более точный алгоритм.

...