Elasticsearch的Mapping、聚合查询、乐观锁、写入原理、script

Mapping

概念

mapping就是ES数据字段field的type元数据,ES在创建索引的时候,dynamic mapping会自动为不同的数据指定相应mapping,mapping中包含了字段的类型、搜索方式(exact value或者full text)、分词器等

查看Mapping

#查看product类型的mapping
GET /product/_mapping

映射

Elasticsearch 支持如下简单域类型:

  • 字符串: string
  • 整数 : byte, short, integer, long
  • 浮点数: float, double
  • 布尔型: boolean
  • 日期: date
JSON type 域 type
布尔型: true 或者 false boolean
整数: 123 long
浮点数: 123.45 float
字符串,有效日期: 2014-09-15 date
字符串: foo bar string

浮点型123.45 基于版本7.12.1 实际测试发现映射的是float类型,官方文档是double类型

核心简单域类型

price是long类型而不是integer原因:es的mapping_type是由JSON分析器检测数据类型,而Json没有隐式类型转换(integer=>long or float=> double),所以dynamic mapping会选择一个比较宽的数据类型。

image-20210525102551899

搜索方式

exact value 精确匹配

在倒排索引过程中,分词器会将field作为一个整体创建到索引中

确切值“Foo”和“foo”就并不相同。确切值2014 和2014-09-15也不相同

对"2020-01-01"进行精确搜索的时候,必须用关键字2020-01-01才能搜索出来,如果搜关键字01,是搜索不出来的

full text全文检索

分词、近义词同义词、混淆词、大小写、词性、过滤、时态转换等(normaliztion)

类似于sql模糊查询,但是比模糊查询的功能更强大

(1) 对"2020-01-01"进行全文索引时,搜关键字2017或01都是可以搜索出来的

(2) 对"china"进行全文索引时,搜关键字cn,也可以将china搜索出来

(3) 对"likes"进行全文索引时,搜关键字like,也可以将likes搜索出来

(4) 对"Tom"进行全文索引时,搜关键字tom,也可以将Tom搜索出来

(5) 对"like"进行全文索引时,搜关键字love,也可以将like搜索出来

ES的数据类型

核心类型

  1. 数字类型

    • long, integer, short, byte, double, float, half_float, scaled_float
    • 在满足需求的情况下,尽可能选择范围小的数据类型。
  2. 字符串:String

    • keyword:适用于索引结构化的字段,可以用于过滤、排序、聚合。keyword类型的字段只能通过精确值(exact value)搜索到。Id应该用keyword

      text:当一个字段是要被全文搜索的,比如Email内容、产品描述,这些字段应该使用text类型。设置text类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项text类型的字段不用于排序,很少用于聚合

      为啥不会为text创建索引

      字段数据会占用大量堆空间,尤其是在加载高基数text字段时。字段数据一旦加载到堆中,就在该段的生命周期内保持在那里。同样,加载字段数据是一个昂贵的过程,可能导致用户遇到延迟问题。这就是默认情况下禁用字段数据的原因

    • 有时在同一字段中同时具有全文本(text)和关键字(keyword)版本会很有用:一个用于全文本搜索,另一个用于聚合和排序。

  3. date(时间类型):exact value

  4. 布尔类型:boolean

  5. binary(二进制):binary

  6. range(区间类型):integer_range、float_range、long_range、double_range、date_range

复杂类型

  1. Object:用于单个JSON对象
  2. Nested:用于JSON对象数组
  3. 地理位置:
    • Geo-point:纬度/经度积分
    • Geo-shape:用于多边形等复杂形状

