Elasticsearch之painless脚本条件判断更新文档

1,283 阅读2分钟

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

有时候在使用_update_by_query接口进行更新数据感觉没有那么自由,这时候可以考虑使用painless脚本去进行自由的更新数据。

Painless是一种简单、安全的脚本语言,专为与 Elasticsearch 一起使用而设计。它是 Elasticsearch 的默认脚本语言,可以安全地用于内联和存储脚本。

  • 快速的性能:painless运行速度是其他替代脚本的数倍
  • 安全性:具有方法调用/字段粒度的细粒度白名单
  • 可选类型:变量和参数可以使用显式类型或动态def类型
  • 语法:扩展 Java 语法的一个子集以提供额外的脚本语言功能
  • 优化:专为 Elasticsearch 脚本编写

假设有一个关于用户的头像(icon)和经常访问的链接(links)两个属性,由于网站进行国际化发展,cn的域名已经不适合,需要使用com域名,那么我们使用painless可以怎样进行更新数据呢?

PUT /user
{
  "mappings": {
    "properties": {
      "icon": {
        "type": "keyword"
      },
      "links": {
        "type": "keyword"
      }
    }
  }
}

POST /user/_doc/1
{
  "icon": "http://cdn.ace.cn/img/98c1d2b3-b1bf-4135-810f-53211aa432fe.png",
  "links": ["http://www.ace.cn"]
}

更新数据:
访问文档的数据使用链式访问,ctx._source指向原文档内容。那么访问头像的话,这样访问即可:ctx._source.icon。后面的链式语法就和java的语法一样(Elasticsearch底层使用的就是java),比如判断字符串是否含有某子串:String.indexOf('subString')
字符串子串替换函数:String.replace('old_string', 'new_string')
判断一个列表的元素个数: Array.size()

POST /user/_update/1
{
  "script": {"source":
    "if(ctx._source.icon.indexOf('ace.cn')!=-1){ctx._source.icon=ctx._source.icon.replace('ace.cn', 'ace.com');}if(ctx._source.links.size()>0){ctx._source.links[0]=ctx._source.links[0].replace('ace.cn', 'ace.com')}"
  }
}

执行后效果:

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "student",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "icon" : "http://cdn.ace.com/img/98c1d2b3-b1bf-4135-810f-53211aa432fe.png",
          "links" : [
            "http://www.ace.com"
          ]
        }
      }
    ]
  }
}

上面是对单一文档数据的更新, 如果要对整个索引里面的所有文档进行更新,那么可以直接使用_update_by_query:

POST /user/_update_by_query
{
  "script": {"source":
    "if(ctx._source.icon.indexOf('ace.cn')!=-1){ctx._source.icon=ctx._source.icon.replace('ace.cn', 'ace.com');}if(ctx._source.links.size()>0){ctx._source.links[0]=ctx._source.links[0].replace('ace.cn', 'ace.com')}"
  }
}