大家在工作中想必也接觸過Elasticsearch,今天介紹一下es中的嵌套對象及對應的查詢方式。
從考慮一個業務場景開始吧,業務上需要把某些類似的商品聚合成為一個關聯組,需要支持根據某個商品的特征,查詢到它所在的關聯組,es中的存儲結構如下:
{
"memberGoods":[
{
"title":"商品A",
"brand":"a"
},
{
"title":"商品B",
"brand":"b"
}
],
"groupId":"A"
}
那么問題來了,如果memberGoods是一個普通的Object類型,對於下面的查詢條件:
{
"query":{
"bool":{
"must":[
{
"match":{
"title":"商品A"
}
},
{
"match":{
"brand":"b"
}
}
]
}
}
}
上面的數據依然會匹配上,但是商品A的品牌應該是a,而不是b呀,造成這種現象的原因是結構性的JSON文檔會平整成索引內的一個簡單鍵值格式,就像這樣:
{
"memberGoods.title":[
"商品A",
"商品B"
],
"memberGoods.brand":[
"a",
"b"
],
"groupId":"A"
}
顯然,如上的數據損失了同一商品數據之間的關聯性,從而出現了交叉匹配的現象,為解決這一問題,nested Object應運而生,它保留了子文檔數據中的關聯性,如果memberGoods的數據格式被定義為nested,那么每一個nested object 將會作為一個隱藏的單獨文本建立索引。如下:
{
"groupId":"A"
},
{
"memberGoods.title":"商品A",
"memberGoods.brand":"a"
},
{
"memberGoods.title":"商品B",
"memberGoods.brand":"b"
}
通過分開給每個nested object建索引,object內部的字段間的關系就能保持。當執行查詢時,只會匹配’match’同時出現在相同的nested object的結果。
不僅如此,由於nested objects 建索引的方式,在查詢的時候將根文本和nested文檔拼接是很快的,就跟把他們當成一個單獨的文本一樣的快。
nested object作為一個獨立隱藏文檔單獨建索引,因此,我們不能直接查詢它們。取而代之,我們必須使用nested查詢或者nested filter來接觸它們,java語言描述如下:
queryBuilder.must(QueryBuilders.nestedQuery("memberGoods"/** nested 字段*/,
QueryBuilders.matchPhraseQuery("memberGoods.title", "商品A"), ScoreMode.Avg));
其中memberGoods是父字段,memberGoods.title是子字段,以上已有提及,最后的參數ScoreMode.Avg是父文檔匹配分數的設定,(Parent hit's score is the average/max/sum/min of all child scores.)
此外,nested形式的查詢也有一些需要注意的缺點:
1.增加,改變或者刪除一個nested文本,整個文本必須重新建索引。nested文本越多,代價越大。
2.檢索請求會返回整個文本,而不僅是匹配的nested文本。盡管有計划正在執行以能夠支持返回根文本的同時返回最匹配的nested文本,但目前還未實現。
--------------------------------------------
更多詳細內容,請參考es官方文檔:
https://www.elastic.co/guide/en/elasticsearch/guide/master/nested-objects.html
https://www.elastic.co/guide/en/elasticsearch/guide/master/index.html