问题背景

社区的一个伙伴想对一个 integer 的字段类型添加一个 keyword 类型的子字段,然后进行精确匹配的查询优化,提高查询的速度。

整个索引数据量不大,并不想进行 reindex 这样的复杂操作,就想到了使用 update_by_query 的存量数据更新。

所以我们测试了下面这套方案,在设置完字段的子字段后,利用 set processor 来对这个子字段进行 update_by_query

操作记录:

# 测试索引
PUT /test
{"mappings": {"properties": {"status": {"type": "integer"}}}
}
# 测试数据
POST /test/_bulk
{"index":{}}
{"status":404}
{"index":{}}
{"status":500}GET test/_search# 添加子字段
PUT test/_mapping
{"properties": {"status": {"type": "integer","fields": {"keyword": {"type": "keyword"}}}}
}GET test/_search#创建管道pipeline.实现更新逻辑
PUT _ingest/pipeline/copy_status_to_keyword
{"description": "resets the value of status and subfields","processors": [{"set": {"field": "status","value": "{{{status}}}"}}]
}#update 执行
POST test/_update_by_query?pipeline=copy_status_to_keyword
{"query": {"bool": {"must_not": {"exists": {"field": "status.keyword"}},"must": {"exists": {"field": "status"}}}}
}GET test/_search
{"query": {"exists": {"field": "status.keyword"}}
}# 返回结果"hits": [{"_index": "test_set","_type": "_doc","_id": "G7zHNpUBLvnTvXTpVIC4","_score": 1,"_source": {"status": "404"}},{"_index": "test_set","_type": "_doc","_id": "HLzHNpUBLvnTvXTpVIC4","_score": 1,"_source": {"status": "500"}}]

测试检查了一下,status.keyword 可以被 search,可以满足我们的预期要求。

但是,小伙伴到正式上线的时候却发生了问题。应用程序读取发现 _source 中 status 的类型变了,开始报错字段类型不符合。

# 写入的时候"hits": [{"_index": "test","_type": "_doc","_id": "2ry5NpUBLvnTvXTp1F5z","_score": 1,"_source": {"status": 404 # 这里还是 integer 类型}},{"_index": "test","_type": "_doc","_id": "27y5NpUBLvnTvXTp1F5z","_score": 1,"_source": {"status": 500}}]# update 完成后"hits": [{"_index": "test","_type": "_doc","_id": "2ry5NpUBLvnTvXTp1F5z","_score": 1,"_source": {"status": "404" # 字段内容添加上了引号,成为了 string 类型}},{"_index": "test","_type": "_doc","_id": "27y5NpUBLvnTvXTp1F5z","_score": 1,"_source": {"status": "500"}}]

解决方案

还好小伙伴那边有数据主备库,赶紧做了切换。然后开始对已有的数据进行修复。

最终商定了下面两个方案进行 fix。

  1. 用 script 保持数据类型重写
POST test/_update_by_query
{
"script": {"source": """if (ctx._source.status instanceof String) {ctx._source.status = Integer.parseInt(ctx._source.status);}""","lang": "painless"}
}
  1. 查询结果读取 docvalue 而不是 source。这个方案可以绕过这个问题,但是需要改动应用程序。
GET test/_search
{"_source": false,"docvalue_fields": ["status"]
}# 返回"hits": [{"_index": "test","_type": "_doc","_id": "wLy-NpUBLvnTvXTpRGvw","_score": 1,"fields": {"status": [404]}},{"_index": "test","_type": "_doc","_id": "wby-NpUBLvnTvXTpRGvw","_score": 1,"fields": {"status": [500]}}]

问题分析

好了,现在我们回过头来分析一下之前方案出现的问题,用 set proceesor 为什么会导致 source 内的字段类型从 int 变成 string 呢?

因为 script 脚本写法能够成功,而 set 会失败,我们从 set 的使用入手,去看看代码里是不是有什么线索?

set processor 问题的细节

让我们深入分析值类型转换的核心代码路径:

// SetProcessor.java
document.setFieldValue(field, value, ignoreEmptyValue);

这里的 value 参数类型为 ValueSource,

// SetProcessor.Factory.create()
Object value = ConfigurationUtils.readObject(TYPE, processorTag, config, "value");
ValueSource valueSource = ValueSource.wrap(value, scriptService);

