0.引用
今天有同学问到doc['field'].value与params._source['field']用法的区别,起因在于下述的一道题的解法上,下面详细讲述下我的看法
【有错望指出,看完如果有表述的不清楚,或者不明白的,也请告诉我,我们一起完善这个知识点】
1 题目
题目是要求根据现有字段,利用runtime mapping计算原字段的值
这是索引mapping
{
"dur_log-1" : {
"mappings" : {
"properties" : {
"browser" : {
"type" : "keyword"
},
"duration" : {
"type" : "double"
},
"timestamp" : {
"type" : "date",
"format" : "yyyy-MM-dd HH:mm:ss"
}
}
}
}
}
以下是习题答案:
duration字段的值是当browser字段为‘Firefox’时就除以1000。
GET dur_log-1/_search
{
"runtime_mappings": {
"duration": {
"type": "double",
"script": {
"source": """
if(doc['browser'].value == "Firefox"){
emit(params._source['duration'] / 1000.0);
} else {
emit(params._source['duration']);
}
"""
}
}
},
"size": 0,
"aggs": {
"terms": {
"terms": {
"field": "browser"
},
"aggs": {
"average duration": {
"avg": {
"field": "duration"
}
}
}
}
}
}
这个答案本身没有什么问题,但大家知道painless中引用字段的方法有两种doc和params。这里面引用duration时用了params,但是当我们把这里的引用改为doc时就会发现出现问题了
报错了,错误原因是Cyclic dependency detected while resolving runtime fields: duration -> duration。也就是说产生循环依赖了。这是为什么呢?引用同一个变量,params就不报错,而doc为什么就报错了呢?
2 规避方案
在阐明问题原因之前,我先说明一下如何规避这种问题,如果你要给已有字段赋值,并且要调用这个字段本身,那就使用params取值,其他的场景只要字段类型为简单值的都可以用doc
什么是简单值?见下述解释
3 问题原因
那么接下来我们就来分析问题的原因,为什么放到最后讲,是因为以下原因仅仅基于本人查阅资料后的推测,并没有实验数据或官方材料支撑这个想法,所以仅供参考
doc和params的区别
首先我们要理解doc和params的区别:
doc是加载到内存中,直接从内存中获取对象的。所以它的显著特点就是快!doc另外一个显著特点就是只能访问简单字段值,什么是简单字段值?根据官方的描述:
```
Doc-values can only return "simple" field values like numbers, dates, geo- points, terms, etc, or arrays of these values if the field is multi-valued. It cannot return JSON objects.
```
即数字、日期、地理点、术语等或者这些值的数组,所以doc不支持json对象
params是从磁盘中获取的,每次都要从磁盘取一次,所以速度相对较慢。所以一般情况下,我们更加推荐使用doc
原因推测
基于以上基础,我们知道doc是直接从内存获取,比如说我们要赋值duration的内存地址为a,而引用的doc['duration']也为a,也就是说如果用doc的获取duration,就相当于我引用我自己来定义我自己,如下图所示,没有尽头。这也就产生了循环依赖。
而params为什么可以呢?因为params是从磁盘获取后再加载到内存的,所以每次的内存地址都是新的,因此也就没有了循环依赖的问题。
以上就是个人对这个问题的理解和推测,不具备真实性。