特有类型

  1. IP地址:ip 用于IPv4和IPv6地址

  2. Completion:提供自动完成建议

  3. Tocken_count:计算字符串中令牌的数量

  4. Murmur3:在索引时计算值的哈希并将其存储在索引中

  5. Annotated-text:索引包含特殊标记的文本(通常用于标识命名实体)

  6. Percolator:接受来自query-dsl的查询

  7. Join:为同一索引内的文档定义父/子关系

  8. Rank features:记录数字功能以提高查询时的点击率。

  9. Dense vector:记录浮点值的密集向量。

  10. Sparse vector:记录浮点值的稀疏向量。

  11. Search-as-you-type:针对查询优化的文本字段,以实现按需输入的完成

  12. Alias:为现有字段定义别名。

  13. Flattened:允许将整个JSON对象索引为单个字段。

  14. Shapeshape 对于任意笛卡尔几何。

  15. Histogramhistogram 用于百分位数聚合的预聚合数值。

  16. Constant keywordkeyword当所有文档都具有相同值时的情况的 专业化。

  17. Array(数组):在Elasticsearch中,数组不需要专用的字段数据类型。默认情况下,任何字段都可以包含零个或多个值,但是,数组中的所有值都必须具有相同的数据类型。

    ES 7新增

  18. Date_nanos:date plus 纳秒

  19. Features:

  20. Vector:as

手工创建mapping

#创建date类型为text的mapping
PUT /product/_mapping
{
  "properties": {
    "date": {
      "type": "text"
    }
  }
}

Mapping parameters(映射参数)

index

是否对创建对当前字段创建索引,默认true,如果不创建索引,该字段不会通过索引被搜索到,但是仍然会在source元数据中展示

analyzer

指定分析器(character filtertokenizer、Token filters)

boost

对当前字段相关度的评分权重,默认1,其影响范围为词根查询(team query),对前缀、范围查询、全文索引(match query)不生效。

注意:不建议在创建索引映射时使用boost属性,而是在查询时通过boost参数指定。其主要原因如下:
1)无法动态修改字段中定义的boost值,除非使用reindex命令重建索引。
2)相反,如果在查询时指定boost值,每一个查询都可以使用不同的boost值,灵活。
3)在索引中指定boost值,boost存储在记录中,从而会降低分数计算的质量。

coerce

是否进行类型“隐式转换”。es最终存储文档的格式是字符串。

例如存在如下字段类型:

"number_one": {
   "type": "integer"
}

声明number_one字段的类型为数字类型,那是否允许接收“6”字符串形式的数据呢?因为在JSON中,“6”用来赋给int类型的字段,也是能接受的,默认coerce为true,表示允许这种赋值,但如果coerce设置为false,此时es只能接受不带双引号的数字,如果在coerce=false时,将“6”赋值给number_one时会抛出类型不匹配异常。

可以在创建索引时指定默认的coerce值


PUT /product3
{
  "settings": {
    "index.mapping.coerce": false
  },
  "mappings": {
    // 省略字段映射定义
  }
}

copy_to

copy_to参数允许您创建自定义的_all字段。换句话说,多个字段的值可以复制到一个字段中例如,first_name和last_name字段可以复制到full_name字段如下:

#字段full_name的值来自 first_name + last_name。
PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "first_name": {
          "type": "text",
          "copy_to": "full_name" 
        },
        "last_name": {
          "type": "text",
          "copy_to": "full_name" 
        },
        "full_name": {
          "type": "text"
        }
      }
    }
  }

关于copy_to重点说明

  1. 字段的复制是原始值,而不是分词后的词根。
  2. 复制字段不会包含在_souce字段中,但可以使用复制字段进行查询。
  3. 同一个字段可以复制到多个字段,写法如下:"copy_to": [ "field_1", "field_2" ]

★doc_values(正排索引)

当需要对一个字段进行排序时,es需要提取匹配结果集中的排序字段值集合,然后进行排序。倒排索引的数据结构对检索来说相当高效,但对排序就不那么擅长了

对排序、聚合非常高效的数据存储格式首推列式存储,在elasticsearch中,doc_values就是一种列式存储结构,默认情况下绝大多数数据类型都是开启的,即在索引时会将字段的值(或分词后的词根序列)加入到倒排索引中,同时也会该字段的值加入doc_values中,所有该类型的索引下该字段的值用一列存储。

