Lucene: фразы из нескольких слов в качестве поисковых терминов

Я пытаюсь сделать доступный для поиска телефон / местный бизнес-справочник, используя Apache Lucene.

У меня есть поля для названия улицы, названия компании, номера телефона и т. Д. Проблема, с которой я сталкиваюсь, заключается в том, что когда я пытаюсь выполнить поиск по улице, где название улицы содержит несколько слов (например, "полумесяц"), результаты не возвращаются. Но если я попытаюсь выполнить поиск только одним словом, например, "полумесяц", я получу все результаты, которые мне нужны.

Я индексирую данные с помощью следующего:

String LocationOfDirectory = "C:\\dir\\index";

StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_34);
Directory Index = new SimpleFSDirectory(LocationOfDirectory);

IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE.34, analyzer);
IndexWriter w = new IndexWriter(index, config);


Document doc = new Document();
doc.add(new Field("Street", "the crescent", Field.Store.YES, Field.Index.Analyzed);

w.add(doc);
w.close();

Мои поиски работают так:

int numberOfHits = 200;
String LocationOfDirectory = "C:\\dir\\index";
TopScoreDocCollector collector = TopScoreDocCollector.create(numberOfHits, true);
Directory directory = new SimpleFSDirectory(new File(LocationOfDirectory));
IndexSearcher searcher = new IndexSearcher(IndexReader.open(directory);

WildcardQuery q = new WildcardQuery(new Term("Street", "the crescent");

searcher.search(q, collector);
ScoreDoc[] hits = collector.topDocs().scoreDocs;

Я попытался поменять местами шаблонный запрос для запроса фразы, сначала со всей строкой, а затем разделить строку на пустое пространство и обернуть их в BooleanQuery следующим образом:

String term = "the crescent";
BooleanQuery b = new BooleanQuery();
PhraseQuery p = new PhraseQuery();
String[] tokens = term.split(" ");
for(int i = 0 ; i < tokens.length ; ++i)
{
    p.add(new Term("Street", tokens[i]));
}
b.add(p, BooleanClause.Occur.MUST);

Однако это не сработало. Я попытался использовать KeywordAnalyzer вместо StandardAnalyzer, но затем все другие типы поиска перестали работать. Я попытался заменить пробелы другими символами (+ и @) и преобразовать запросы в и из этой формы, но это все равно не работает. Я думаю, что это не работает, потому что + и @ являются специальными символами, которые не индексируются, но я не могу найти список где-нибудь, какие символы такие.

Я начинаю немного сходить с ума, кто-нибудь знает, что я делаю не так?

Спасибо Рик

5 ответов

Решение

Я обнаружил, что моя попытка сгенерировать запрос без использования QueryParser не сработала, поэтому я перестал пытаться создавать свои собственные запросы и вместо этого использовал QueryParser. Все рекомендации, которые я видел в Интернете, показали, что вы должны использовать тот же Analyzer в QueryParser, который вы используете во время индексации, поэтому я использовал StandardAnalyzer для создания QueryParser.

Это работает в этом примере, потому что StandardAnalyzer удаляет слово "the" с улицы "crescent" во время индексации, и, следовательно, мы не можем искать его, потому что его нет в индексе.

Однако, если мы решим выполнить поиск "Дорога в роще", у нас возникнет проблема с готовой функциональностью, а именно, что запрос вернет все результаты, содержащие либо "Грув", либо "Дорога". Это легко исправить, настроив QueryParser так, чтобы его операцией по умолчанию было И вместо ИЛИ.

В итоге правильное решение было следующим:

int numberOfHits = 200;
String LocationOfDirectory = "C:\\dir\\index";
TopScoreDocCollector collector = TopScoreDocCollector.create(numberOfHits, true);
Directory directory = new SimpleFSDirectory(new File(LocationOfDirectory));
IndexSearcher searcher = new IndexSearcher(IndexReader.open(directory);

StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_35);

//WildcardQuery q = new WildcardQuery(new Term("Street", "the crescent");
QueryParser qp = new QueryParser(Version.LUCENE_35, "Street", analyzer);
qp.setDefaultOperator(QueryParser.Operator.AND);

Query q = qp.parse("grove road");

searcher.search(q, collector);
ScoreDoc[] hits = collector.topDocs().scoreDocs;

Причина, по которой вы не получаете свои документы обратно, заключается в том, что при индексации вы используете StandardAnalyzer, который преобразует токены в нижний регистр и удаляет стоп-слова. Таким образом, единственный термин, который индексируется для вашего примера, это "полумесяц". Однако подстановочные запросы не анализируются, поэтому в качестве обязательной части запроса указывается "the". То же самое касается запросов фраз в вашем сценарии.

KeywordAnalyzer вероятно, не очень подходит для вашего варианта использования, потому что он принимает содержимое всего поля как один токен. Ты можешь использовать SimpleAnalyzer для поля улицы - разделит ввод на все не-буквенные символы, а затем преобразует их в строчные. Вы также можете рассмотреть возможность использования WhitespaceAnalyzer с LowerCaseFilter, Вам нужно попробовать разные варианты и решить, что лучше всего подходит для ваших данных и пользователей.

Кроме того, вы можете использовать различные анализаторы для каждого поля (например, с PerFieldAnalyzerWrapper) если изменение анализатора для этого поля прерывает другие поиски.

Решение @RikSaunderson для поиска документов, в которых должны выполняться все подзапросы запроса, все еще работает с Lucene 9.

      QueryParser queryParser = new QueryParser(LuceneConstants.CONTENTS, new StandardAnalyzer());
queryParser.setDefaultOperator(QueryParser.Operator.AND);

Если вы хотите, чтобы точные слова соответствовали улице, вы можете установить поле "Улица" NOT_ANALYZED, которое не будет фильтровать стоп-слово "the".

doc.add(new Field("Street", "the crescent", Field.Store.YES, Field.Index.Not_Analyzed);

Нет необходимости использовать какие-либо Analyzer здесь Coz Hibernate неявно использует StandardAnalyzer который разделит слова на основе white spaces поэтому решение здесь установлено Analyze в NO он будет автоматически выполнять Multi Phrase Search

 @Column(name="skill")
    @Field(index=Index.YES, analyze=Analyze.NO, store=Store.NO)
    @Analyzer(definition="SkillsAnalyzer")
    private String skill;
Другие вопросы по тегам