Запрос Lucene: bla ~ * (совпадающие слова, начинающиеся с чего-то нечеткого), как?
В синтаксисе запроса Lucene я хотел бы совместить * и ~ в действительном запросе, подобном:
bla ~ *//недопустимый запрос
Значение: Пожалуйста, сопоставьте слова, начинающиеся с "bla" или что-то похожее на "bla".
Обновление:
То, что я делаю сейчас, работает для малого ввода, использует следующий (фрагмент схемы SOLR):
<fieldtype name="text_ngrams" class="solr.TextField">
<analyzer type="index">
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="0"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.EdgeNGramFilterFactory" minGramSize="2" maxGramSize="15" side="front"/>
</analyzer>
<analyzer type="query">
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="0"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
Если вы не используете SOLR, это делает следующее.
Indextime: индексировать данные, создавая поле, содержащее все префиксы моего (короткого) ввода.
Searchtime: используйте только оператор ~, поскольку префиксы явно присутствуют в индексе.
Ответы
Ответ 1
Я не считаю, что Lucene поддерживает что-то подобное, и я не верю, что это имеет тривиальное решение.
"Нечеткие" запросы не работают с фиксированным числом символов. bla~
может, например, соответствовать blah
, и поэтому он должен учитывать весь термин.
Что вы можете сделать, так это реализовать алгоритм расширения запроса, который взял запрос bla~*
и преобразовал его в серию OR запросов
bla* OR blb* OR blc OR .... etc.
Но это действительно реально, если строка очень короткая или вы можете сузить расширение, основанное на некоторых правилах.
В противном случае, если длина префикса исправлена, вы можете добавить поле с подстроками и выполнить нечеткий поиск. Это даст вам то, что вы хотите, но будет работать только в том случае, если ваш случай использования достаточно узкий.
Вы точно не определяете, зачем вам это нужно, возможно, это приведет к появлению других решений.
Один сценарий, о котором я могу думать, имеет дело с разными формами слов. Например. нахождение car
и cars
.
Это легко на английском языке, так как существуют словарные штемпели. На других языках может быть довольно сложно реализовать словосочетание, если не невозможно.
В этом случае вы можете (при условии, что у вас есть доступ к хорошему словарю), найдите поисковый запрос и программно разверните поиск для поиска всех форм слова.
например. поиск cars
переводится в car OR cars
. Это было успешно применено для моего языка, по крайней мере, в одной поисковой системе, но, очевидно, нетривиально реализовать.
Ответ 2
в области разработки lucene (еще не выпуске), существует код для поддержки таких случаев, как это, через AutomatonQuery. Предупреждение: API могут/будут изменяться до его выпуска, но это дает вам идею.
Вот пример вашего случая:
// a term representative of the query, containing the field.
// the term text is not so important and only used for toString() and such
Term term = new Term("yourfield", "bla~*");
// builds a DFA that accepts all strings within an edit distance of 2 from "bla"
Automaton fuzzy = new LevenshteinAutomata("bla").toAutomaton(2);
// concatenate this DFA with another DFA equivalent to the "*" operator
Automaton fuzzyPrefix = BasicOperations.concatenate(fuzzy, BasicAutomata.makeAnyString());
// build a query, search with it to get results.
AutomatonQuery query = new AutomatonQuery(term, fuzzyPrefix);
Ответ 3
Это для службы поиска адресов, где я хочу предложить адреса, основанные на частично типизированных и, возможно, туманных уличных именах /citynames/etc (любая комбинация). (думаю, ajax, пользователи вводят частичные адреса улицы в текстовое поле)
В этом случае предлагаемое расширение запроса, возможно, не так возможно, так как частичная строка (адрес улицы) может стать длиннее "короткой":)
Нормализация
Одна из возможностей, о которой я могу думать, - это использовать "нормализацию" строки вместо нечетких запросов и просто комбинировать их с подстановочными запросами. Адрес страницы
"miklabraut 42, 101 reykjavík"
, при нормализации станет "miklabrat 42 101 rekavik"
.
Итак, построим такой индекс:
1) создайте индекс с записями, содержащими "нормализованные" версии имен улиц, названий городов и т.д., с одним адресом на один документ (1 или несколько полей).
И найдите индекс, подобный этому:
2) Нормализовать входные строки (например, mikl reyk
), используемые для формирования запросов (т.е. mik rek
).
3) используйте групповой символ op для выполнения поиска (т.е. mik* AND rek*
), оставив нечеткую часть.
Это будет летать, если алгоритм нормализации достаточно хорош:)
Ответ 4
Вы хотите объединить подстановочный знак и нечеткий запрос? Вы можете использовать логический запрос с условием ИЛИ для объединения, например:
BooleanQuery bq = new BooleanQuery();
Query q1 = //here goes your wildcard query
bq.Add(q1, BooleanClause...)
Query q2 = //here goes your fuzzy query
bq.Add(q2, BooleanClause...)