Lucene BooleanQuery неверный результат

Я создал Lucene RAMDirectory для сбора данных из разных источников и быстрого их поиска. Я потратил много часов, чтобы понять различные анализаторы и стратегии индексирования, но в некоторых случаях результат запроса не является ожидаемым.

Вот демонстрационный класс:

class LuceneDemo {

    static final String ANIMAL = "animal";
    static final String PERSON = "person";

    private StandardAnalyzer analyzer = new StandardAnalyzer();

    private IndexSearcher searcher;
    private IndexWriter writer;

    LuceneDemo() {
        Directory ramDirectory = new RAMDirectory();
        IndexWriterConfig config = new IndexWriterConfig(analyzer);
        try {
            writer = new IndexWriter(ramDirectory, config);

            addDocument(createDocument(PERSON, "DR-(frankenstein)"));
            addDocument(createDocument(ANIMAL, "gray fox"));
            addDocument(createDocument(ANIMAL, "island fox"));

            writer.close();
            IndexReader reader = DirectoryReader.open(ramDirectory);
            searcher = new IndexSearcher(reader);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private Document createDocument(String type, String value) {
        Document document = new Document();
        document.add(new TextField("type", type, Field.Store.YES));
        document.add(new TextField("name", value, Field.Store.YES));
        document.add(new StringField("name", value, Field.Store.YES));
        return document;
    }

    private void addDocument(Document document) {
        try {
            writer.addDocument(document);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    List<String> getDocuments(String type, String value) {
        value = "*" + QueryParser.escape(value) + "*";
        try {
            QueryParser queryParser = new QueryParser("name", analyzer);
            queryParser.setAllowLeadingWildcard(true);
            queryParser.setDefaultOperator(QueryParser.Operator.AND);

            BooleanQuery.Builder query = new BooleanQuery.Builder();
            query.add(new TermQuery(new Term("type", type)), BooleanClause.Occur.MUST);
            query.add(queryParser.parse(value), BooleanClause.Occur.MUST);

            TopDocs docs = searcher.search(query.build(), 10);

            return Arrays.stream(docs.scoreDocs).map(scoreDoc -> {
                try {
                    return searcher.doc(scoreDoc.doc).get("name");
                } catch (IOException e) {
                    return "";
                }
            }).collect(Collectors.toList());
        } catch (ParseException | IOException e) {
            e.printStackTrace();
        }
        return Collections.emptyList();
    }
}

Если я ищу "бык", "серая лиса" или "-(frankenstein)", код работает довольно хорошо. Но у меня нет результата для "DR-(Франкенштейн)". Понятия не имею, что я сделал не так. Так что любые предложения приветствуются.

// OK
luceneDemo.getDocuments(LuceneDemo.ANIMAL, "ox").forEach(System.out::println);
luceneDemo.getDocuments(LuceneDemo.ANIMAL, "gray fox").forEach(System.out::println);
luceneDemo.getDocuments(LuceneDemo.PERSON, "-(frankenstein)").forEach(System.out::println);

// NOT OK
luceneDemo.getDocuments(LuceneDemo.PERSON, "DR-(frankenstein)").forEach(System.out::println);

1 ответ

Решение

Вот как индексируются ваши документы -

  1. Тип документа #1: имя человека: имя доктора: имя Франкенштейна:DR-(Франкенштейн) (Примечание: StringField не является токенизированным и не преобразуется в нижний регистр)
  2. doC#2 тип: имя животного: серая кличка: имя лисы: серая лиса
  3. doC#3 тип: имя животного: имя острова: имя лисы: остров лисы

В принципе StringField поле индексов независимо от analyzer - без токенизации и опускания корпуса. В то время как читатель использует StandardAnalyzer и опускает дело для всего поиска. Следовательно, поиск "DR-(frankenstein)" ищет "dr-(frankenstein)", который не имеет соответствия.

Чтобы ваш код работал с использованием StandardAnalyzer, вам нужно индексировать StringField в нижнем регистре.

document.add(new StringField("name", value.toLowerCase(), Field.Store.YES));
Другие вопросы по тегам