如果确定不需要对字段进行排序或聚合,也不需要通过脚本访问字段值,则可以禁用doc值以节省磁盘空间(不支持textannotated_text关闭)

image-20210525113057320

image-20210525112354170

dynamic

控制是否可以动态添加新字段

  1. true 新检测到的字段将添加到映射中。(默认)
  2. false 新检测到的字段将被忽略。这些字段将不会被索引,因此将无法搜索,但仍会出现在_source返回的匹配项中。这些字段不会添加到映射中,必须显式添加新字段。
  3. strict 如果检测到新字段,则会引发异常并拒绝文档。必须将新字段显式添加到映射中

eager_global_ordinals

用于聚合的字段上,优化聚合性能

Frozen indices

冻结索引几乎没有集群开销(除了将其元数据保留在内存中),并且被阻止进行写操作。请参阅冻结索引解冻索引

有些索引使用率很高,会被保存在内存中,有些使用率特别低,宁愿在使用的时候重新创建,在使用完毕后丢弃数据,Frozen indices的数据命中频率小,不适用于高搜索负载,数据不会被保存在内存中,堆空间占用比普通索引少得多,Frozen indices是只读的,请求可能是秒级或者分钟级

#冻结某个索引
POST / my_index / _freeze 
#解冻索引
POST / my_index / _unfreeze 

eager_global_ordinals不适用于Frozen indices

如果您使用基于时间的索引来存储日志消息或时间序列数据,则较早的索引可能比最近的索引的搜索频率要低得多。较旧的索引也不会收到索引请求。此外,通常情况下,对较旧索引的搜索是为了执行较慢的响应而可以接受的长期分析。

enable

是否创建倒排索引,可以对字段操作,也可以对索引操作,如果不创建索引,让然可以检索并在_source元数据中展示,谨慎使用,该状态无法修改

PUT my_index{
  "mappings": {
    "enabled": false 
  }}

fielddata

Elasticsearch:fielddata 介绍

大多数字段可以将索引时生产的磁盘doc_values用于此数据访问模式,但是文本(text)字段不支持doc_values。替代的方案,文本(text)字段使用查询时内存中的数据结构,称为fielddata。 当我们首次将该字段用于聚合,排序或在脚本中使用时,将按需构建此数据结构。 它是通过从磁盘读取每个段的整个反向索引,反转术语↔︎文档关系并将结果存储在JVM堆中的内存中来构建的

Fielddata会占用大量堆空间,尤其是在加载大量的文本字段时。 一旦将字段数据加载到堆中,它在该段的生命周期内将一直保留在那里。 同样,加载字段数据是一个昂贵的过程,可能导致用户遇到延迟的情况。 这就是默认情况下禁用字段数据的原因。

fields

因为不同的目的将同一个字段用不同的方式索引。这就相当于实现了 multi-fields。例如,一个 string 类型字段可以被映射成 text 字段作为 full-text 进行搜索,同时也可以作为 keyword 字段用于排序和聚合:

对于相同索引中具有相同名称的字段,fields设置允许有不同的设置。可以使用PUT映射API将新的多字段添加到已存在的字段中。

Multi_fields 不会改变原始的 _source 字段。

#创建mapping
PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "city": {
          "type": "text",
          "fields": {
            "raw": { 
              "type":  "keyword"
            }
          }
        }
      }
    }
  }
}
#city字段用于全文本搜索
# city.raw用于排序与聚合。
GET my_index/_search
{
  "query": {
    "match": {
      "city": "york" 
    }
  },
  "sort": {
    "city.raw": "asc" 
  },
  "aggs": {
    "Cities": {
      "terms": {
        "field": "city.raw" 
      }
    }
  }
}

format

格式化

"date": {
      "type":   "date",
      "format": "yyyy-MM-dd"
 }

ignore_above

超过长度将被忽略

ignore_malformed

忽略类型错误

