在ES的日常使用中,需要使用到Nested结构存储数个同级的子节点数据,例如一条主订单下的N条子订单的数据。
新增更新操作
现在,假设我们在ES中有这样一条数据
PUT /celebrities/_doc/114
{
"user" : "Kun",
"post_date" : "2009-11-15T14:12:12",
"message" : "Idol who has been practicing for two and a half years",
"skills":[
{
"name":"sing",
"skill_level":"A"
},
{
"name":"jump",
"skill_level":"S"
},
{
"name":"rap",
"skill_level":"SS"
}
]
}
我们需要往skills的Nested数组中添加一个新的节点,节点的name为“consecutive five whips”,则可以这么写
POST /celebrities/_doc/114
{
"script": {
"source": "if (ctx._source.skills == null) {List ls = new ArrayList();ls.add(params.skill);ctx._source.skills = ls;} else {ctx._source.skills.add(params.skill);}",
"lang": "painless",
"params": {
"skill": {
"name": "consecutive five whips",
"skill_level": "SSS"
}
}
}
}
得到返回结果,表明执行成功
{
"_index": "celebrities",
"_id": "114",
"_version": 6,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 6,
"_primary_term": 1
}
通过代码可以简单的看出逻辑,如果_source.skills为null,则创建一个新的ArrayList,并将参数中的skill节点的内容放进去,最后赋值给_source.skills,反之如果不为null的话,则直接往里加入当前的skill字段的内容。
if (ctx._source.skills == null) {
List ls = new ArrayList();
ls.add(params.skill);
ctx._source.skills = ls;
} else {
ctx._source.skills.add(params.skill);
}
要先判断是否为null,否则无法调用add方法,并会抛出一个异常
"caused_by": {
"type": "null_pointer_exception",
"reason": "cannot access method/field [add] from a null def reference"
}
上面的代码是可以正确添加新的值了,但是如果不小心重复执行的话,会一直往skills的Nested数组中重复添加相同的数据,所以接下来我们仍要做进一步的优化,进行去重判断,根据某个字段做唯一判断,有则更新,无则新增
首先我们还是将数据恢复成
"skills":[
{
"name":"sing",
"skill_level":"A"
},
{
"name":"jump",
"skill_level":"S"
},
{
"name":"rap",
"skill_level":"SS"
}
]
之后执行我们修改后的update语句
POST /celebrities/_update/114
{
"script": {
"source": "if (ctx._source.skills == null) {List ls = new ArrayList();ls.add(params.skill);ctx._source.skills = ls;} else { def flag = true;for (item in ctx._source.skills) { if (item['name'] == params.skill.name) { item['skill_level'] = params.skill.skill_level;flag = false;}} if(flag){ctx._source.skills.add(params.skill)}}",
"lang": "painless",
"params": {
"skill": {
"name": "consecutive five whips",
"skill_level": "SSS"
}
}
}
}
看到返回结果,更新插入成功
{
"_index": "celebrities",
"_id": "114",
"_version": 7,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 15,
"_primary_term": 1
}
根据文档ID,GET一下查询结果
GET /celebrities/_doc/114
确认无误,成功执行了插入
{
"_index": "celebrities",
"_id": "114",
"_version": 7,
"_seq_no": 15,
"_primary_term": 1,
"found": true,
"_source": {
"user": "Kun",
"post_date": "2009-11-15T14:12:12",
"message": "Idol who has been practicing for two and a half years",
"skills": [
{
"name": "sing",
"skill_level": "A"
},
{
"name": "jump",
"skill_level": "S"
},
{
"name": "rap",
"skill_level": "SS"
},
{
"name": "consecutive five whips",
"skill_level": "SSS"
}
]
}
}
之后我们再修改下update语句的内容,期望能看到name为consecutive five whips的这条数据skill_level更新为SSS+
,而不是skills的Nested嵌套结构中再新增一条name为consecutive five whips的数据
POST /celebrities/_update/114
{
"script": {
"source": "if (ctx._source.skills == null) {List ls = new ArrayList();ls.add(params.skill);ctx._source.skills = ls;} else { def flag = true;for (item in ctx._source.skills) { if (item['name'] == params.skill.name) { item['skill_level'] = params.skill.skill_level;flag = false;}} if(flag){ctx._source.skills.add(params.skill)}}",
"lang": "painless",
"params": {
"skill": {
"name": "consecutive five whips",
"skill_level": "SSS+"
}
}
}
}
执行成功后,再次查询GET /celebrities/_doc/114
,得到结果,确实如我们预期的那样
{
"_index": "celebrities",
"_id": "114",
"_version": 8,
"_seq_no": 16,
"_primary_term": 1,
"found": true,
"_source": {
"user": "Kun",
"post_date": "2009-11-15T14:12:12",
"message": "Idol who has been practicing for two and a half years",
"skills": [
{
"name": "sing",
"skill_level": "A"
},
{
"name": "jump",
"skill_level": "S"
},
{
"name": "rap",
"skill_level": "SS"
},
{
"name": "consecutive five whips",
"skill_level": "SSS+"
}
]
}
}
删除操作
如果需要删除某一条的话,则可以
POST /celebrities/_update/114
{
"script": {
"source": "ctx._source.skills.removeIf(item -> item.name == params.skill.name)",
"lang": "painless",
"params": {
"skill": {
"name": "consecutive five whips"
}
}
}
}
看到更新成功,我们再次查询下GET /celebrities/_doc/114
,确认确实是删除掉了
{
"_index": "celebrities",
"_id": "114",
"_version": 9,
"_seq_no": 18,
"_primary_term": 1,
"found": true,
"_source": {
"user": "Kun",
"post_date": "2009-11-15T14:12:12",
"message": "Idol who has been practicing for two and a half years",
"skills": [
{
"name": "sing",
"skill_level": "A"
},
{
"name": "jump",
"skill_level": "S"
},
{
"name": "rap",
"skill_level": "SS"
}
]
}
}
上面的演示数据中 script 是 painless的语法,painless是es中对脚本支持较好的。
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html
发表评论