wordpress 谷歌西seo優(yōu)化排名
文章目錄
- 0、聲明
- 1、問題描述
- 2、問題剖析
- 2.1 NULL或者空值類型有哪些
- 2.2 案例講解:嘗試檢索值為 `null` 的字段
- 2.3 解決思路
- 3、使用 null_value 的諸多坑(避免生產(chǎn)事故)
- 3.1 null_value 替換的是索引,并不會直接替換源數(shù)據(jù)
- 3.2 不支持 Text 類型
- 3.2 null_value 的值必須可以隱式類型轉(zhuǎn)換為當(dāng)前字段類型
- 3.4 BUG
- 4、如何查詢字段值非空或者不為 null 的文檔?
0、聲明
本文所述問題和解決方案基于 Elasticsearch 7.17.3 版本,具體問題可能會隨著版本的變化有所不同,如有疑問請聯(lián)系作者。
1、問題描述
null 值是個麻煩的問題,在業(yè)務(wù)系統(tǒng)中經(jīng)常有如下場景:
- 檢索值為
null
或''
的文檔(數(shù)據(jù)記錄) - 判斷某字段是否存在
本文主要解決在 ES 中如何處理空只或者 NULL 值,如檢索值為空的文檔,如何存儲空值或 NULL 值等。
2、問題剖析
2.1 NULL或者空值類型有哪些
"NULL"
(字符串,不區(qū)分大小寫)null
' '
(空白符)''
(空值)
2.2 案例講解:嘗試檢索值為 null
的字段
首先添加一個名為 null_value_index
的測試索引,將上述類型的值分別創(chuàng)建一條數(shù)據(jù)出來,然后查看檢索結(jié)果,如下所示:
PUT null_value_index
{"mappings": {"properties": {"null_field": {"type": "keyword"}}}
}PUT null_value_index/_bulk
{"index":{"_id":1}}
{"null_field":null}
{"index":{"_id":2}}
{"null_field":"null"}
{"index":{"_id":3}}
{"null_field":""}
{"index":{"_id":4}}
{"null_field":" "}
{"index":{"_id":5}}
{"null_field":[]}GET null_value_index/_search
1、嘗試檢索值為null
的文檔:
POST null_value_index/_search
{"query": {"term": {"null_field": null}}
}
執(zhí)行結(jié)果如下:
{"error" : {"root_cause" : [{"type" : "illegal_argument_exception","reason" : "field name is null or empty"}],"type" : "illegal_argument_exception","reason" : "field name is null or empty"},"status" : 400
}
發(fā)現(xiàn)不支持直接搜索值為 null
的字段,搜索值為 []
也是一樣
2、那么嘗試搜索其他幾種空值呢?
POST null_value_index/_search
{"query": {"terms": {"null_field": [""," "]}}
}
執(zhí)行結(jié)果:
{"took" : 0,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 0,"relation" : "eq"},"max_score" : null,"hits" : [ ]}
}
結(jié)果:沒有匹配任何文檔,說明不論是檢索''
還是' '
都檢索不到任何文檔
分析:在全文檢索中,空值本來就會被作為停用詞處理,在分詞過程中就會被“干掉”,即便我們使用 term 做精準(zhǔn)查詢,不會被分詞,空值也不會被創(chuàng)建索引,因此無法匹配到任何結(jié)果,這一點不同于關(guān)系數(shù)據(jù)庫。
3、那么搜索"null"
值呢?
POST null_value_index/_search
{"query": {"term": {"null_field": "null"}}
}
查詢結(jié)果:
{"took" : 0,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 0.2876821,"hits" : [{"_index" : "null_value_index","_type" : "_doc","_id" : "2","_score" : 0.2876821,"_source" : {"null_field" : "null"}}]}
}
為什么搜索字符串就有值了呢?很簡單,這就是一個普通的搜索,跟空值沒有任何關(guān)系,這本質(zhì)上跟搜索 "test_value"沒有任何區(qū)別。
2.3 解決思路
那么這有什么意義呢?
其實這給我們提供了一個思路,如果我們想搜索空值字段,只需要在數(shù)據(jù)寫入的時候,把空值字段給他一個默認(rèn)值就行了
ES 為我們提供了一個 null_value
參數(shù),在定義字段的時候,可以聲明在遇到 null
值或其他空值的時候,將其替換為指定的值,
注意: null_value
替換的是分詞后的結(jié)果,源數(shù)據(jù)并不受影響,這一點后面會詳細(xì)講述
代碼示例:
DELETE null_value_index
PUT null_value_index
{"mappings": {"properties": {"null_field": {"type": "keyword","null_value": "NULL"}}}
}
PUT null_value_index/_bulk
{"index":{"_id":1}}
{"null_field":null}
{"index":{"_id":2}}
{"null_field":"NULL"}
{"index":{"_id":3}}
{"null_field":""}
{"index":{"_id":4}}
{"null_field":" "}
{"index":{"_id":5}}
{"null_field":[]}
上述代碼在創(chuàng)建 Mapping 的時候,顯式的聲明 null_value
參數(shù),其值為當(dāng) null_field
字段遇到null
值的時候的替換值,也就是說 null_value
的值配置什么,這個字段原本的null
值就會被替換成什么。
因此上述例子中 _id:1
的數(shù)據(jù)的值就會被替換,而其他數(shù)據(jù)不受到影響,因此當(dāng)執(zhí)行以下查詢時,返回結(jié)果應(yīng)該為 _id: 1
和 _id: 2
兩條結(jié)果。
GET null_value_index/_search
{"query": {"term": {"null_field": "NULL"}}
}
執(zhí)行結(jié)果:
"hits" : [{"_index" : "null_value_index","_type" : "_doc","_id" : "1","_score" : 0.6931471,"_source" : {"null_field" : null}},{"_index" : "null_value_index","_type" : "_doc","_id" : "2","_score" : 0.6931471,"_source" : {"null_field" : "NULL"}}
]
3、使用 null_value 的諸多坑(避免生產(chǎn)事故)
3.1 null_value 替換的是索引,并不會直接替換源數(shù)據(jù)
解釋:當(dāng) null_value 生效發(fā)生替換行為時,其替換的并不是源數(shù)據(jù)(_source_data)而是索引數(shù)據(jù),簡單來說,就是當(dāng)你執(zhí)行 GET null_value_index/_search
時,是看不到任何源數(shù)據(jù)的變化的。
示例:
測試數(shù)據(jù):
DELETE null_value_index
PUT null_value_index
{"mappings": {"properties": {"null_field": {"type": "keyword","null_value": "Elastic"}}}
}PUT null_value_index/_bulk
{"index":{"_id":1}}
{"null_field":null}
執(zhí)行查詢:
GET null_value_index/_search
返回結(jié)果:
"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 1.0,"hits" : [{"_index" : "null_value_index","_type" : "_doc","_id" : "1","_score" : 1.0,"_source" : {"null_field" : null}}]}
可以看到,源數(shù)據(jù)中的 null 并未被直接替換,這是因為 null_value 替換的并不是源數(shù)據(jù),而是索引數(shù)據(jù),也就是說,當(dāng)我們通過 term:"Elastic"
是可以檢索到上面的文檔的,替換的值對我們是不可見的。
3.2 不支持 Text 類型
作為 ES 中最常用的類型,text 類型是不支持設(shè)置 null_value 參數(shù)的,如過添加次參數(shù)會出現(xiàn)以下錯誤:
報錯可以看出:text 類型不支持配置 null_value 參數(shù)
分析原因:推測是因為 text
類型是用于全文檢索,會被分詞,通常使用 match 檢索 text
字段,而此時源數(shù)據(jù)和搜索詞都會被分詞,如果給出了 null_value,ES 就不知道應(yīng)不應(yīng)該給這個 null_value 的值分詞了,null_value 替換的原本就是索引數(shù)據(jù),如果分詞可能會影響搜索結(jié)果的準(zhǔn)確性,使用戶得到意想不到的結(jié)果,但是如果部分詞又違背了 text 類型的設(shè)計理念和規(guī)則,因此選擇了不支持。
引申理解:其實非常建議官方添加對 text 類型對 null_value
類型的支持,因為用戶只需要設(shè)置一個不會被分詞的 null_value 值就可以了,比如"elastic"、"null"
這樣的詞。因為這個問題實在是給廣大 elastic 愛好者帶來了很大的麻煩。
解決方案:鑒于在業(yè)務(wù)場景中,經(jīng)常有 “查詢結(jié)果 不為空 或不為 null
" 這樣的需求,針對此問題,文末將給出解決方案。
3.2 null_value 的值必須可以隱式類型轉(zhuǎn)換為當(dāng)前字段類型
官方的解釋是需要設(shè)置成和當(dāng)前字段相同的類型,原文如下:
注意官方文檔說的必須是 the same data type as the field
,實際上只要是可以隱式類型轉(zhuǎn)換轉(zhuǎn)換就可以,比如字段類型為 long
而 null_value 的配置值為 "1"
或者1
在語法上都是完全沒問題的。
可以看到,不管是創(chuàng)建 Mapping 還是寫入數(shù)據(jù),都是沒有問題的,而且不影響 null_value 的正常功能。
但需要注意的是,如果 type
是 long
類型,那么 null_value
的值給了一個 "elastic"
這樣的值是不行的。
3.4 BUG
請看如下示例:
DELETE null_value_index
PUT null_value_index
{"mappings": {"properties": {"null_field": {"type": "short","null_value": 1}}}
}PUT null_value_index/_bulk
{"index":{"_id":1}}
{"null_field":null}
{"index":{"_id":2}}
{"null_field":""}
{"index":{"_id":3}}
{"null_field":[]}
基于以上數(shù)據(jù),執(zhí)行如下查詢,請各位思考,返回的結(jié)果應(yīng)該是什么
GET null_value_index/_search
{"query": {"term": {"null_field": {"value": 1}}}
}
按照官方對 null_value
的解釋,返回結(jié)果應(yīng)只為 doc2(_id: 1)
的數(shù)據(jù),然而 doc2(_id: 1)
也被召回了,這顯然是不正常的。
注意,""
值是不被 null_value
替換的,這一點當(dāng)我們把字段類型換成 keyword
的時候,就可以得到驗證:
4、如何查詢字段值非空或者不為 null 的文檔?
推薦閱讀: