Я пытаюсь выполнить поиск в спящем режиме, чтобы он поддерживал поиск как по токену, так и без него (простите, если я здесь использую неправильный термин).Пример:
У меня есть список сущностей следующего типа.
@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();
}
Последующий вопрос Почему вышеуказанная настройка действительно работает?