3.4.2.19.copy_to
创作人:章海怒
审稿人:欧阳楚才
在写入数据时对写入的内容进行拼贴,组合成一个全新的数据;也就是说固定的数据的拼接可以在 ES 中先完成,而不需要在查询数据时针对查询做繁杂的拼接。
关键词:copy_to
copy_to 参数允许将多个字段的值复制到一个单独的字段中,然后可以将其作为单个字段进行查询。
使用场景:copy_to 的一个常见用法就是用来减少搜索的字段数来提升搜索的效率;查询的字段越多,理论上 ES 的返回就越慢。多字段查询的时候,一种常见的性能优化方式是通过 copy_to 将多个查询字段的值合并为1个字段(数据的简单拼贴),通过合并后的字段进行查询,这样可以有效避免 multi_match 的使用,并且可以使得效率提高。
例如:
{ "user": "双榆树-张三", "message": "今儿天气不错啊,出去转转去", "uid": 2, "age": 20, "city": "北京", "province": "北京", "country": "中国", "address": "中国北京市海淀区", "location": { "lat": "39.970718", "lon": "116.325747" } }
在这里,我们可以看到在这个文档中,有这样的几个相关字段:city、province、country ,如果想放在一起搜索,一种方法我们可以在 must 子句中使用 should 子句查询。这种方法写起来比较麻烦并且性能比较低。这个时候,我们就可以考虑用 copy_to 将3个字段糅合成一个字段来搜索。
例如:
PUT twitter { "mappings": { "properties": { "address": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "age": { "type": "long" }, "city": { "type": "keyword", "copy_to": "region" }, "country": { "type": "keyword", "copy_to": "region" }, "province": { "type": "keyword", "copy_to": "region" }, "region": { "type": "text", "store": true }, "location": { "type": "geo_point" }, "message": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "uid": { "type": "long" }, "user": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } } }
我们通过 mapping 时声明 copy_to 的指定,把 city、 country 及 province 三个字段合并成为一个字段 region,但是这个 region 并不存在于我们文档的 source 里。
我们录入一些样例数据:
POST _bulk { "index" : { "_index" : "twitter", "_id": 1} } {"user":"双榆树-张三","message":"今儿天气不错啊,出去转转去","uid":2,"age":20,"city":"北京","province":"北京","country":"中国","address":"中国北京市海淀区","location":{"lat":"39.970718","lon":"116.325747"}} { "index" : { "_index" : "twitter", "_id": 2 }} {"user":"东城区-老刘","message":"出发,下一站云南!","uid":3,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区台基厂三条3号","location":{"lat":"39.904313","lon":"116.412754"}} { "index" : { "_index" : "twitter", "_id": 3} } {"user":"东城区-李四","message":"happy birthday!","uid":4,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区","location":{"lat":"39.893801","lon":"116.408986"}} { "index" : { "_index" : "twitter", "_id": 4} } {"user":"朝阳区-老贾","message":"123,gogogo","uid":5,"age":35,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区建国门","location":{"lat":"39.718256","lon":"116.367910"}} { "index" : { "_index" : "twitter", "_id": 5} } {"user":"朝阳区-老王","message":"Happy BirthDay My Friend!","uid":6,"age":50,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区国贸","location":{"lat":"39.918256","lon":"116.467910"} { "index" : { "_index" : "twitter", "_id": 6} } {"user":"虹桥-老吴","message":"好友来了都今天我生日,好友来了,什么 birthday happy 就成!","uid":7,"age":90,"city":"上海","province":"上海","country":"中国","address":"中国上海市闵行区","location":{"lat":"31.175927","lon":"121.383328"}}
假如我们想搜索 country : 中国,province : 北京 这样的记录,我们可以只写如下的一条语句就可以了。
GET twitter/_search { "query": { "match": { "region": { "query": "中国 北京", "minimum_should_match": 4 } } } }
如果想查看 copy_to 的内容,就需要如例子中,显性的声明 region 这个字段 "store": true
声明以后就可以通过:
GET twitter/_doc/1?stored_fields=region
来查看对应region的内容,以上查询可以返回结果:
{ "_index": "twitter", "_type": "_doc", "_id": "1", "_version": 1, "_seq_no": 0, "_primary_term": 1, "found": true, "fields": { "region": [ "北京", "北京", "中国" ] } }
小贴士:
1、copy_to 是值拷贝,也就是说拷贝之后 city ,provice 还是可以独立用于查询(与传统值拷贝不同的是,ES 更新时先查询出原来的文档,然后插入一条新文档,标记删除旧文档,所以copy_to 的字段在源字段更新时,会同时更新)。
2、copy_to 不可以嵌套使用。
3、可以拷贝至多个字段例如 "copy_to": [ "field_1", "field_2" ]。
4、copy_to 的字段本身包含类型的声明,支持不同类型的数据 copy_to 到同一字段,会有一次类型的转换。
5、copy_to 实际 copy 发生在数据写入时,会直接形成一个新的字段,所以 copy_to 实际会占用集群的磁盘、物理资源等。
本文部分内容参考自CSDN——Elastic 中国社区官方博客