Мне не удалось заставить 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. Я думаю, что немного рекурсивного или целочисленного программирования можно было бы применить, чтобы сделать его менее жадным, но меня это не беспокоило, и я все равно хотел более быстрый, а не более точный алгоритм.