使用 Hibernate Search 进行部分日期匹配
Partial Date Match with Hibernate Search
我们有以下需求
作为搜索的一部分,用户可以搜索某人的出生日期。一般来说,用户从前端的日期选择器中选择时效果很好,并且搜索按预期工作。
但是我们现在有一个要求,即用户可能不知道确切的出生日期,例如他们可能只知道出生年份。
我试过的
@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 class 只是 returns 日期作为字符串,例如 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,但最终出现了不同的错误
只是想知道是否可以在日期上进行这种类型的通配符搜索?如果有人知道如何这样做。
谢谢
首先,我推荐一种不同的方法,因为通配符查询,尤其是带有前导通配符的查询,性能可能非常差。
相反,保留一个日期字段,并利用数字查询。
- 删除你的桥,这样日期就保持日期
- 依靠 range queries 查找特定年、月或日内的日期。
例如,要搜索给定年份:
// 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 的映射中注册为 'date' 字段,而您希望它是一个字符串。
您应该能够告诉 Hibernate Search 它是一个字符串字段,方法是在您的桥中实现 MetadataProvidingFieldBridge
,并以这种方式提供字段类型:
@Override
public void configureFieldMetadata(String name, FieldMetadataBuilder builder) {
builder.field( name, FieldType.STRING );
}
不要忘记在此更改后删除并 re-create 您的 Elasticsearch 索引。
或者,您也可以移动到 Hibernate Search 6。它处于测试阶段,但它很稳定,即将发布,并且那里的 Elasticsearch 支持要好得多(不再是实验性的)。但是,API 不同,因此如果您已经拥有扩展代码库,则需要进行大量迁移工作。我正在写迁移指南。
我们有以下需求
作为搜索的一部分,用户可以搜索某人的出生日期。一般来说,用户从前端的日期选择器中选择时效果很好,并且搜索按预期工作。
但是我们现在有一个要求,即用户可能不知道确切的出生日期,例如他们可能只知道出生年份。
我试过的
@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 class 只是 returns 日期作为字符串,例如 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,但最终出现了不同的错误
只是想知道是否可以在日期上进行这种类型的通配符搜索?如果有人知道如何这样做。
谢谢
首先,我推荐一种不同的方法,因为通配符查询,尤其是带有前导通配符的查询,性能可能非常差。
相反,保留一个日期字段,并利用数字查询。
- 删除你的桥,这样日期就保持日期
- 依靠 range queries 查找特定年、月或日内的日期。
例如,要搜索给定年份:
// 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 的映射中注册为 'date' 字段,而您希望它是一个字符串。
您应该能够告诉 Hibernate Search 它是一个字符串字段,方法是在您的桥中实现 MetadataProvidingFieldBridge
,并以这种方式提供字段类型:
@Override
public void configureFieldMetadata(String name, FieldMetadataBuilder builder) {
builder.field( name, FieldType.STRING );
}
不要忘记在此更改后删除并 re-create 您的 Elasticsearch 索引。
或者,您也可以移动到 Hibernate Search 6。它处于测试阶段,但它很稳定,即将发布,并且那里的 Elasticsearch 支持要好得多(不再是实验性的)。但是,API 不同,因此如果您已经拥有扩展代码库,则需要进行大量迁移工作。我正在写迁移指南。