Поиск внутренних подстрок в спящем поиске - PullRequest
0 голосов
/ 13 июня 2019

Я определил свою сущность следующим образом.

@Entity
@Indexed
@AnalyzerDef(name = "ngram_index", tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
    filters = {
        @TokenFilterDef(factory = LowerCaseFilterFactory.class),
        @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
        @TokenFilterDef(factory = NGramFilterFactory.class,
                        params = {
                            @Parameter(name = SearchConstants.MIN_GRAM_SIZE_NAME, value = SearchConstants.MIN_GRAM_SIZE_VALUE),
                            @Parameter(name = SearchConstants.MAX_GRAM_SIZE_NAME, value = SearchConstants.MAX_GRAM_SIZE_VALUE)
                        })
    })
@AnalyzerDef(name = "ngram_query", tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
    filters = {
        @TokenFilterDef(factory = LowerCaseFilterFactory.class),
        @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
    })
@NormalizerDef(name = "lowercase",
    filters = {
        @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
        @TokenFilterDef(factory = LowerCaseFilterFactory.class)
    }
)

@Table(name = "ORDER")
public class Order {
    @Id
    @DocumentId
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Field(analyzer = @Analyzer(definition = "ngram_index"))
    @Field(name = "name_Sort", store = Store.YES, normalizer= @Normalizer(definition="lowercase"))
    @SortableField(forField = "name_Sort")
    @Column(name = "NAME")
    private String name;

    //other fields, getters and setters omitted for brevity

Затем я попытался переписать анализатор по умолчанию, который используется во время индексации для запроса в другом классе, который не является сущностью.

public abstract class AbstractHibernateSearcher<S extends SearchableEntity> {
    // other fields and methods omitted here 

    protected Query buildInputSearchQuery(String[] searchableFields) {
        if(Strings.isNullOrEmpty(searchRequest.getQuery()) || searchableFields.length == 0) {
            return null;
        }
        SimpleQueryStringMatchingContext simpleQueryStringMatchingContext = queryBuilder.simpleQueryString().onField(searchableFields[0]);
        for(int i = 1; i < searchableFields.length; i++) {
            simpleQueryStringMatchingContext = simpleQueryStringMatchingContext.andField(searchableFields[i]);
        }
        Query inputSearchQuery = simpleQueryStringMatchingContext
            .withAndAsDefaultOperator()
            .matching((searchRequest.getQuery()).toLowerCase()).createQuery();

        QueryBuilder queryBuilder = getNGramQueryBuilder(searchableFields);
        return queryBuilder.bool().must(inputSearchQuery).createQuery();
    }

    protected QueryBuilder getNGramQueryBuilder(String[] searchFields) {
        if (searchFields.length == 0) {
            return null;
        }
        EntityContext entityContext = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(clazz);
        for(String field : searchFields) {
            entityContext = entityContext.overridesForField(field, "ngram_query");
        }
        return entityContext.get();
    }
}

Это дает мне следующую ошибку, когда я выполняю поиск по запросу.

{message: "HSEARCH000353: Неизвестный анализатор: 'ngram_query'. Убедитесь, что вы определили этот анализатор.",…} исключение: "RuntimeException" сообщение: «HSEARCH000353: Неизвестный анализатор:« ngram_query ». Убедитесь, что вы определили этот анализатор».

Я нашел это из официального документа.

Вы можете использовать @AnalyzerDef для любого:

@ Индексируемый объект независимо от того, где применяется анализатор;

родительский класс сущности @Indexed;

package-info.java пакета, содержащего сущность @Indexed.

Поскольку я вижу неизвестный анализатор, я думаю, что класс, в котором я пытался перезаписать анализатор "ngram_query", не имеет видимости на этом анализаторе?

1 Ответ

1 голос
/ 17 июня 2019

Да, вы можете создавать ngrams для каждого слова: используйте WhitespaceTokenizerFactory для вашего токенизатора и добавьте NGramFilterFactory к своим фильтрам токенов (обратите внимание, что это не тот класс, который вы упомянули: это фильтр токенов, а не токенизатор) .

Вам также понадобится использовать другой анализатор во время запроса, который не создает нграмм. В противном случае пользователь, набрав «manhantan», может получить соответствие для документов, содержащих, например, «man». См. https://stackoverflow.com/a/56107399/6692043 для получения информации о том, как это сделать.

Обратите внимание, что ngram может привести к очень большим индексам, особенно если вы не будете осторожны со значениями параметров "minGramSize" и "maxGramSize".

Другим решением будет использование вашего исходного анализатора и запроса с подстановочными знаками, но, к сожалению, он игнорирует анализ и может быть довольно медленным при использовании ведущих подстановочных знаков (что вам и нужно).

    protected Query inputFilterBuilder() {
        String[] searchableFields = getSearchableFields();
        if(searchableFields.length == 0) {
            return null;
        }
        TermMatchingContext termMatchingContext = queryBuilder.keyword().wildcard().onField(searchableFields[0]);
        for(int i = 1; i < searchableFields.length; i++) {
            termMatchingContext = termMatchingContext.andField(searchableFields[i]);
        }
        return termMatchingContext
            .matching(("*" + searchRequest.getQuery() + "*").toLowerCase()).createQuery();
    }

Обратите внимание, что приведенный выше код будет работать только при наличии одного условия поиска. Как только в searchRequest.getQuery() появятся пробелы, вы не получите никакого результата. Однако в индексированном тексте могут быть пробелы, чего вы и хотели, если я правильно понял.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...