PUT my_index{
  "mappings": {
    "properties": {
      "number_one": {
        "type": "integer",
        "ignore_malformed": true
      },
      "number_two": {
        "type": "integer"
      }
    }
  }}
PUT my_index/_doc/1{
  "text":       "Some text value",
  "number_one": "foo" }   //虽然有异常 但是能正常插入数据
PUT my_index/_doc/2{
  "text":       "Some text value",
  "number_two": "foo" }   //数据格式不对

index_options

控制将哪些信息添加到反向索引中以进行搜索和突出显示。仅用于text字段

Index_phrases

提升exact_value查询速度,但是要消耗更多磁盘空间

Index_prefixes

前缀搜索

"index_prefixes": {
          "min_chars" : 1,
          "max_chars" : 10
        }	

min_chars

前缀最小长度,>0,默认2(包含)

max_chars

前缀最大长度,<20,默认5(包含)

meta

附加元数据

normalizer

normalizerkeyword的一个属性,可以对 keyword生成的单一 Term再做进一步的处理,比如 lowercase,即做小写变换。使用方法和自定义分词器有些类似,需要自定义

# 自定义 normalizer
PUT test_normalizer
{
  "settings": {
    "analysis": {
      "normalizer": {
        "lowercase": {
          "type": "custom",
          "filter": [
            "lowercase"
          ]
        }
      }
    }
  },
  "mappings": {
    "doc": {
      "properties": {
        "type": {
          "type": "keyword"
        },
        "type_normalizer": {
          "type": "keyword",
          "normalizer": "lowercase"
        }
      }
    }
  }
}

norms

是否禁用评分(在filter和聚合字段上应该禁用)。

null_value

为null值设置默认值

"null_value": "NULL"

position_increment_gap

对于analyzed string字段,都会考虑把词的位置信息,用于支持位置和短语匹配查询(proximity or phrase queries),例如,有一个字符串字段,该字段中存在多个词“fake”,ElasticSearch引擎会在每个值之间增加一个gap,以防止短语匹配或位置匹配查询出现跨越多个词的异常,这个gap的值就是属性position_increment_gap,默认值是100;

proterties

除了mapping还可用于object的属性设置

