elasticsearch陣列型別建立索引的一種應用場景
elasticsearch有各種core-type,另外還有各種複雜結構,比如nested,child-parent等。
array是一種常用的型別。在es中array是預設支援的,並不存在單獨的一個type=array。當你插入資料帶有“[]”的時候,這個field就成為array。
一般建立索引是採用bulk+prepareindex的方式完成的,檢索出需要建立索引的資料,然後index。陣列的話就帶上方括號。很方面。
但是現實專案中由於各種資料模型的限制並不能直接運用上面簡易的方式進行。下面是一種應用場景:
1:一行關係型資料庫中的資料被打散了,各個field知道屬於那一個id(顯然這個id不能由es自動生成,而是規定好的)
2:一行資料也不是順序過來的,欄位也是無序過來的。
3:可以從一張單獨的表中獲取所有id。
現在要把以前的一行記錄完整的索引起來,es中的id就採用1中描述的id,各欄位作為es的field。
顯然最簡單的索引方法不適用與這個應用場景,但是採用bulk的方式是肯定的,要不然效率太低。
首先想到的是es提供的partital-update的方法,用setDoc+upsert來實現,看起來沒什麼問題,但是如果是array型別,問題出現了:
“The update API also support passing a partial document, which will be merged into the existing document (simple recursive merge, inner merging of objects, replacing core "keys/values" and arrays).
會覆蓋之前的陣列值。。。。。。
剩下的只能採用groovy指令碼的方式的,用指令碼+upsert可行麼?不可行。無法適應存在多個欄位是array型別的情況。比如現在field1 和 field2都是array。
upsert中根據首個值建立array。field1的多個值過來的,能很好的完成,當時field2的多個值過來的時候,失敗了。
upsert是首次出現該id的時候執行的語句,因此 "upsert" : { "field1" = ["valu1"]}
"script" : " if(ctx.source.field1.add(value1))".
“params” : "value1" : "v1"
第一個field是ok的,但是第二個field是不行的,因為直接走了script,沒有走upsert(id已經存在了,無法走upsert)。
可能想改進一下指令碼:
"script" : " if (ctx._source.containsKey(field1) {ctx._source.field1.add(value1)} else {ctx.source.field1=[value]})"
只有第一個field走一次upsert,其餘field都不走,在指令碼中判斷,像上邊一樣,如何?看上去挺好但是。。。。這樣程式碼怎麼寫呢,囧,你如何控制第一個field走upsert?
難道要先檢索再判斷!!!!!!還能不能愉快的一起玩耍了!!
不過那個改進後的指令碼倒是給了啟示!何不直接放棄upsert!!完全用update的方式。yeah!這樣邏輯就很通暢了,一個指令碼就搞定!
問題是:update要知道id才行?怎麼辦?我們可以先把id放到索引中啊,我們的應用場景允許我們這樣做。
怎麼放?只需要在mapping中設定一個index=no的欄位,給這個欄位賦值用prepareindex的方式賦值即可。這樣所有id都存在了,然後批量update吧!
該應用場景下是否還有更好的解決方法呢?