我正在定制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。
答案 0 :(得分:0)
我无法让FVH正确处理短语查询,并且不得不开发自己的摘要生成器。我的方法的要点被讨论here;我最后要做的是创建一个对象数组,每个术语对应一次从查询中提取。每个对象都包含一个单词索引及其位置,以及它是否已在某些匹配项中使用过。这些实例是下面示例中的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。我认为可以应用一些递归或整数编程来减少它的贪婪,但我不能打扰,并且想要一个更快而不是更准确的算法。