#hibernate #elasticsearch #hibernate-search
#переход в режим гибернации #elasticsearch #переход в режим гибернации-поиск
Вопрос:
У нас есть следующее требование
В рамках поиска пользователь может выполнять поиск по дате рождения человека. Вообще говоря, это работает нормально, пользователь выбирает из выбора даты во внешнем интерфейсе, и поиск работает так, как ожидалось.
Однако теперь у нас есть требование, при котором пользователь может не знать точную дату рождения, например, он может знать только год рождения.
Что я пробовал
@Basic
@Field
@Field(name = "dob_string", bridge = @FieldBridge(impl = CustomDateStringBridge.class)
,analyzer = @Analyzer(definition = "dob_string_analyzer"))
@Column(name = "date_of_birth")
private Date dateOfBirth;
Класс CustomDateStringBridge просто возвращает дату в виде строки, например 19780418, которая работает должным образом.
Проблема заключается в том, что мы пытаемся выполнить запрос в поле dob_string
У нас есть следующее как часть общего запроса
partialDOB = DIGIT_ONLY_PATTERN.matcher(partialDOB).replaceAll("");
bool.must(queryBuilder.keyword()
.wildcard()
.onField("datesOfBirth.dob_string")
.ignoreFieldBridge()
.ignoreAnalyzer()
.matching("*" partialDOB "*")
.createQuery());
Однако это приводит к следующей ошибке
"type": "parse_exception",
"reason": "failed to parse date field [*1979*] with format [
strict_date_optional_time||epoch_millis]"
Я пробовал без ignoreAnalyzer и ignoreFieldBridge, но в итоге получаю разные ошибки
Просто интересно, возможно ли выполнить этот тип поиска по шаблону по дате? И если у кого-нибудь есть какие-либо идеи о том, как это сделать.
Спасибо
Комментарии:
1. У меня была аналогичная проблема (у меня был пользовательский ввод либо год, либо месяц год), но мой подход заключался в том, чтобы проанализировать ввод в приложении, а затем просто использовать функции sql
month()
иyear()
Ответ №1:
Прежде всего, я бы рекомендовал другой подход, потому что запросы с подстановочными знаками, особенно с ведущим подстановочным знаком, могут работать очень плохо.
Вместо этого сохраните поле даты и воспользуйтесь числовыми запросами.
- Удалите свой мост, чтобы дата оставалась датой
- Для поиска дат в пределах определенного года, месяца или дня полагайтесь на запросы диапазона.
Например, для поиска заданного года:
// Input
int yearAsInteger = ...;
// Replace this with the user timezone if your Date instances
// are created for a user timezone different from the system default
ZoneId timezone = ZoneId.systemDefault();
Year year = Year.of( yearAsInteger );
Date startOfYear = Date.from( year.atDay( 1 ).atStartOfDay( timezone ).toInstant() );
Date startOfNextYear = Date.from( year.plusYear( 1L ).atDay( 1 ).atStartOfDay( timezone ).toInstant() );
bool.must(queryBuilder.range()
.onField("datesOfBirth.dateOfBirth")
.from(startOfYear)
.to(startOfNextYear).excludeLimit()
.createQuery());
Теперь, если вы действительно хотите использовать строковое поле… Проблема в вашем сопоставлении Elasticsearch. Поле dob_string
зарегистрировано в сопоставлении Elasticsearch как поле «дата», тогда как вы хотите, чтобы оно было строкой.
Вы должны быть в состоянии сообщить Hibernate Search, что это строковое поле, внедрив MetadataProvidingFieldBridge
его в свой мост и указав тип поля таким образом:
@Override
public void configureFieldMetadata(String name, FieldMetadataBuilder builder) {
builder.field( name, FieldType.STRING );
}
Не забудьте удалить и заново создать свои индексы Elasticsearch после этого изменения.
Кроме того, вы также можете перейти к поиску в режиме гибернации 6. Это бета-версия, но она стабильна, приближается к выпуску, и поддержка Elasticsearch там намного лучше (больше не экспериментальная). Однако API отличается, поэтому, если у вас уже есть расширенная кодовая база, потребуется значительная работа по миграции. Я пишу руководство по миграции, пока мы говорим.