天天看点

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吧!

该应用场景下是否还有更好的解决方法呢?

继续阅读