2014-09-29 3 views
2

я придумал решение для programmaticlly создать запрос для поиска фразы с подстановочными с помощью этого кода: создание Примера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 будет выполнять заданные пересечения между установленными условиями.

Пример: Предположим, что я хочу запросить выражение «a * karenjin *» (которое я разделил на ["a *", "karenjin *"], а затем создал запрос с использованием метода createPhraseQuery), и я хочу, чтобы это матчи предложения, содержащие: «ana karenjina», «ani karenjinoj», «ane karenjine», ... (разные случаи из-за хорватской грамматики).

Этот запрос очень медленный, что я не дождался достаточно долго, чтобы получить результаты (более 1 часа), а иногда и превышение исключения верхнего предела GC. Такое поведение несколько ожидалось, так как сам «a *» соответствует огромному количеству документов. Я знаю, что я могу запросить «a? Karanjin *», который дает результат 30-40 секунд (быстрее, но все же медленно).

Здесь я смущен. Если я запрошу только «karenjin *», он даст результаты за 1 сек. Поэтому я попытался запросить «a * karenjin *» и использовать фильтр «karenjin *», используя WildcardQuery и QueryWrapperFilter. И это все еще неприемлемо медленным (я убил процесс, прежде чем он вернулся).

В документации говорится, что фильтр уменьшает пространство поиска в запросе. Поэтому я попытался использовать фильтр:

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); 

ОК, что мой вопрос ?

Как же это quering:

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)); 
+0

Спасибо за совет, я буду стараться ограничить число членов и посмотреть, как он будет выполнять. Я ожидаю, что это будет намного быстрее. Но результаты могут быть неполными. Это компромисс между временем и результатами. –

+0

Я попробую. И в соответствии с книгой Lucene в действии WildcardQuery будет внутренне распознан и оптимизирован для PrefixQuery, если он заканчивается символом * или даже TermQuery, если нет подстановочных знаков. –

+0

Я считаю, что это правильно, но я скорее ожидал, что логика будет жить в синтаксическом анализе, и я не видел ее там. Однако может быть частью самого переписывания. – femtoRgon