【发布时间】:2020-11-27 10:54:43
【问题描述】:
我想在现在映射到 text 的 date 字段上进行 Range 搜索。问题是那个领域已经有数据了。所以删除索引并重新创建一个新的index 以将字段映射为date 不是一个好主意。所以我想在该字段上应用一个自定义函数来进行Range 搜索。这可能吗?
【问题讨论】:
我想在现在映射到 text 的 date 字段上进行 Range 搜索。问题是那个领域已经有数据了。所以删除索引并重新创建一个新的index 以将字段映射为date 不是一个好主意。所以我想在该字段上应用一个自定义函数来进行Range 搜索。这可能吗?
【问题讨论】:
选项 1:无需删除和重新创建整个索引即可更新文档。
如果为索引启用了存储,您可以创建具有正确数据类型的子字段。例如如果现有字段名称为dateText,那么您可以按如下方式更新索引映射,并利用查询更新来重新索引所有文档。
PUT myindex/_mapping
{
"properties": {
"datetext": {
"type": "text",
"fields": {
"dateField": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss" //<--- change this as per the data
}
}
}
}
}
更新映射后使用update by query。
POST myindex/_update_by_query?conflicts=proceed
完成上述操作后,您可以在dateText.dateField 上使用范围查询。
选项 2:您可以使用 script query 处理文本到日期的转换并应用范围逻辑。
【讨论】:
当然可以使用脚本查询,但您的文本字段要么需要设置fielddata: true,要么是.keyword 类型(例如multifield),脚本才能访问字段值。
处理完之后,假设您的文本字段 dateFieldAsText 包含以秒为单位的纪元时间戳,并且您希望使用人类可读的日期范围过滤 gte & lte。然后我们可以将所有内容解析为毫秒,然后进行简单的比较:
{
"query": {
"script": {
"script": {
"source": """
def doc_ts_milli = Integer.parseInt(doc['dateFieldAsText'].value) * 1000L;
def df = new SimpleDateFormat("yyyy/MM/dd");
def gte_ts = df.parse(params.gte).getTime();
def lte_ts = df.parse(params.lte).getTime();
return doc_ts_milli >= gte_ts && doc_ts_milli <= lte_ts
""",
"params": {
"gte": "2020/01/01",
"lte": "2021/01/01"
}
}
}
}
}
这样做的挑战是脚本在每个查询中都会重新初始化(除非they're stored),因此速度很慢。因此,最好的方法是使用新的日期字段更新映射,然后将_reindex 调用到update all docs。无需删除索引。
现在这种方法的问题是它不会影响任何新的传入文档。在这种情况下,您可以修改您的摄取流程以也包含该新日期字段(因此您将有两个字段,由于遗留原因具有相同的值),或者您建立一个摄取管道来为您执行此操作(-> 没有摄取流程调整需要)。这是such a pipeline 的一个示例,尽管其目的略有不同。原理是一样的。
【讨论】: