Как искать специальные символы ( ! ? : ) в Lucene

#lucene

#lucene

Вопрос:

Я хочу искать специальные символы в индексе.

Я экранировал все специальные символы в строке запроса, но когда я выполняю запрос как в lucene в индексе, он создает запрос как ().

Следовательно, поиск выполняется без полей.

Как решить эту проблему? Мой индекс содержит эти специальные символы.

Комментарии:

1. Пожалуйста, приведите пример того, что вы ищете и что создано. Что вы имеете в виду под «запросом как «?

2. Я ищу специальные символы, такие как ! ? и т.д. Что ж, я нашел решение. На самом деле мы используем какой-то пользовательский анализатор, и из-за примененных фильтров он выдавал запрос blanck( ()). Но когда я использовал KeywordAnalyzer, это сработало. Есть какие-либо входные данные на нем?

3. Используете ли вы один и тот же анализатор для индексации и для запроса? Пожалуйста, добавьте пример кода, описывающий ваш точный запрос и то, как вы его обрабатываете, прежде чем вызывать поиск.

Ответ №1:

Если вы используете StandardAnalyzer , это приведет к отбрасыванию не буквенных символов. Попробуйте проиндексировать то же значение с помощью WhitespaceAnalyzer и посмотрите, сохранятся ли при этом нужные вам символы. Он также может содержать ненужные вам данные: именно тогда вы можете подумать о написании собственного анализатора, что в основном означает создание стека TokenStream, который выполняет именно ту обработку, которая вам нужна.

Например, SimpleAnalyzer реализует следующий конвейер:

 @Override
public TokenStream tokenStream(String fieldName, Reader reader) {
   return new LowerCaseTokenizer(reader);
}
  

которые просто записывают токены в нижнем регистре.

StandardAnalyzer Делает гораздо больше:

 /** Constructs a {@link StandardTokenizer} filtered by a {@link
StandardFilter}, a {@link LowerCaseFilter} and a {@link StopFilter}. */
@Override
public TokenStream tokenStream(String fieldName, Reader reader) {
    StandardTokenizer tokenStream = new StandardTokenizer(matchVersion, reader);
    tokenStream.setMaxTokenLength(maxTokenLength);
    TokenStream result = new StandardFilter(tokenStream);
    result = new LowerCaseFilter(result);
    result = new StopFilter(enableStopPositionIncrements, result, stopSet);
    return resu<
 }
  

Вы можете смешивать и сопоставлять эти и другие компоненты в org.apache.lucene.analysis , или вы можете написать свои собственные специализированные TokenStream экземпляры, которые будут встроены в конвейер обработки вашим пользовательским Analyzer способом.

Еще одна вещь, на которую следует обратить внимание, — это какой CharTokenizer тип вы используете. CharTokenizer это абстрактный класс, который определяет механизм для обозначения текстовых строк. Это используется некоторыми более простыми анализаторами (но не StandardAnalyzer ). Lucene поставляется с двумя подклассами: a LetterTokenizer и a WhitespaceTokenizer . Вы можете создать свой собственный, который сохраняет нужные вам символы и разбивает их на те, которые вам не нужны, путем реализации boolean isTokenChar(char c) метода.

Ответ №2:

Возможно, это не актуально для автора, но для поиска специальных символов вам нужно:

  1. Создайте пользовательский анализатор
  2. Используйте это для индексации и поиска

Пример того, как это работает у меня:

 import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.custom.CustomAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.RAMDirectory;
import org.junit.Test;

import java.io.IOException;

import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;

public class LuceneSpecialCharactersSearchTest {

/**
 * Test that tries to search a string by some substring with each special character separately.
 */
@Test
public void testSpecialCharacterSearch() throws Exception {
    // GIVEN
    LuceneSpecialCharactersSearch service = new LuceneSpecialCharactersSearch();
    String[] luceneSpecialCharacters = new String[]{" ", "-", "amp;amp;", "||", "!", "(", ")", "{", "}", "[", "]", "^", """, "~", "*", "?", ":", "\"};

    // WHEN
    for (String specialCharacter : luceneSpecialCharacters) {
        String actual = service.search("list's special-characters "   specialCharacter);

        // THEN
        assertThat(actual, equalTo(LuceneSpecialCharactersSearch.TEXT_WITH_SPECIAL_CHARACTERS));
    }
}

private static class LuceneSpecialCharactersSearch {
    private static final String TEXT_WITH_SPECIAL_CHARACTERS = "This is the list's of special-characters   - amp;amp; || ! ( ) { } [ ] ^ " ~ ? : \ *";

    private final IndexWriter writer;

    public LuceneSpecialCharactersSearch() throws Exception {
        Document document = new Document();
        document.add(new TextField("body", TEXT_WITH_SPECIAL_CHARACTERS, Field.Store.YES));

        RAMDirectory directory = new RAMDirectory();
        writer = new IndexWriter(directory, new IndexWriterConfig(buildAnalyzer()));
        writer.addDocument(document);
        writer.commit();
    }

    public String search(String queryString) throws Exception {
        try (IndexReader reader = DirectoryReader.open(writer, false)) {
            IndexSearcher searcher = new IndexSearcher(reader);

            String escapedQueryString = QueryParser.escape(queryString).toLowerCase();

            Analyzer analyzer = buildAnalyzer();
            QueryParser bodyQueryParser = new QueryParser("body", analyzer);
            bodyQueryParser.setDefaultOperator(QueryParser.Operator.AND);


            Query bodyQuery = bodyQueryParser.parse(escapedQueryString);
            BooleanQuery query = new BooleanQuery.Builder()
                    .add(new BooleanClause(bodyQuery, BooleanClause.Occur.SHOULD))
                    .build();
            TopDocs searchResult = searcher.search(query, 1);

            return searcher.doc(searchResult.scoreDocs[0].doc).getField("body").stringValue();
        }
    }

    /**
     * Builds analyzer that is used for indexing and searching.
     */
    private static Analyzer buildAnalyzer() throws IOException {
        return CustomAnalyzer.builder()
                .withTokenizer("whitespace")
                .addTokenFilter("lowercase")
                .addTokenFilter("standard")
                .build();

    }
}
}