一种处理Elasticsearch对象数组类型的方式

目前情况

Elasticsearch中处理对象数组有两种格式array和nested,但这两种都有一定的不足。
以下面的文档为例:

1
2
3
4
5
6
7
8
9
10
11
12
{
"user": [
{
"first": "John",
"last": "Smith"
},
{
"first": "Alice",
"last": "White"
}
]
}

如果在mapping中以array存储,那么实际存储为:

1
2
user.first:["John","Alice"]
user.last:["Smith","White"]

如果以must的方式查询user.first:Johnuser.last:White,那么这篇文档也会命中,这不是我们期望的。

如果在mapping中以array存储,Elasticsearch将每个对象视为一个doc,这例子会存储3个doc,会严重影响ES写入和查询的效率。

Flatten格式

我想到的存储方式很简单,就是将对象数组打平保存为一个keyword类型的字符串数组,故起名Flatten格式。
以上面文档为例,数组对象需要转换为下面的格式

1
2
3
4
5
6
7
8
"user.flatten": [
"first:John",
"last:Smith",
"first:John&last:Smith",
"first:Alice",
"White:last",
"first:Alice&last:White"
]

这样以must的方式查询user.first:Johnuser.last:White,可以转换为term查询first:John&last:White,并不会命中文档。
同时,这种方式还是保存1个doc,避免了nested的缺点。

对于flatten格式有几点说明

user.flatten数组的大小

如果user对象个数为M,user属性个数为N,那么其数组大小为(2^N-1)*M

对象为空怎么处理

建议以null方式保存,例如:

1
2
3
4
{
"first": "John",
"last": null
}

转换后的格式

1
2
3
4
5
[
"first:John",
"last:null",
"first:John&last:null",
]

保存和查询对于对象属性的处理顺序要保持一致

上述例子都是按first&last顺序存储的,那么以must的方式查询user.first:Johnuser.last:White也要以first:John&last:White方式查询,不能用last:White&first:John

不足

  • 需要自己编码将JSON对象转换为字符串数组
  • 需要自己编码转换查询语句
  • 只支持term查询