0

我正在定制荧光笔插件(使用 FVH)以输出给定搜索的查询词的位置偏移量。到目前为止,我已经能够使用下面的代码提取正常查询的偏移信息。但是,对于 Phrase 查询,即使它不是 Phrase 查询的一部分,代码也会返回所有查询术语(即 termSet)的位置偏移量。因此,我想知道 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。

4

1 回答 1

0

我无法让 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;
}

这种方法似乎有效,但它是贪婪的。给定一个序列“aab c”,它将匹配第一个 a(不理会第二个 a),然后匹配 b 和 c。我认为可以应用一些递归或整数编程来减少贪婪,但我不会被打扰,并且无论如何都想要一个更快而不是更准确的算法。

于 2011-05-30T23:05:20.460 回答