PUT my_index{
  "settings": {
    "analysis": {
      "filter": {
        "autocomplete_filter": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 20
        }
      },
      "analyzer": {
        "autocomplete": { 
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "autocomplete_filter"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "text": {
        "type": "text",
        "analyzer": "autocomplete", 
        "search_analyzer": "standard" 
      }
    }
  }}

similarity

为字段设置相关度算法,支持BM25、claassic(TF-IDF)、boolean

store

存储(store)属性指定是否将字段的原始值写入索引,默认值是no,字段值被分析,能够被搜索,但是,字段的原始值不会存储,这意味着,该字段能够被查询,但是无法获取字段的原始值。默认情况下,该字段的值会被存储到_source字段中,如果想要获取单个或多个字段的值,而不是整个_source字段,可以使用 source filtering 来实现;但是在特定的条件下,只存储一个字段的值是有意义的(make sense),例如,一个article文档包含:title,postdate和content字段,从文档中只获取title和postdate字段,并且使_source 字段包含content字段,必须通过store属性来控制:

"mappings": {
    "my_type": {
      "properties": {
        "title": {
          "type": "string",
          "store": true 
        },
        "date": {
          "type": "date",
          "store": true 
        },
        "content": {
          "type": "string",          "store": false   
        }
      }
    }
  }

聚合查询

es聚合是将数据汇总为 度量、统计、以及其它分析。主要分为以下三大类

  • 桶(bucket)聚合 - 桶的作用,是按照某种方式对数据进行分组,每一组数据在ES中称为一个
  • 度量(metrics)聚合 - 分组完成以后,我们一般会对组中的数据进行聚合运算,例如求平均值、最大、最小、求和等,这些在ES中称为度量
  • Pipeline 聚合 - 从已聚合数据中进行聚合查询

常用的一些度量聚合方式

  • Avg Aggregation:求平均值
  • Max Aggregation:求最大值
  • Min Aggregation:求最小值
  • Percentiles Aggregation:求百分比
  • Stats Aggregation:同时返回avg、max、min、sum、count等
  • Sum Aggregation:求和
  • Top hits Aggregation:求前几
  • Value Count Aggregation:求总数
  • Date Histogram Aggregation:根据日期阶梯分组,例如给定阶梯为周,会自动每周分为一组
  • Histogram Aggregation:根据数值阶梯分组,与日期类似
  • Terms Aggregation:根据词条内容分组,词条内容完全匹配的为一组
  • Range Aggregation:数值和日期的范围分组,指定开始和结束,然后按段分组

例子

导入数据

#创建数据
#在ES中,需要进行聚合、排序、过滤的字段其处理方式比较特殊,因此不能被分词。这里我们将color和make这两个文字类型的字段设置为keyword类型,这个类型不会被分词,将来就可以参与聚合
PUT /cars
{
 "settings": {
   "number_of_shards": 1,
   "number_of_replicas": 0
 },
 "mappings": {
   "properties": {
     "color":{
       "type": "keyword"
     },
     "make":{
       "type": "keyword"
     }
   }
 }
}

#导入数据
#   POST /cars/transactions/_bulk   7.0之后es官方不建议指定类型
POST /cars/_bulk
{ "index": {}}
{ "price" : 10000, "color" : "red", "make" : "吉利", "sold" : "2020-10-28" }
{ "index": {}}
{ "price" : 20000, "color" : "red", "make" : "长城", "sold" : "2020-11-05" }
{ "index": {}}
{ "price" : 30000, "color" : "green", "make" : "比亚迪", "sold" : "2020-05-18" }
{ "index": {}}
{ "price" : 15000, "color" : "blue", "make" : "比亚迪", "sold" : "2020-07-02" }
{ "index": {}}
{ "price" : 12000, "color" : "green", "make" : "长城", "sold" : "2020-08-19" }
{ "index": {}}
{ "price" : 20000, "color" : "red", "make" : "长安", "sold" : "2020-11-05" }
{ "index": {}}
{ "price" : 80000, "color" : "red", "make" : "吉利", "sold" : "2020-01-01" }
{ "index": {}}
{ "price" : 25000, "color" : "blue", "make" : "长安", "sold" : "2020-02-12" }

聚合为桶

汽车的颜色color来划分桶


GET /cars/_search
{
  "size": 0, 
 "aggs": {
   "popular_colors": {
     "terms": {
       "field": "color.keyword"
     }
   }
 }
}
  • size: 查询条数,这里设置为0,因为我们不关心搜索到的数据,只关心聚合结果,提高效率
  • aggs:声明这是一个聚合查询,是aggregations的缩写
    • popular_colors:给这次聚合起一个名字,任意
    • terms:划分桶的方式,这里是根据词条划分
    • field:划分桶的字段

这里field字段使用的是color.keyword是因为text字段在聚合索引中没有进行优化,使用color会报错


#返回数据
{
  "took" : 11,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 8,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "popular_colors" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "red",
          "doc_count" : 4
        },
        {
          "key" : "blue",
          "doc_count" : 2
        },
        {
          "key" : "green",
          "doc_count" : 2
        }
      ]
    }
  }
}
  • hits:查询结果为空,因为我们设置了size为0
  • aggregations:聚合的结果
  • popular_colors:我们定义的聚合名称
  • buckets:查找到的桶,每个不同的color字段值都会形成一个桶
    • key:这个桶对应的color字段的值
    • doc_count:这个桶中的文档数量

桶内度量

求价格平均值的度量

