Как поддерживать поиск по токенам и без них одновременно - PullRequest
0 голосов
/ 27 марта 2019

Я пытаюсь выполнить поиск в спящем режиме, чтобы он поддерживал поиск как по токену, так и без него (простите, если я здесь использую неправильный термин).Пример:

У меня есть список сущностей следующего типа.

@Entity
@Indexed
@NormalizerDef(name = "lowercase",
    filters = {
        @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
        @TokenFilterDef(factory = LowerCaseFilterFactory.class)
    }
)
public class Deal {
    //other fields omitted for brevity purposes

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

    //Getters/Setters omitted here
}

Я также использовал метод ключевого слова для построения построителя запросов, показанного ниже.Метод getSearchableFields возвращает список доступных для поиска полей.В этом примере «name» будет в этом возвращенном списке, так как имя поля в Deal доступно для поиска.

    protected Query inputFilterBuilder() {
        return queryBuilder.keyword()
            .wildcard().onFields(getSearchableFields())
            .matching("*" + searchRequest.getQuery().toLowerCase() + "*").createQuery();
    }

Эта настройка отлично работает, когда я использую только слова целиком для поиска.Например, если у меня есть две сущности Deal, одну зовут «Практичная конкретная шляпа», а другую - «Практичный хлопковый сыр».При поиске по «Практике» я получаю эти две сущности обратно.Но при поиске по "Practical Co" я получаю 0 объектов обратно.Причина в том, что имя поля маркировано, а «Practical Co» не является ключевым словом.

Мой вопрос заключается в том, как поддерживать оба поиска одновременно, чтобы эти 2 объекта возвращались при поиске по "Практикум" или "Практикам".

Я прочитал официальную документацию по поиску в спящем режиме, и у меня есть догадка, что я должен добавить еще одно поле, предназначенное для поиска без тегов.Возможно, нужно также обновить способ построения построителя запросов?

Обновить

Не работает решение с использованием SimpleQueryString.

На основе предоставленногоответ, я написал следующую логику построителя запросов.Однако это не работает.

    protected Query inputFilterBuilder() {
        String[] searchableFields = getSearchableFields();
        if(searchableFields.length == 0) {
            return queryBuilder.simpleQueryString().onField("").matching("").createQuery();
        }
        SimpleQueryStringMatchingContext simpleQueryStringMatchingContext = queryBuilder.simpleQueryString().onField(searchableFields[0]);
        for(int i = 1; i < searchableFields.length; i++) {
            simpleQueryStringMatchingContext = simpleQueryStringMatchingContext.andField(searchableFields[i]);
        }
        return simpleQueryStringMatchingContext
            .matching("\"" + searchRequest.getQuery() + "\"").createQuery();
    }

Рабочее решение с использованием отдельного анализатора для запросов и фраз.

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

    protected Query inputFilterBuilder() {
        String[] searchableFields = getSearchableFields();
        if(searchableFields.length == 0) {
            return queryBuilder.phrase().onField("").sentence("").createQuery();
        }
        PhraseMatchingContext phraseMatchingContext = queryBuilder.phrase().onField(searchableFields[0]);
        for(int i = 1; i < searchableFields.length; i++) {
            phraseMatchingContext = phraseMatchingContext.andField(searchableFields[i]);
        }
        return phraseMatchingContext.sentence(searchRequest.getQuery()).createQuery();
    }

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

Определения анализаторов:

@AnalyzerDef(name = "edgeNgram", tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
    filters = {
        @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
        @TokenFilterDef(factory = LowerCaseFilterFactory.class),
        @TokenFilterDef(factory = EdgeNGramFilterFactory.class,
                        params = {
                            @Parameter(name = "minGramSize", value = "1"),
                            @Parameter(name = "maxGramSize", value = "10")
                        })
    })
@AnalyzerDef(name = "edgeNGram_query", tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
    filters = {
        @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
        @TokenFilterDef(factory = LowerCaseFilterFactory.class)
    })

Аннотация для поля имени сделки:

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

Код, который переопределяет анализатор поля имени для использования анализатора запросов

            String[] searchableFields = getSearchableFields();
            if(searchableFields.length > 0) {
                EntityContext entityContext = fullTextEntityManager.getSearchFactory()
                    .buildQueryBuilder().forEntity(this.getClass().getAnnotation(SearchType.class).clazz()).overridesForField(searchableFields[0], "edgeNGram_query");

                for(int i = 1; i < searchableFields.length; i++) {
                    entityContext.overridesForField(searchableFields[i], "edgeNGram_query");
                }
                queryBuilder = entityContext.get();
            }

Последующий вопрос Почему вышеуказанная настройка действительно работает?

1 Ответ

1 голос
/ 27 марта 2019

Ваша проблема здесь в запросе с подстановочными знаками. Запросы с подстановочными знаками не поддерживают токенизацию: они работают только с одиночными токенами. На самом деле они даже не поддерживают нормализацию, поэтому вам пришлось самостоятельно вводить строчные буквы ...

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

См. этот ответ для подробного объяснения.

Если вы используете интеграцию ELasticsearch, вам придется полагаться на хак, чтобы заставить анализатор «только для запросов» работать правильно. Смотри здесь .

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