ElasticSearch系列:利用runtime field实现日期字符串实现日期范围查询
在Elasticsearch中,如果你有一个时间字符串字段,并且你希望在查询时将其转换为date
类型以便进行日期范围查询或其他日期相关的操作,你可以使用runtime_fields
来实现这一转换。不过,与转换为UNIX时间戳不同,Elasticsearch的runtime_fields
并不直接支持将字符串转换为date
类型的字段,因为date
类型通常需要在索引时就被正确映射。
然而,你可以通过一种间接的方式来实现这一需求:在查询时,使用脚本将时间字符串解析为日期,并以某种形式(如UNIX时间戳或ISO 8601格式的字符串)返回,然后在查询逻辑中使用这个解析后的日期。虽然这不会改变原始字段的映射,但它允许你在查询时以日期的方式处理这个字段。
那该如何在runtime_fields
中使用脚本将时间字符串解析为ISO 8601格式的日期字符串,并在查询中使用这个日期呢?以 test_index索引为例。
put test_index
{
"mappings": {
"properties": {
"id": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"num": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"t0": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"t2": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"t3": {
"type": "long"
}
}
}
}
PUT test_index/_doc/index_1
{
"id": "1",
"name": "w1",
"num": "9",
"t0": "2024-12-17 14:04:10",
"t2": "2024-12-17 14:04:59",
"t3": 1734418709
}
PUT test_index/_doc/index_2
{
"id": "2",
"name": "w2",
"num": "3",
"t0": "2024-12-17 14:24:10",
"t2": "2024-12-17 14:24:59",
"t3": 1734420689
}
PUT test_index/_doc/index_3
{
"id": "3",
"name": "w3",
"num": "9",
"t0": "2024-12-17 14:28:10",
"t2": "2024-12-17 14:28:59",
"t3": 1734420721
}
PUT test_index/_doc/index_4
{
"id": "4",
"name": "w4",
"num": "23",
"t0": "2024-12-17 14:32:10",
"t2": "2024-12-17 14:32:59",
"t3": 1734420750
}
从上面数据看,t2这个字段存储的日期是字符串类型的,如果想对这个t2这个日期进行范围查询呢?
GET /test_index/_search
{
"fields" : ["*"],
"query": {
"range": {
"t2": {
"gte": "2024-12-17T14:24:58Z",
"lte": "2024-12-17T15:29:58Z"
}
}
}
}
这时就用到了runtime_mappings。
GET /test_index/_search
{
"fields" : ["*"],
"runtime_mappings": {
"t2_runtime": {
"type": "keyword", // 使用keyword类型存储解析后的日期字符串
"script": {
"lang": "painless",
"source": """
String dateStr = params._source.t2;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse(dateStr, formatter);
// 转换为ISO 8601格式并输出
String isoDate = dateTime.atZone(ZoneId.of("UTC")).toInstant().toString();
emit(isoDate);
"""
}
}
},
"query": {
"range": {
"t2_runtime": {
"gte": "2024-12-17T14:24:58Z",
"lte": "2024-12-17T15:29:58Z"
}
}
}
}
在这个示例中:
- t2是索引中存储时间字符串的字段名。
t2_runtime
是定义的runtime_field
,它将存储解析后的ISO 8601格式的日期字符串。- 脚本使用Painless语言,并且使用Java的
DateTimeFormatter
和LocalDateTime
类来解析时间字符串,并将其转换为ISO 8601格式的字符串。 - 查询部分使用
range
查询来查找特定日期范围内的文档。 - 注意时区
请注意,这个示例中的日期格式("yyyy-MM-dd HH:mm:ss"
)和时区(UTC
)应该根据实际数据进行调整。如果时间字符串格式不同,你需要相应地修改DateTimeFormatter
的模式。另外,由于runtime_fields
是在查询时动态计算的,因此它们可能会影响查询性能,特别是在大数据集上。
如果发现性能受到影响,或者需要更复杂的日期处理逻辑(比如时区转换、不同的日期格式等),就需要考虑在索引数据之前就将时间字符串转换为正确的date
类型,并在索引映射中正确配置这个字段。
未完待续。。。