最近公司的APP使用ES搜尋功能時遇到一個需求——需要搜尋出來的資料中只包含某個商戶下的商品,且這些商品的庫存都不為0。
首先我們搜尋得到的文件格式簡化後如下:
也就是說,此時,我的需求的搜尋條件是:merchant_id=11,且stock不為0。如果按照需求來說,上面截圖裡的這個商品是不符合條件的,也就是不應該被搜尋出來。但是如果按照es一般的filter寫法去寫,filter部分的寫法應該是如下——
"filter":[
{ "terms":
{"sku_list.merchant_id":[11]}
},
{"range":
{"sku_list.stock":{"gte":1}}
}
]
但這種寫法會導致那個商品依然會被搜出來,原因是elasticsearch(lucene)使用的庫沒有內部物件的概念,因此內部物件被扁平化為一個簡單的欄位名稱和值列表。也就是說,上面的商品文件,在es中會被轉換為
{
"id": [ **** ],
"name": [ ****],
"title": [ **** ],
"post": [ **** ] ,
"sku_list.sku_id": [ 100, 101, 102],
"sku_list.stock": [ 2,0,3 ],
"sku_list.merchant_id": [ 10, 11, 12 ],
"sku_list.sale_price": [ 128.00 ]
}
所以sku_list裡的stock跟merchant_id不再具有關聯關係,因為整合後的物件滿足了上面兩個條件,所以可以被搜尋出來。
要解決這個問題,我們需要對es的對映(mapping)進行一些小改動,將sku_list的type改為nested(巢狀資料型別,引用其他地方的一個說法:在內部,巢狀物件將陣列中的每個物件索引為單獨的隱藏文件,這意味著可以獨立於其他物件查詢每個巢狀物件)。nested型別是物件資料型別的專用版本,它允許物件陣列以可以彼此獨立查詢的方式進行索引。
那麼如何將sku_list改為nested型別,又儘可能不影響線上已有的功能呢?
首先,可以檢視當前索引下的所有對映以及對映型別。
curl -X GET "http://domain/my_index/_mapping"(domain是es所在伺服器ip+埠,my_index換成對應的索引名稱)
檢視後發現sku_list對應的索引型別是text。但是es不允許直接修改或刪除一個欄位型別,所以通用的修改欄位型別的解決辦法是——
採用reindex的方法實現,就是建立一個新的mapping,裡面的欄位型別按照新的型別定義,然後使用reindex的方法把原來的資料拷貝到新的index下面
1、建立新的索引my_index_new
curl -X PUT "http://domain/my_index_new?pretty"
2、將索引的預設欄位數調大(預設是1000,一般不需要調,但因為我們的文件比較複雜,欄位數使用超過了1000,所以必須調整)
curl -X PUT "http://domain/my_index_new/_settings" -d '{"index.mapping.total_fields.limit": "3000"}'
3、將sku_list的欄位型別設定為nested(注意這一步一定要再下一步同步資料前完成,不然同步完資料後,sku_list的欄位型別又會被設定成了預設的text,就又無法再更改了)
curl -X POST "http://domain/my_index_new/_mapping" -d '{"properties": {"sku_list": {"type":"nested"}}}'
4、將老資料同步到新資料
curl -X POST "http://domain/_reindex" -d '{ "source": { "index": "my_index" }, "dest": { "index": "my_index_new" }} '
如果原先資料量較大,第三步花的時間會比較長,需要耐心等待其同步完成。
5、刪除原先索引(其實如果業務允許的話,可以直接在業務側將索引改為my_index_new,老的索引就不刪除,放著以防後面出現問題可以及時切換)
curl -X DELETE "http://domain/my_index"
6、設定原索引的別名(如果業務側不方便改es介面處的索引,那隻能通過4、5這種方法來確保原索引名正常使用,我們是直接用新的索引名稱,老的不去動它)
curl -X POST "http://domain/_aliases" -d '{ "actions": [{"add":{"index":"my_index_new","alias":"my_index"}}]} '
如果上述步驟都正常完成,此時再檢視索引的mapping,會發現sku_list已經是我們需要的nested型別了。
此時就可以使用nested的語法結構來構造查詢語句。
{"query":{"bool":{"must":[{"dis_max":{"queries":[]}},{"nested":{"path":"sku_list","query":{"bool":{"must":[{"terms":{"sku_list.merchant_id":[11]}},{"range":{"sku_list.stock":{"gte":1}}}]}}}}],"filter":[]}}}
只展示了nested部分的結構,其他的可以根據實際情況替換。其中,nested裡的path就是我們修改了型別的欄位,另外就是注意nested在filter裡使用貌似會報錯,所以需要寫在must查詢條件裡。
另外可以通過瀏覽器直接訪問domain/_cat/indices?v,看到新的索引的資訊,確認新索引的文件記憶體大小是否跟老的一致。
本作品採用《CC 協議》,轉載必須註明作者和本文連結