GET /cars/_search
{
  "size": 0,
  "aggs": {
    "popular_color": {
      "terms": {
        "field": "color.keyword"
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}
  • aggs:我们在上一个aggs(popular_colors)中添加新的aggs。可见度量也是一个聚合,度量是在桶内的聚合
  • avg_price:聚合的名称
  • avg:度量的类型,这里是求平均值
  • field:度量运算的字段

桶内嵌套桶

我们想统计每种颜色的汽车中,分别属于哪个制造商,按照make字段再进行分桶

GET /cars/_search
{
    "size" : 0,
    "aggs" : { 
        "popular_colors" : { 
            "terms" : { 
              "field" : "color.keyword"
            },
            "aggs":{
                "avg_price": { 
                   "avg": {
                      "field": "price" 
                   }
                },
                "maker":{
                    "terms":{
                        "field":"make.keyword"
                    }
                }
            }
        }
    }
}
  • 原来的color桶和avg计算我们不变
  • maker:在嵌套的aggs下新添一个桶,叫做maker
  • terms:桶的划分类型依然是词条
  • filed:这里根据make字段进行划分

阶梯分桶

#按照price,5000为一档,并且去除掉数据为0的档
GET /cars/_search
{
  "size":0,
  "aggs":{
    "price":{
      "histogram": {
        "field": "price",
        "interval": 5000,
        "min_doc_count": 1
      }
    }
  }
}

批量操作

基于mget批量查询以及基于bulk的批量增删改

mget:批量查询

#查询id为15VdonkBKvp3-bOOQqdZ 和3的数据
GET /_mget
{
  "docs": [
    {
      "_index": "cars",
      "_id" : "15VdonkBKvp3-bOOQqdZ"
    },
    {
      "_index": "cars",
      "_id": 3
    }
  ]
}

#同上
GET /cars/_mget
{
  "docs": [
    {
      "_id" : "15VdonkBKvp3-bOOQqdZ"
    },
    {
      "_id": 3
    }
  ]
}
#同上
GET /product/_mget
{
  "ids":["15VdonkBKvp3-bOOQqdZ","3"]
}

bulk:批量增删改 no-query

#这里一个操作一定要放在一行,不能有空格或换行
POST /_bulk
{ "delete": { "_index": "product2",  "_id": "1" }}
{ "create": { "_index": "product2",  "_id": "2" }}
{ "name":    "_bulk create 2" }
{ "create": { "_index": "product2",  "_id": "12" }}
{ "name":    "_bulk create 12" }
{ "index":  { "_index": "product2",  "_id": "3" }}
{ "name":    "index product2 " }
{ "index":  { "_index": "product2",  "_id": "13" }}
{ "name":    "index product2" }
{ "update": { "_index": "product2",  "_id": "4","retry_on_conflict" : "3"} }
{ "doc" : {"test_field2" : "bulk test1"} }


#加?filter_path=items.*.error  只显示失败的
POST /_bulk?filter_path=items.*.error
{ "delete": { "_index": "product2",  "_id": "1" }}
{ "create": { "_index": "product2",  "_id": "2" }}
{ "name":    "_bulk create 2" }
{ "create": { "_index": "product2",  "_id": "12" }}
{ "name":    "_bulk create 12" }
{ "index":  { "_index": "product2",  "_id": "3" }}
{ "name":    "index product2 " }
{ "index":  { "_index": "product2",  "_id": "13" }}
{ "name":    "index product2" }
{ "update": { "_index": "product2",  "_id": "4","retry_on_conflict" : "3"} }
{ "doc" : {"test_field2" : "bulk test1"} }

ES的乐观锁

ES的乐观锁主要通过version字段实现,每一次修改,删除(逻辑删除)都会使version+1

image-20210525160037968

ES Scripts

  1. Groovy:ES 1.4.X-5.0,因为存在安全漏洞,新版本不支持
  2. expression:每个文档的开销较低:表达式的作用更多,可以非常快速地执行,甚至比编写native脚本还要快,支持javascript语法的子集:单个表达式。缺点:只能访问数字,布尔值,日期和geo_point字段,存储的字段不可用
  3. mustache:提供模板参数化查询
  4. java
  5. Painless: 一种专门用于Elasticsearch的简单,用于内联和存储脚本,类似于Java,也有注释、关键字、类型、变量、函数等,安全的脚本语言。它是Elasticsearch的默认脚本语言,可以安全地用于内联和存储脚本。

Painless语法

painless脚本语言

painless

#将id为15VdonkBKvp3-bOOQqdZ的数据中price字段-1
POST cars/_update/15VdonkBKvp3-bOOQqdZ
{
  "script": "ctx._source.price -=1"
}

#同上
POST cars/_update/15VdonkBKvp3-bOOQqdZ
{
  "script": {
    "source": "ctx._source.price -=1"
  }
}
#获取cars中所有的数据price
GET cars/_search
{
  "script_fields": {
    "test field": {
      "script": {
        "lang": "painless",
        "source": "doc['price'].value"
      }
    }
  }
}

#打折模版(脚本模版,对所有索引有效)
POST _scripts/calculate-discount
{
  "script": {
    "lang": "painless",
    "source": "doc['price'].value * params.discount"
  }
}
#查看模版
GET _scripts/calculate-discount
#删除模版
DELETE _scripts/calculate-discount

#使用模版
GET cars/_search
{
  "script_fields": {
    "discount_price": {
      "script": {
        "id":"calculate-discount",
        "params": {
          "discount": 0.8
        }
      }
    }
  }
}


#测试painless函数方法
PUT test_index/_bulk?refresh
{"index":{"_id":1}}
{"ajbh": "12345","ajmc": "立案案件","lasj": "2020/05/21 13:25:23","jsbax_sjjh2_xz_ryjbxx_cleaning": [{"XM": "张三","SF": "男"},{"XM": "李四","SF": "男"},{"XM": "王五","SF": "女"},{"XM": "赵六","SF": "男"}]}
{"index":{"_id":2}}
{"ajbh": "563245","ajmc": "结案案件","lasj": "2020/05/21 13:25:23","jsbax_sjjh2_xz_ryjbxx_cleaning": [{"XM": "张三2","SF": "男"},{"XM": "李四2","SF": "男"},{"XM": "王五2","SF": "女"},{"XM": "赵六2","SF": "女"}]}
{"index":{"_id":3}}
{"ajbh": "12345","ajmc": "立案案件","lasj": "2020/05/21 13:25:23","jsbax_sjjh2_xz_ryjbxx_cleaning": [{"XM": "张三3","SF": "男"},{"XM": "李四3","SF": "男"},{"XM": "王五3","SF": "女"},{"XM": "赵六3","SF": "男"}]}

#获取所有数据中案件中性别为男的人
#此处要将doc['field'].value换成params['_source']['jsbax_sjjh2_xz_ryjbxx_cleaning']
GET /test_index/_search
{
 "size": 0,
 "aggs": {
   "tag_add_group": {
     "sum": {
       "script": {
         "lang": "painless",
         "source": """
         int total=0;
         for(int i=0;i<params['_source']['jsbax_sjjh2_xz_ryjbxx_cleaning'].length;i++)
         {
           if(params['_source']['jsbax_sjjh2_xz_ryjbxx_cleaning'][i]['SF']=='男')
           total+=i;
         }
         return total;
         """
       }
     }
   }
 }
}

doc['field'].value和params['_source'] ['field']的区别

使用doc关键字,将导致该字段的条件被加载到内存(缓存),这将导致更快的执行,但更多的内存消耗。此外,doc[...]符号只允许简单类型(不能返回一个复杂类型(JSON对象或者nested类型)),只有在非分析或单个词条的基础上有意义。但是,doc如果可能,使用仍然是从文档访问值的推荐方式,因为_source每次使用时都必须加载并解析。使用_source非常缓慢

ES的写入原理

image-20210525163142635

posted @ 2021-06-08 15:55  知白守黑,和光同尘  阅读(333)  评论(0编辑  收藏  举报