Фраза 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));
Другие вопросы по тегам