其核心实现逻辑在接口 ValueSource.java 中:

// ValueSource.java 关键方法 59行
public static ValueSource wrap(Object value, ScriptService scriptService) {......} else if (value instanceof String) {// This check is here because the DEFAULT_TEMPLATE_LANG(mustache) is not// installed for use by REST tests. `value` will not be// modified if templating is not availableif (scriptService.isLangSupported(DEFAULT_TEMPLATE_LANG) && ((String) value).contains("{{")) {Script script = new Script(ScriptType.INLINE, DEFAULT_TEMPLATE_LANG, (String) value, Collections.emptyMap());return new TemplatedValue(scriptService.compile(script, TemplateScript.CONTEXT));} else {return new ObjectValue(value);}}......
}

当配置中的 value 值为"{{{status}}}"字符串时,创建 TemplateValue 实例。

这里 “{{{status}}}” 的写法属于 Mustache 语法,一种轻量级的模板引擎语法。ES 在 search template 中会主要应用,在 set processor 也用了 Mustache 进行字段内容的引用。

// 在 ValueSource.java 内部
private static class TemplateValue extends ValueSource {private final TemplateScript.Factory template;@Overridepublic Object copyAndResolve(Map<String, Object> model) {return template.newInstance(model).execute();}}

继续看抽象类 TemplateScript.java#execute() ,这个方法在定义的时候已经明确声明返回的是 string

    /** Run a template and return the resulting string, encoded in utf8 bytes. */public abstract String execute();

而实现的子类则很明显是 MustacheExecutableScript.execute(),即 Mustache 语法引擎的实现。

