Фраза Lucene с подстановочными знаками
Я пришел к решению, чтобы программно создать запрос для поиска фразы с подстановочными знаками, используя этот код:
public static Query createPhraseQuery(String[] phraseWords, String field) {
SpanQuery[] queryParts = new SpanQuery[phraseWords.length];
for (int i = 0; i < phraseWords.length; i++) {
WildcardQuery wildQuery = new WildcardQuery(new Term(field, phraseWords[i]));
queryParts[i] = new SpanMultiTermQueryWrapper<WildcardQuery>(wildQuery);
}
return new SpanNearQuery(queryParts, //words
0, //max distance
true //exact order
);
}
Создание примера и вызов метода toString() выведут:
String[] phraseWords = new String[]{"foo*", "b*r"};
Query phraseQuery = createPhraseQuery(phraseWords, "text");
System.out.println(phraseQuery.toString());
выходы:
spanNear([SpanMultiTermQueryWrapper(text:foo*), SpanMultiTermQueryWrapper(text:b*r)], 0, true)
Который работает отлично, и достаточно быстро для большинства случаев. Например, если я создаю такой запрос и выполняю поиск по нему, он выдаст желаемые результаты, например:
Sentence with foo bar.
Foolies beer drinkers.
...
И не что-то вроде
Bar fooes.
Foo has bar.
Я упоминал, что в большинстве случаев запрос работает достаточно быстро. В настоящее время у меня есть индекс с размером aprox. 200 ГБ и в среднем время поиска составляет от 0,1 до 3 секунд. В зависимости от многих факторов, таких как: кеш, размер подмножеств документов, соответствующих одному слову во фразе, так как lucene будет выполнять пересечения набора между основанными терминами.
Пример: Позвольте supose я хочу запросить фразу "an* karenjin*" (которую я разделю на ["an*", "karenjin*"] и затем создать запрос с использованием метода createPhraseQuery) и хочу, чтобы она соответствовала предложениям, содержащим: " ana karenjina "," ani karenjinoj "," ane karenjine ",... (разные случаи из-за хорватской грамматики).
Этот запрос очень медленный, поэтому я не ждал достаточно долго, чтобы получить результаты (более 1 часа), и иногда вызывает превышение лимита служебных данных GC. Такое поведение несколько ожидаемо, так как само "*" соответствует большому количеству документов. Я знаю, что я мог бы запросить "? Karanjin*", который дает результаты в 30-40 секунд (быстрее, но все еще медленно).
Это где я запутался. Если я запрашиваю только "karenjin *", это дает результаты в течение 1 секунды. Поэтому я попытался запросить "* karenjin *" и использовать фильтр "karenjin *" с использованием WildcardQuery и QueryWrapperFilter. И это все еще недопустимо медленно (я убил процесс, прежде чем он вернулся в любом случае).
Документация говорит, что Filter уменьшает пространство поиска Query. Поэтому я попытался использовать фильтр:
Filter filter = new QueryWrapperFilter(new WildcardQuery(new Term("text", "karanjin*")));
И запрос:
Query query = createPhraseQuery(new String[]{"an*", "karenjin*"}, "text");
Чем поиск, (после нескольких разминочных запросов):
Sort sort = new Sort(new SortField("insertTime", SortField.Type.STRING, true));
TopDocs docs = searcher.search(query, filter, 100, sort);
ОК, какой у меня вопрос?
Как получилось, спрашивает:
Query query = new WildcardQuery(new Term("text", "karanjin*"));
быстро, но использование фильтра, описанного выше, все еще медленно?
1 ответ
Да, подстановочные знаки могут влиять на производительность, особенно если они соответствуют многим терминам, но то, что вы описываете, кажется удивительным. Трудно сказать наверняка, почему это происходит, но для попытки.
Я приму:
Query query = new WildcardQuery(new Term("text", "an*"));
Сам по себе работает очень плохо, как описано. Поскольку оба подстановочных знака являются запросами в стиле префикса, лучше использовать PrefixQuery
вместо.
Query query = new PrefixQuery(new Term("text", "an"));
Хотя я не думаю, что это будет иметь большое значение, если вообще будет. То, что может сделать что-то другое - это изменить метод переписывания. Вы можете попробовать ограничить количество Terms
запрос переписывается в:
Query query = new PrefixQuery(new Term("text", "an"));
//or
//Query query = new WildcardQuery(new Term("text", "an*"));
query.setRewriteMethod(new MultiTermQuery.RewriteMethod.TopTermsRewrite(10));