ES 踩坑記:Set Processor 字段更新引發的 _source 污染

問題背景

社區的一個伙伴想對一個 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;例如…