private class MustacheExecutableScript extends TemplateScript {......@Overridepublic String execute() {final StringWriter writer = new StringWriter();try {// crazy reflection hereSpecialPermission.check();AccessController.doPrivileged((PrivilegedAction<Void>) () -> {template.execute(writer, params);return null;});} catch (Exception e) {logger.error((Supplier<?>) () -> new ParameterizedMessage("Error running {}", template), e);throw new GeneralScriptException("Error running " + template, e);}return writer.toString();}......

这里也可以印证了字段内容类型被强制转为字符串

类型转换过程

deepseek 帮我总结的类型转换过程如下:

sequenceDiagramparticipant SetProcessorparticipant ValueSourceparticipant TemplateValueparticipant TemplateScriptparticipant MustacheEngineSetProcessor->>ValueSource: wrap("{{status}}")ValueSource->>TemplateValue: 创建实例SetProcessor->>TemplateValue: copyAndResolve(doc)TemplateValue->>TemplateScript: newInstance(doc)TemplateScript->>MustacheEngine: compile("{{status}}")MustacheEngine-->>TemplateScript: 返回Mustache编译后模板实现TemplateValue->>TemplateScript: execute()MustacheEngine-->>TemplateScript: 在这里将结果渲染为StringTemplateValue-->>SetProcessor: 返回"200"(String)

小结

所以,这里 source 内字段类型被转变的原因,是 ES 对 set processor 使用 Mustache 语法产生的结果值进行了特殊处理,将内容都处理成了 string。

假设这次使用 set 去处理的值都是一个默认值 404 ,则不会出现这个问题

PUT _ingest/pipeline/copy_status_to_keyword_1
{"description": "resets the value of status and subfields","processors": [{"set": {"field": "status","value": 404}}]
}#update 执行方式
POST test/_update_by_query?pipeline=copy_status_to_keyword_1
{"query": {"bool": {"must_not": {"exists": {"field": "status.keyword"}},"must": {"exists": {"field": "status"}}}}
}GET test/_search
# 返回内容{"_index": "test","_type": "_doc","_id": "tN0QRZUBLvnTvXTpJMTI","_score": 1,"_source": {"status": 404}},{"_index": "test","_type": "_doc","_id": "td0QRZUBLvnTvXTpJMTI","_score": 1,"_source": {"status": 404}}

那 ES 在 set 这段 Mustache 语法的处理里,使用 string 作为返回值,大家觉得合理么?如果需要保留原来的数据内容类型,不修改 TemplateScript.java#execute()这个方法可以实现么?

作者:金多安,极限科技(INFINI Labs)搜索运维专家,Elastic 认证专家,搜索客社区日报责任编辑。一直从事与搜索运维相关的工作,日常会去挖掘 ES / Lucene 方向的搜索技术原理,保持搜索相关技术发展的关注。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92400.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92400.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/92400.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何彻底搞定 PyCharm 中 pip install 报错 ModuleNotFoundError: No module named ‘requests’ 的问题

如何彻底搞定 PyCharm 中 pip install 报错 ModuleNotFoundError: No module named ‘requests’ 的问题 在使用 PyCharm 开发 Python 项目时&#xff0c;ModuleNotFoundError: No module named requests 是一个常见但令人头疼的问题。本篇博文将从环境配置、原因分析到多种解…

powerquery如何实现表的拼接主键

在做表过程中&#xff0c;有时候没有基表&#xff0c;这个时候就要构造完整的主键&#xff0c;这样才可以使之后匹配的数据不会因为主键不全而丢失数据 我的处理方法是吧多个表的主键拼在一起然后去重&#xff0c;构造一个单单之后之间的表作为基表去匹配数据 所以就哟啊用到自…

今日Github热门仓库推荐 第八期

今日Github热门仓库推荐2025-07-22 如果让AI分别扮演 后端开发人员和前端开发人员&#xff0c;然后看看他们分别对github每天的trending仓库感兴趣的有哪些&#xff0c;并且给出他感兴趣的理由&#xff0c;那会发生什么呢&#xff1f; 本内容通过Python AI生成&#xff0c;项…

Dify-13: 文本生成API端点

本文档提供了有关 Dify 中与文本生成相关的 API 端点的全面信息。文本生成 API 支持无会话持久性的单次请求文本生成&#xff0c;使其适用于翻译、摘要、文章写作等非对话式人工智能应用场景。 概述 文本生成 API 端点允许开发人员将 Dify 的文本生成功能集成到不需要维护对话上…

Leetcode 3620. Network Recovery Pathways

Leetcode 3620. Network Recovery Pathways 1. 解题思路2. 代码实现 题目链接&#xff1a;3620. Network Recovery Pathways 1. 解题思路 这一题我最开始想的是遍历一下所有的网络路径&#xff0c;不过遇到了超时的情况。因此后来调整了一下处理思路&#xff0c;使用二分法的…

链路备份技术(链路聚合、RSTP)

一、链路聚合&#xff01;链路备份技术之一-----链路聚合&#xff08;Link Aggregation&#xff09;被视为链路备份技术&#xff0c;核心原因在于它能通过多条物理链路的捆绑&#xff0c;实现 “一条链路故障时&#xff0c;其他链路自动接管流量” 的冗余备份效果&#xff0c;同…

PyTorch新手实操 安装

PyTorch简介 PyTorch 是一个基于 Python 的开源深度学习框架&#xff0c;由 Meta AI&#xff08;原 Facebook AI&#xff09;主导开发&#xff0c;以动态计算图&#xff08;Define-by-Run&#xff09;为核心&#xff0c;支持灵活构建和训练神经网络模型。其设计理念高度契合科…

Element Plus Table 组件扩展:表尾合计功能详解

前言在现代数据驱动的社会中&#xff0c;数据分析和统计成为了非常重要的任务。为了更有效地分析数据和展示统计结果&#xff0c;前端开发人员可以使用Vue框架和Element Plus组件库来实现数据的统计和分析功能。以下是一个关于如何在 Element Plus 的 el-table 组件中实现行汇总…

神经网络 非线性激活层 正则化层 线性层

神经网络 非线性激活层 作用&#xff1a;增强模型的非线性拟合能力 非线性激活层网络&#xff1a; class activateNet(nn.Module):def __init__(self):super(activateNet,self).__init__()self.relu nn.ReLU()self.sigmoid nn.Sigmoid()def forward(self,input):#output sel…

【Vue进阶学习笔记】组件通信专题精讲

目录前言props 父传子原理说明使用场景代码示例父组件 PropsTest.vue子组件 Child.vue自定义事件 $emit 子传父原理说明使用场景代码示例父组件 EventTest.vue子组件 Event2.vueEvent Bus 兄弟/跨层通信原理说明使用场景代码示例事件总线 bus/index.ts兄弟组件通信示例Child2.v…

【PTA数据结构 | C语言版】求最小生成树的Prim算法

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 请编写程序&#xff0c;实现在带权的无向图中求最小生成树的 Prim 算法。 注意&#xff1a;当多个待收录顶点到当前点集的距离等长时&#xff0c;按编号升序进行收录。 输入格式&#xff1a; 输入首…

【加解密与C】Rot系列(四)RotSpecial

RotSpecial 函数解析RotSpecial 是一个自定义函数&#xff0c;通常用于处理特定的旋转操作&#xff0c;尤其在图形变换或数据处理中。该函数可能涉及欧拉角、四元数或其他旋转表示方法&#xff0c;具体行为取决于实现上下文。以下是关于该函数的通用解释和可能的使用方法&#…

【机器学习深度学习】LLaMAFactory中的精度训练选择——bf16、fp16、fp32与pure_bf16深度解析

目录 前言 一、 为什么精度如此重要&#xff1f;—— 内存、速度与稳定性的三角博弈 二、 四大精度/模式详解&#xff1a; bf16, fp16, fp32, pure_bf16 三、 关键特性对比表 ▲四大计算类型核心对比表 ▲ 显存占用对比示例&#xff08;175B参数模型&#xff09; ▲LLa…

C# 基于halcon的视觉工作流-章21-点查找

C# 基于halcon的视觉工作流-章21-点查找 本章目标&#xff1a; 一、检测显著点&#xff1b; 二、Harris检测兴趣点&#xff1b; 三、Harris二项式检测兴趣点&#xff1b; 四、Sojka运算符检测角点&#xff1b; 五、Lepetit算子检测兴趣点&#xff1b;一、检测显著点 halcon算子…

(11)机器学习小白入门YOLOv:YOLOv8-cls epochs与数据量的关系

YOLOv8-cls epochs与数据量的关系 (1)机器学习小白入门YOLOv &#xff1a;从概念到实践 (2)机器学习小白入门 YOLOv&#xff1a;从模块优化到工程部署 (3)机器学习小白入门 YOLOv&#xff1a; 解锁图片分类新技能 (4)机器学习小白入门YOLOv &#xff1a;图片标注实操手册 (5)机…

Grafana | 如何将 11.x 升级快速到最新 12.x 版本?

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路 ]&#x1f4e2; 大家好&#xff0c;我是 WeiyiGeek&#xff0c;一名深耕安全运维开发&#xff08;SecOpsDev&#xff09;领域的技术从业者&#xff0c;致力于探索DevOps与安全的融合&#xff08;Dev…

Dubbo + Spring Boot + Zookeeper 快速搭建分布式服务

Dubbo Spring Boot Zookeeper 快速搭建分布式服务 本文将详细介绍如何基于 Dubbo、Spring Boot 和 Zookeeper 快速搭建一个简单的分布式服务调用场景&#xff0c;包含服务提供者&#xff08;Provider&#xff09;、服务消费者&#xff08;Consumer&#xff09;及公共接口&…

五分钟掌握 TDengine 数据文件的工作原理

小 T 导读&#xff1a;今天我们来探讨一下——TDengine中的时序数据到底是如何存储的&#xff1f; 在上一期的文章《五分钟掌握 TDengine 时序数据的保留策略》中&#xff0c;我们知道了TDengine是如何按照时间段对数据进行分区来管理数据的。 接下来&#xff0c;我们和大家一起…

Python爬虫实战:研究http-parser库相关技术

一、研究背景与意义 在当今数字化时代,网络数据蕴含着巨大的价值。从商业决策、学术研究到社会治理,对海量网络信息的有效采集与分析至关重要。网络爬虫作为数据获取的核心工具,其性能与稳定性直接影响数据质量。然而,随着互联网技术的发展,网站反爬机制不断升级,传统爬…

Go语言实战案例-批量重命名文件

在《Go语言100个实战案例》中的 文件与IO操作篇 - 案例17&#xff1a;批量重命名文件 的完整内容&#xff0c;适合初学者实践如何使用 Go 操作文件系统并批量处理文件名。&#x1f3af; 案例目标实现一个小工具&#xff0c;能够批量重命名指定目录下的所有文件&#xff0c;例如…