問題描述
es中存在有一個名為task_data_1的索引,其欄位對映關係如下所示:
{ "task_data_1" : { "mappings" : { "dynamic_templates" : [ { "dates" : { "match_mapping_type" : "date", "mapping" : { "type" : "date" } } }, { "doubles" : { "match_mapping_type" : "double", "mapping" : { "type" : "double" } } }, { "objects" : { "match_mapping_type" : "object", "mapping" : { "type" : "object" } } }, { "longs" : { "match_mapping_type" : "long", "mapping" : { "type" : "integer" } } }, { "strings" : { "match" : "*", "match_mapping_type" : "string", "mapping" : { "type" : "keyword" } } } ], "properties" : { "createUsername" : { "type" : "keyword" }, "data" : { "type" : "nested", "dynamic" : "true", "properties" : { "daterange102110" : { "type" : "date" }, "input18779" : { "type" : "keyword" }, "rate48025" : { "type" : "integer" }, "textarea24212" : { "type" : "keyword" }, "textarea38172" : { "type" : "keyword" }, "timerange47544" : { "type" : "keyword" }, "url" : { "type" : "keyword" } } }, "formId" : { "type" : "long", "store" : true }, "updateUsername" : { "type" : "keyword" } } } } }
透過createUsername、updateUsername、formId等欄位可以正常進行過濾查詢功能,但是data這個map中的所有欄位都無法正常進行過濾查詢功能。
解決過程
起初,我是直接用map中對應的欄位名進行過濾查詢,編寫的程式碼生成的DSL如下所示:
POST task_data_1/_search { "from": 0, "size": 10, "query": { "bool": { "must": [ { "term": { "input18779": { "value": "3213", "boost": 1 } } } ], "adjust_pure_negative": true, "boost": 1 } }, "sort": [ { "createTime": { "order": "desc" } } ], "track_total_hits": 2147483647 }
這樣子的查詢語法有很明顯的問題,因為input32768這個欄位是巢狀在data這個map中的,直接使用input32768這個欄位名是無法查詢到對應資料的。
發現問題後,我修改了程式碼邏輯,而後生成的DSL如下所示:
POST task_data_1/_search { "from": 0, "size": 10, "query": { "bool": { "must": [ { "term": { "data.input18779": { "value": "3213", "boost": 1 } } } ], "adjust_pure_negative": true, "boost": 1 } }, "sort": [ { "createTime": { "order": "desc" } } ], "track_total_hits": 2147483647 }
這樣子的查詢語法,從表現上看是沒有任何問題的,但依然查不出資料。
後來我到kibana查詢了該索引的欄位對映關係,就是上文中的這一段json資料:
{ "task_data_1" : { "mappings" : { "dynamic_templates" : [ { "dates" : { "match_mapping_type" : "date", "mapping" : { "type" : "date" } } }, { "doubles" : { "match_mapping_type" : "double", "mapping" : { "type" : "double" } } }, { "objects" : { "match_mapping_type" : "object", "mapping" : { "type" : "object" } } }, { "longs" : { "match_mapping_type" : "long", "mapping" : { "type" : "integer" } } }, { "strings" : { "match" : "*", "match_mapping_type" : "string", "mapping" : { "type" : "keyword" } } } ], "properties" : { "createUsername" : { "type" : "keyword" }, "data" : { "type" : "nested", "dynamic" : "true", "properties" : { "daterange102110" : { "type" : "date" }, "input18779" : { "type" : "keyword" }, "rate48025" : { "type" : "integer" }, "textarea24212" : { "type" : "keyword" }, "textarea38172" : { "type" : "keyword" }, "timerange47544" : { "type" : "keyword" }, "url" : { "type" : "keyword" } } }, "formId" : { "type" : "long", "store" : true }, "updateUsername" : { "type" : "keyword" } } } } }
從這段json資料中可以發現,data這個map的型別是nested。
查資料後得知,在 Elasticsearch 中,"nested" 型別是一種特殊的資料型別,用於處理巢狀檔案(nested documents)。
針對這種型別的資料,需要使用 Nested Query 結合 Match Query 或 Term Query 等查詢型別來搜尋巢狀欄位。
因此,我對程式碼做出類似如下整改:
queryBuilder.must(QueryBuilders.nestedQuery("data", QueryBuilders.termQuery(queryFieldName, item.getFilterValue()), ScoreMode.None));
主要是使用到了 Nested Query,之後生成的DSL如下所示:
POST task_data_1/_search { "from": 0, "size": 10, "query": { "bool": { "must": [ { "nested": { "query": { "term": { "data.input18779": { "value": "3213", "boost": 1 } } }, "path": "data", "ignore_unmapped": false, "score_mode": "none", "boost": 1 } } ], "adjust_pure_negative": true, "boost": 1 } }, "sort": [ { "createTime": { "order": "desc" } } ], "track_total_hits": 2147483647 }
此時,對應的資料結果就能夠被查詢出來了。
總結
在 Elasticsearch 中,"nested" 型別是一種特殊的資料型別,用於處理巢狀檔案(nested documents)。
對於 "nested" 型別的欄位,它包含的子欄位(metadata)在查詢時需要使用特定的巢狀查詢來進行搜尋操作,簡單的查詢無法直接搜尋到巢狀欄位的內容。
以我提供的資料對映為例,資料中的 "data" map 中的每個欄位(如 "daterange102110"、 "input18779" 等)都無法直接進行搜尋,因為 Elasticsearch 預設不會對巢狀欄位進行索引。
如果你希望能夠對巢狀欄位進行搜尋,你需要使用巢狀查詢。例如,可以使用 Nested Query 結合 Match Query 或 Term Query 等查詢型別來搜尋巢狀欄位。