计算机系统应用教程网站

网站首页 > 技术文章 正文

一篇文章让你了解Elasticsearch搜索

btikc 2024-10-29 13:13:30 技术文章 5 ℃ 0 评论

一、索引

1.1、常见概念

创建索引前,先弄清楚这几个概念,index_template、index、setting、mapping;alais、ilm/policy;dynamic_template、dynamic_mapping。

es创建索引是相当灵活的,可以插入数据的时候,自动推断字段类型,生成索引【此时称为dynamic_mapping】;也可以先创建索引【index】,再插入数据,不过这个索引是一对一;也可以创建一个索引模板【index_template】,后面生成索引直接使用索引模板创建,就不需要为每个索引设置字段类型了,但是索引模板字段类型固定,也会出现一定局限,这时还可以使用动态模板【dynamic_template】做一些按条件生成索引。索引设置后,我们还可以为索引创建别名【alais】,这样当索引分片后(比如index1,index2),我们还可以通过一个统一名称去访问它,索引的数据存在冷、热、温不同的生命周期,我们可以通过设置索引的ilm/policy去实现。

1.2、字段类型

Elasticsearch字段类型类似于MySQL中的字段类型。Elasticsearch字段类型主要有:核心类型复合类型多字段特性、特殊类型

1.2.1、 核心类型

核心类型可以划分为字符串类型、数字类型、日期类型、布尔类型、基于BASE64的二进制类型、范围类型

类型

具体类型

二进制类型

binary

字符串类型

text、keyword

布尔类型

boolean

数字类型

long、integer、short、byte、double、float、half_float、scaled_float

日期类型

date、date_nanos

范围类型

range

字符串类型
Elasticsearch 7.x有两种字符串类型:text和keyword,在Elasticsearch 5.x之后string类型已经不再支持。

  • text:text类型适用于需要被全文检索的字段,例如新闻正文、邮件内容等比较长的文字。text类型会被Lucene分词器(Analyzer)处理为一个个词项,并使用Lucene倒排索引存储。text字段不能被用于排序,无法模糊查询keyword:keyword适合简短、结构化字符串,例如主机名、姓名、商品名称等,可以用于过滤、排序、聚合检索,也可以用于精确查询

保存一个字符串新字段,可以自动识别为text+keyword类型,当我们不想分词查询时,可以name.keyword进行查询。如果想将已有的text不分词,可以执行上面命令,再name.keyword进行查询。

数字类型

数字类型分为long、integer、short、byte、double、float、half_float、scaled_float,其范围如下:

  • long,?263到263-1integer,-231到231-1short,?32768到 32767byte,?128到127double,IEEE 754标准双精度浮点类型,8字节float,IEEE 754标准单精度浮点类型,4字节half_float,IEEE 754标准半精度浮点类型,2字节scaled_float,缩放类型浮点类型

数字类型的字段在满足需求的前提下应当尽量选择范围较小的数据类型,字段长度越短,搜索效率越高。

日期类型
在Elasticsearch中日期可以为以下形式:

  • 格式化的日期字符串,例如2019-01-01 00:00、2019/01/01时间戳(和1970-01-01 00:00:00 UTC的差值),单位毫秒或者秒

即使是格式化的日期字符串,Elasticsearch底层依然采用的是时间戳的形式存储

布尔类型

JSON文档中同样存在布尔类型,不过JSON字符串类型也可以被Elasticsearch转换布尔类型存储,前提是字符串的取值为"true"或者"false"。布尔类型常用于检索中的过滤条件。

二进制类型

二进制类型binary接受BASE64编码的字符串,默认store属性为false,并且不可以被搜索

范围类型

范围类型可以用来表达一个数据的区间,可以分为5种:

  • integer_range,可以表示最大的范围为 [?231,231?1]float_range,可以表达IEEE754单精度浮点数范围long_range,可以表示最大的范围为 [?263,263?1]double_range,可以表达IEEE754双精度浮点数范围date_range,可以表达64位时间戳(单位毫秒)范围

1.2.2、 复合类型

复合类型主要有array【数组】、object【对象】、nested【嵌套】。

  • 数组类型

Lucene底层并不真正支持数组类型,所以Elasticsearch也没有专用的数组类型,依据数组元素类型定,每个元素必须是同一种类型。例如:tag:[1,2,3]、["a","b","c"]、[{"name":"Trump"},{"name":"Smith"}]是合法的。

{
  "mappings": {
    "properties": {
      "tag": {
        "type": "keyword" 
      }
    }
  }
}
  • Object类型

JSON字符串允许嵌套对象,一个文档可以嵌套多个、多层对象。可以通过object类型来存储二级文档,不过由于Lucene并没有内部对象的概念,Elasticsearch会将原JSON文档扁平化,例如文档:

{ "name": { "first": "Donald", "last": "Trump" } } --> { "name.first": "Donald", "name.last": "Trump" }【实际上ES会将其转换为以下格式,并通过Lucene存储】。

  • nested类型【"nested"

nested类型是特殊的对象类型,特殊的地方是索引对象数组方式不同,允许数组中的对象“各自地进行索引”【也是各个元素类型可以不一致】,目的是对象之间彼此独立被查询出来。

{ 
    "mappings": { 
        "properties": { 
            "actors":{
                "type": "nested",
                "properties":{
                    "id":{
                        "type": "keyword",
                        "store":true
                    },
                    "name":{
                        "type": "keyword",
                        "store":true
                    },
                    "sex":{
                        "type": "integer",
                        "store":true
                   }
               }
            }
        } 
    } 
}

1.2.3、多字段特性

多字段特性(multi-fields),表示允许对同一字段采用不同的配置,比如分词、text.keyword等。

常见例子是对人名实现拼音搜索,只需要在人名中新增一个字段pinyin即可。

1.2.4、 特殊类型

特殊类型:地理类型、 ip(记录ip地址)、completion(实现自动补全)、token_count(记录分词数)、murmur3(记录字符串hash值)。

  • 地理类型

地理类型分为两种:经纬度类型地理区域类型,这里主要介绍经纬度类型:经纬度类型

经纬度类型自动可以存储经纬度相关信息。通过地理类型的字段,可以用来实现诸如查找在指定地理区域内相关的文档、距离某个点范围内的文档、根据地理位置修改搜索评分等需求。如果需要使用经纬度类型,需要手动定义映射

{
"mappings": {
"properties": {
"location": {
"type": "geo_point"
}
}
}
}

geo_point(geo_point 接受以下地理位置数据)

#经纬度键值对,lat为纬度,lon为经度
{
"location": {
"lat": 29.0,
"lon": 107.0
}
}
#用逗号分割的字符串,前者为纬度,后者为经度
{
"location": "29.0,107.0"
}
#数组形式坐标
{
"location": [29.0,-50.0]
}


  • IP类型

可以用来存储IPv4或者IPV6地址,如果需要存储IP类型的字段,需要手动定义映射

#ip类型
PUT my_index
{
"mappings": {
"properties": {
"ip_addr": {
"type": "ip"
}
}
}
}
PUT my_index/_doc/1
{
"ip_addr": "192.168.1.1"
}
GET my_index/_search
{
"query": {
"term": {
"ip_addr": "192.168.0.0/16"
}
}
}
# ip_range类型
PUT range_index
PUT range_index/_mapping
{
"properties": {
"ip_whitelist": {
"type": "ip_range"
}
}
}
PUT range_index/_doc/2
{
"ip_whitelist" : "192.100.0.0/16"
}
PUT range_index/_doc/3
{
"ip_whitelist" : "192.100.100.0/24"
}
GET /range_index/_search
{
"query": {
"range": {
"ip_whitelist": {
"gte": "192.100.0.0",
"lte": "192.100.88.0"
}
}
}
}

  • join类型

join类型是ES 6.x引入的类型,以取代淘汰_parent元字段。用来实现文档的一对一、一对多的关系。ES一般不用来做关系映射。


1.3、字段属性

  • 限制的是搜索、存储、聚合、排序等特性。

类型

说明

_source

默认是打开的,将原始文档以JSON的形式存储在_source字段中,在lucene中_source只是一个字段,即在一个字段中存储了一个文档中所有字段的值。_source是es层面的设置,相当于给lucene多加了一个字段用于存储整个原始文档的值。不需要读取原始字段内容可以考虑关闭,但是关闭后无法reindex无法在查询中使用script。

enabled

默认值为true,设置字段是否索引分析。false 仅存储,不做搜索和聚合分析。(只在_source中存储,关闭index和doc_values操作)【不能查询、聚合,可以在_source展示

index

默认为true,控制当前字段是否索引,是否analyze,是否加入倒排索引,关闭后无法对其进行搜索比如:手机号、身份证号等敏感信息,不希望被检索】但字段仍存储在_source和doc_values,即字段可以被排序和聚合。【不能查询,可以聚合,在_source展示

index_options

用于控制倒排索引记录的内容。

docs: 只记录doc id;freqs: 记录doc id 和term frequencies;positions: 记录doc id、term frequencies和term position;offsets: 记录doc id、term frequencies、term position和character offsets。

text类型默认配置为positions,其他默认为docs 。

store

默认是false,即为不存储该字段;如果该字段的store属性设置为true,则在lucene中该字段的值被单独存储,与_source是相互独立的,同时开启会将该原始字段存储两份。【指定stored_fields才能查询store字段

doc_values

倒排索引可以提供全文检索的能力,但是无法对排序和聚合提供支持。doc_values本质是一个序列化的列式存储。这个结构非常适用于聚合,排序。es默认几乎为所有类型提供列doc_value支持。除了analyzed类型,而text字段就是analyzed类型,如果不需要对该字段排序或聚合。则关闭该字段的doc_value存储。

field_data

默认值为false。实现聚合和排序,不过它是基于内存

norms

默认值为true是否需要对字段做打分操作

dynamic

mapping级别的设置控制字段的新增true默认值,允许新增字段和写入;false不允许新增字段,文档可以写入,但是无法查询;strict文档不能写入。

coerce

默认是true。是否开启数据类型转换功能,比如数字类型字符串转数字。

multifields

多字段,灵活的使用多字段特性来解决多样的业务需求。

date_detection

默认是true。是否字段识别日期类型。

null_value

当字段遇到null值时的处理策略,默认为null,即空值,此时es会忽略该值。可以通过设定字段的默认值。

eager_global_ordinals

默认值为false指明该字段是否加载全局序数?默认为false,不加载。 对于经常用于术语聚合的字段,启用此功能是个好主意。

ignore_above

默认值是256,该参数的意思是,当字段文本的长度大于指定值时,不会被索引【不能搜索】,但是会存储【可以随数据一起显示】


1.4、mapping字段设置流程

1.4.1、字段是何种类型

  • 字符串类型----需要分词则设定为text类型,否则设置为keyword类型。
  • 枚举类型----基于性能考虑将其设定为keyword类型,即便该数据为整型。
  • 数值类型----尽量选择贴近最大值的类型,比如byte即可表示所有数值时,即选用byte,节省空间。
  • 其他类型----比如布尔类型、日期、地理位置数据等。

1.4.2、是否需要检索

  • 完全不需要检索、排序、聚合分析的字段 ----enabled设置为false(此字段值会出现在_source中)
  • 不需要检索的字段----index设置为false
  • 需要检索的字段index_options结合需要设定norms不需要归一化数据时关闭即可

1.4.3、是否需要排序和聚合分析

不需要排序或者聚合分析功能

  • doc_values设定为false
  • fielddata设定为false

1.4.4、是否需要另行存储

  • store 设定为true,即可存储该字段的原始内容(与_source中的不相关)
  • 一般情况是结合_source的enabled设定为false时 使用该属性

索引

  • 索引创建后,分片不能修改,副本数可修改。


二、搜索原理

Elasticsearch是分布式搜索引擎,因此提供的所有功能都是分布式的。默认情况下,ES的查询分为两个阶段,“query阶段”和“fetch阶段”。


  • query阶段
  1. 客户端发送一个search请求到Node3【任意节点】上,然后Node3会创建一个优先级队列,它的大小=from+size【假设:from=90,size=10
  2. Node3转发这个search请求到索引里面每一个主shard或者副本shard上,每个shard会在本地查询然后添加100条结果到本地的排序好的优先级队列里面。
  3. 每个shard返回docId和所有参与排序字段的值例如_score到优先级队列里面,然后再返回10条给coordinating节点也就是Node 3,然后Node 3负责将所有shard里面的数据给合并到一个全局的排序的列表,“再取10条


  • fetch阶段
  1. coordinating 节点标识了那些document需要被拉取出来【合并后的10条】,并发送一个批量的mutil get请求到相关的shard上
  2. 每个shard加载相关document,如果需要数据在当前shard,则当前shard将会返回数据到coordinating 节点上
  3. 一旦所有的document被拉取回来,coordinating节点将会返回结果集到客户端上


注意:

  • 当查询from=90,size=10时,即在每个分片都有一个优先级队列,都会查询100条,然后返回10条给“协调节点”,若有5个分片,协调节点就有50条数据,然后取10条。
  • query阶段,每个shard只返回docId和所有参与排序字段的值例如_score,在fetch阶段再通过docId查询_source里数据返回客户端。
  • 查询过程分类 query and fetch(最快的)、 query then fetch( es 默认的搜索方式)、 DFS query and fetch(精确控制搜索打分)、 DFS query then fetch(精确控制搜索打分和排名,最慢的)
  • 从性能考虑 QUERY_AND_FETCH 是最快的, DFS_QUERY_THEN_FETCH 是最慢的。从搜索的准确度来说, DFS 要比非 DFS 的准确度更高。


三、搜索分类

3.1、按结构分类

3.1.1、URL

URI 搜索方式通过URI参数来指定查询相关参数。

  • GET /alibaba/employee/_search?q=last_name:Smith //精确 where last_name like '%Smith%'
  • GET /website/_search?q=+name:trying +date:2014 //where name like '%trying%' or date like '%2014%',+前缀表示必须与查询条件匹配
GET /_search //搜索所有的索引中所有的类型
GET /alibaba/_search //在alibaba索引中搜索所有的类型
GET /alibaba,kxtx/_search //在alibaba和kxtx索引中中搜索所有的文档
GET /m*,k*/_search //在任何以m或者k开头的索引中搜索所有的类型
GET /alibaba/employee/_search //在alibaba索引中搜索employee类型
GET /gb,us/user,tweet/_search //在gb和us索引中搜索user和tweet类型
GET /_all/user,tweet/_search //在所有的索引中搜索user和tweet 类型
  
{undefined
   "hits" : {                          //最重要的部分
      "total" :       14,             //匹配到的文档总数
      "hits" : [                         //文档结果集
        {undefined
          "_index":   "us",
          "_type":    "tweet",
          "_id":      "7",
          "_score":   1,               //它衡量了文档与查询的匹配程度,默认是按照_score降序排列的
          "_source": {                 //原始文档
             "date":    "2014-09-17",
             "name":    "John Smith",
             "tweet":   "The Query DSL is really powerful and flexible",
             "user_id": 2
          }
       },
        ... 9 RESULTS REMOVED ...
      ],
      "max_score" :   1              //与查询所匹配文档的_score的最大值
   },
   "took" :           4,                  //整个请求耗费了多少毫秒
   "_shards" : {                        //查询中参与分片的总数
      "failed" :      0,                  //正常情况下我们不希望分片失败,但是分片失败是可能发生的(遭遇到一种灾难级别的故障:丢失了相同分片的原始数据和副本)
      "successful" :  10,
      "total" :       10
   },
   "timed_out" :      false          //查询是否超时,默认情况下,搜索请求不会超时  
}


3.1.2、DSL

Request body 搜索方式以JSON格式在请求体中定义查询query。

GET /twitter/_search
{
"query" : {
"term" : { "user" : "kimchy" }
}
}

3.2、按复杂度分类

注:包含URL和DSL,下面着重展示DSL。

  • 简单查询
  • 复合查询
  • 聚合查询

首层关键字



query

查询

aggs

聚合

sort

排序。可以指定按一个或多个字段排序。也可通过_score指定按评分值排序,_doc 按索引顺序排序。默认是按相关性评分从高到低排序。对于值是数组或多值的字段,也可进行排序,通过mode参数指定按多值的(min/max/sum/avg/median)。

GET /bank/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order": "desc" //
如果不给定,默认是asc,_score默认是desc
} },
{
"balance": {
"order": "asc"
} },
"_score"
]
}

From / Size

分页查询,请注意,From+Size不能超过index.max_result_window索引设置,默认值为10000。

在分布式系统中,对结果排序的成本随分页的深度成指数上升。这就是web搜索引擎对任何查询都不要返回超过1000个结果的原因:假设在一个有5个主分片的索引中搜索,size=10(每一个分片产生前10的结果,返回给协调节点,协调节点对50个结果排序得到全部结果的前10个),size=10&from=10000(每个分片不得不产生前10010个结果,然后协调节点对全部 50050 个结果排序最后丢弃掉这些结果中的50040个结果)。要解决它,请参阅scroll或search after api以获得更有效的深度滚动方法。

GET /_search
{

"from" : 0, "size" : 10,
"query" : {"term" : { "user" : "kimchy" }}
}

Search After

在指定文档后取文档, 可用于深度分页

search_after的理念是,在不同分片上(假设有5个分片),先按照指定顺序排好,根据我们传的search_after值 ,然后仅取这个值之后的size个文档。这 5*size 个文档拿到Es内存中排序后,返回前size个文档即可

GET twitter/_search
{
"size": 10,
"query": {
"match" : {
"title" : "elasticsearch"
}},
"search_after": [1463538857, "654323"],
"sort": [
{"date": "asc"},
{"_id": "desc"}
]}

注意:使用search_after,必须有全局唯一id来排序(最好排序中包含_id字段)。 取满足search_after值的数据,再取size条数据。

Scroll

与在传统数据库中使用光标的方式非常相似(游标查询允许我们先做查询初始化,然后再批量地拉取结果)。Scroll可以有效地执行大批量的文档查询,而又不用付出深度分页那种代价。它仅仅从还有结果的分片返回下一批结果。

游标查询会取某个时间点的快照数据,查询初始化之后索引上的任何变化会被它忽略。

深度分页的代价根源是结果集全局排序,如果去掉话查询结果的成本就会很低。游标查询用字段_doc 来排序。

GET /old_index/_search?scroll=1m //保持游标查询窗口一分钟
{
"query": { "match_all": {}},
"sort" : ["_doc"], //_doc是最有效的排序顺序
"size": 1000 / /仅作用于单个分片,有可能取到超过这个值数量,
}
{//返回结果
"scroll": "1m",
"scroll_id" : "cXVlcnlUaGVuRmV0Y2..."
}
_scroll_id,每次请求会返回一个新值(它是一个base64编码的长字符),每次我们做下一次游标查询,我们必须把前一次查询返回的字段_scroll_id传递进去,当没有更多的结果返回的时候才算结束

_source

对返回的_source字段进行选择

GET /_search
{//或者"_source": "obj.*" 或者 "_source": [ "obj1.*", "obj2.*" ]
"_source": {
"includes": [ "obj1.*", "obj2.*" ],
"excludes": [ "*.description" ]
},
"query" : {
"term" : { "user" : "kimchy" }
}
}

Fields

指定返回哪些stored字段【没有stored字段,就不会返回】,* 可用来指定返回所有存储字段,不会返回_source

GET /_search
{
"stored_fields" : ["user", "postDate"],
"query" : {
"term" : { "user" : "kimchy" }
}
}

Script Fields

脚本来对命中的每个文档的字段进行运算后返回

Doc value Fields

返回存储了doc Value的字段值,文档值是一种磁盘上的数据结构,以面向列的方式存储,这对于排序和聚合有效。

GET /_search
{
"query" : {
"match_all": {}
},
"docvalue_fields" : ["test1", "test2"]
}

Post filter

后置过滤:在查询命中文档、完成聚合后,再对命中的文档进行过滤

{"query": {
"match_all": {}
},
"post_filter": { //先查询再过滤
"term": {
"price": 30
}}}

还有一个对应的反向的操作,filtered(先过滤再查询,速度快),不过已废弃。filtered 查询将替换为bool查询。

{ "query": {
"filtered": {
"query": {
"match": {
"match_all": {}
}},
"filter": {
"term": {
"price": 30
}}}}}

Highlighting

高亮显示

Explain

对每次命中的分数计算方式启用解释

GET /_search
{
"explain": true,
"query" : {
"term" : { "user" : "kimchy" }
}
}

Rescoring

目前,rescore API只有一个实现:rescorer查询,它使用查询来调整评分。在将来,可提供替代的再订阅者,例如,成对的再订阅者。rescorer查询 仅对查询和后筛选阶段返回的top-k结果执行第二个查询。每个分片上要检查的文档数可以由window_size参数控制,该参数默认为10。默认情况下,原始查询和rescorer存储查询的分数是线性组合的,以生成每个文档的最终分数。原始查询和rescorer查询的相对重要性可以分别通过查询权重和rescorer查询权重来控制。两者都默认为1。

POST /_search
{
"query" : {
"match" : {
"message" : {
"operator" : "or",
"query" : "the quick brown"
}
}
},
"rescore" : {
"window_size" : 50,
"query" : {
"rescore_query" : {
"match_phrase" : {
"message" : {
"query" : "the quick brown",
"slop" : 2
}
}
},
"query_weight" : 0.7,
"rescore_query_weight" : 1.2
}
}
}

Preference

选择在那些分片上执行查询,它有_primary、_primary_first、_replica..

Version

指定返回文档的版本字段

GET /_search
{
"version": true,
"query" : {
"term" : { "user" : "kimchy" }
}
}

Index Boost

允许在搜索多个索引时为每个索引配置不同的boost level。当来自一个索引的点击比来自另一个索引的点击更重要时(想想每个用户都有一个索引的社交图),这非常方便

min_score

限制最低评分得分

GET /_search
{
"min_score": 0.5,
"query" : {
"term" : { "user" : "kimchy" }
}
}

Named Queries

每个过滤器和查询可以在其顶级定义中接受一个名称。

GET /_search
{
"query": {
"bool" : { //bool查询只对查询和过滤器有意义
"should" : [
{"match" : { "name.first" : {"query" : "shay", "_name" : "first"} }},
{"match" : { "name.last" : {"query" : "banon", "_name" : "last"} }}
],
"filter" : {
"terms" : {
"name.last" : ["banon", "kimchy"],
"_name" : "test"
}
}
}
}
}

Inner hits

父联接和嵌套功能允许返回在不同范围内具有匹配项的文档。

Field Collapsing

用 collapse指定根据某个字段对命中结果进行折叠

3.2.1、基本查询

  • Query查询和Filter查询

DSL查询分为:Query查询和Filter查询(查询(Query)和过滤(filter),查询即是之前得到的query查询,它(查询)默认会计算每个返回文档的得分,然后根据得分排序,而过滤(filter)直会筛选出符合的文档,并不计算得分,且它可以缓存文档,所以,单从性能考虑,过滤比查询更快)



Query context

查询上下文,用在查询上下文中的子句回答“这个文档有多匹配这个查询?”。除了决定文档是否匹配,字句匹配的文档还会计算一个子句评分,来评定文档有多匹配。查询上下文由 query 元素表示。

查询器有:
match_all、match、multi_match、bool、wildcards、regexp、prefix、match_phrase

Filter context

过滤上下文,过滤上下文由 filter 元素或 bool 中的 must not 表示。用在过滤上下文中的字句回答“这个文档是否匹配这个查询?”,过滤语句的目的就是缩小匹配的文档结果集,不参与相关性评分。被频繁使用的过滤器将被ES自动缓存,来提高查询性能。

过滤器有:
term、terms、range、exists、missing、bool、script、type、ids


3.2.2、复合查询

  • 复合查询模型:
{
"query": {//1、查询   
        "bool": {//2、复合 bool、boosting、constant_score、dis_max、function_score
            "must": [//3、复合关联词 must、filter、should、must_not
                {
                    "match": { //4、简单查询关键字match、range、term....
                        "title": "Search" //5、查询的字段
                    }
                },
                {
                    "match": {
                        "content": "Elasticsearch"
                    }
                }
            ],
            "filter": [
                //过滤
                {
                    "term": {
                        "status": "published"
                    }
                },
                {
                    "range": {
                        "publish_date": {
                            "gte": "2015-01-01"
                        }
                    }
                }
            ]
        }
    }
}


大类

子类

说明

bool

must

包含

filter

过滤

should

可能

must_not

排除

boosting query

positive

用来区别must、must_not组合中的must_not粗暴过滤。positive包含,negative按照negative_boost分数降低结果顺序。

就是相近的不直接排除,而是排在后面。【查询“苹果公司”,苹果树排在后面】

{ "query": { "boosting": { "positive": { "match": { "content": "apple" } }, "negative": { "match": { "content": "pie" } }, "negative_boost": 0.5 } } }

negative

negative_boost

constant_score

filter

目的就是返回指定的score,一般都结合filter使用,因为filter context忽略score

dis_max

queries

只是取分数最高的那个query的分数而已。

GET /_search { "query": { "dis_max" : { "queries" : [ { "term" : { "title" : "Quick pets" }}, { "term" : { "body" : "Quick pets" }} ], "tie_breaker" : 0.7 } } }

function_score


function_score是处理分值计算过程的终极工具


3.2.3、聚合查询

  • 聚合查询模型
{
    "aggs": {//查询类型
        "trigger": {//操作自定义名称
            "date_histogram": {//聚合类型【Metric、Bucketing】
                "field": "triggerTime", 
                "format": "yyyy-MM-dd"
            }
        }
    }
}

分类

子类

关键字

含义

例子

Metric(指标)

  • 它对文档进行权值计算,类似sql中的统计


单值分析

min

最小值


max

最大值


avg

平均值


sum

总数


cardinality

distinct聚合


多值分析

stats

min、max、sum、count、avg5个值一起输出


extended_stats

比stats多的维度:平方和、方差、标准差、平均值加/减两个标准差的区间


percentile

百分比统计


percentile_rank

百分比排名聚合


top hits

最高匹配权值聚合


geo_bounds

地理边界聚合


geo_centroid

地理重心聚合


Bucketing(桶)

  • 它执行的是对文档分组的操作(与sql中的group by类似)



terms

词聚合(字段分组group by)


filter

过滤聚合


filters

多过滤聚合


range

范围分组聚合(按区间分组统计count)


date_range

日期范围聚合


ip_range

【IPv4范围】的桶分聚合


missing

缺失值的桶聚合


Pipeline(管道)

  • 聚合基础上的聚合【从其他聚合而不是文档或字段获取输入的聚合。】
  • 包含bucket_path指定上一层的路径


parent父聚合

max_bucket、min_bucket、avg_bucket、the sum_bucket


{"size": 0,
"aggs": {
"sales_per_month": {
"date_histogram": {
"field": "date",
"interval": "month"
},
"aggs": {
"sales": {
"sum": {
"field": "price"
}}}},
"avg_monthly_sales": {
"avg_bucket": {
"buckets_path": "sales_per_month>sales"
}}}}


stats、extended_status_bucket、percentiles_bucket




sibling兄弟聚合

derivative(求导)、cumulative sum(累计求和)、moving function(滑动窗口)


{"aggs" : {
"sales_per_month" : {
"date_histogram" : {
"field" : "date",
"interval" : "month"
},
"aggs": {
"sales": {
"sum": {
"field": "price"
}},
"sales_deriv": { //对每个月销售总和 sales 求导
"derivative": {
"buckets_path": "sales" //用于计算均值的权值路径,同级,直接用metric值
}}}}}}

Matrix(矩阵)





3.3、样例

GET /1400_notification_log_202210/_search
{
  "size": 1,//返回条数
  "query": {
    "bool": {//复合查询关键字
      "must": [
        {
          "match_phrase": {//简单查询
            "viewType": "10.117.58.88:80"
          }
        },
        {
          "range": {
            "notificationTime": {
              "gte": "2022-10-31 00:00:00.000",
              "lte": "2022-10-31 23:59:59.999"
            }
          }
        }
      ]
    }
  },
  "aggs": {//聚合查询关键字
    "fenzu": {
      "terms": {//桶
        "field": "objectType"
      }
    },
    "distinct-objectTyp": {
      "cardinality": {//指标
        "field": "objectType"
      }
    },
    "callInterfaceTimeSlot_range": { //【接口调用时间按照0~20,20~100,100以上分组统计】
      "range": {
        "field": "callInterfaceTimeSlot",
        "ranges": [
          {
            "to": 20
          },
          {
            "from": 20,
            "to": 100
          },
          {
            "from": 100
          }
        ]
      },
      "aggs": {//嵌套【对每个区间】
        "b_stats": {
          "stats": {
            "field": "callInterfaceTimeSlot"
          }
        }
      }
    },
    "dataLength_statictis": {
      "date_histogram": {//【抓拍接收时间,按小时分段统计】
        "field": "checkTime",
        "interval": "hour"
      },
      "aggs": {
        "dataLength_sum": {
          "sum": {//【计算每个分段数据总大小】
            "field": "dataLength"
          }
        }
      }
    },
    "avg_dataLength": {//管道【父子】
      "avg_bucket": {
        "buckets_path": "dataLength_statictis>dataLength_sum" //【计算总大小】
      }
    }
  }
}
{
  "took" : 10,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1266,
      "relation" : "eq"
    },
    "max_score" : 1.0020769,
    "hits" : [],
    
  "aggregations" : {
    "dataLength_statictis" : {
      "buckets" : [
        {
          "key" : 1667188800000,
          "doc_count" : 318,
          "dataLength_sum" : {
            "value" : 603246.0
          }
        },
        {
          "key" : 1667192400000,
          "doc_count" : 360,
          "dataLength_sum" : {
            "value" : 682920.0
          }
        },
        {
          "key" : 1667196000000,
          "doc_count" : 360,
          "dataLength_sum" : {
            "value" : 682920.0
          }
        },
        {
          "key" : 1667199600000,
          "doc_count" : 222,
          "dataLength_sum" : {
            "value" : 421134.0
          }
        }
      ]
    },
    "fenzu" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "12",
          "doc_count" : 1260
        },
        {
          "key" : "3",
          "doc_count" : 6
        }
      ]
    },
    "distinct-objectTyp" : {
      "value" : 2
    },
    "callInterfaceTimeSlot_range" : {
      "buckets" : [
        {
          "key" : "*-20.0",
          "to" : 20.0,
          "doc_count" : 131,
          "bmax" : {
            "count" : 131,
            "min" : 14.0,
            "max" : 19.0,
            "avg" : 18.18320610687023,
            "sum" : 2382.0
          }
        },
        {
          "key" : "20.0-100.0",
          "from" : 20.0,
          "to" : 100.0,
          "doc_count" : 1104,
          "bmax" : {
            "count" : 1104,
            "min" : 20.0,
            "max" : 36.0,
            "avg" : 22.08786231884058,
            "sum" : 24385.0
          }
        },
        {
          "key" : "100.0-*",
          "from" : 100.0,
          "doc_count" : 25,
          "bmax" : {
            "count" : 25,
            "min" : 868.0,
            "max" : 1213.0,
            "avg" : 1049.44,
            "sum" : 26236.0
          }
        }
      ]
    },
    "avg_dataLength" : {
      "value" : 597555.0
    }
  }
}


四、数据处理

4.1、script : ctx._source应用

PUT /person_test/_doc/1

{

"name" : "zhangsan",

"age":20,

"cards.sfz":"12121",

"hairs" : ["red"],

"createTime":1666753812000

}
  • 查询【字段修饰】
GET /person_test/_search
{
  "script_fields": {
    "result": {
      "script": {
        "source":"doc['age'].value*params['multiplier']",
        "params": {"multiplier":2}
      }
    }
  }
}
结果:
{
        "_index" : "person_test",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "fields" : {
          "result" : [
            40
          ]
        }
      }
  • 内容更新【多字段更新、传参更新、判断更新、查询更新】
//传参更新
POST /person_test/_update/1
{
  "script" : {
        "source": "ctx._source.age += params.count",
        "lang": "painless",
        "params" : {
            "count" : 4
        }
    }
}
结果:age=24
 
//数组添加元素
POST /person_test/_update/1
{
    "script" : {
        "source": "ctx._source.hairs.add(params.hair)",
        "lang": "painless",
        "params" : {
            "hair" : "blue"
        }
    }
}
结果:hairs=red、blue
 
//判断更新
POST /person_test/_update/1
{
    "script" : {
        "source": "if (ctx._source.hairs.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'none' }",
        "lang": "painless",
        "params" : {
            "tag" : "blue"
        }
    }
}
 
//多字段更新【简写】
POST /person_test/_update/1
{
  "script": "ctx._source.age=20;ctx._source.name += '程飞'"
}
  • 匹配插入
POST /person_test/_update_by_query
{
  "script": {
    "lang": "painless",
    "source": "ctx._source.name = params.par"
    , "params": {
      "par":"程飞"
    }
  },
  "upsert": {
    "name":"wangwu",
    "age":30
  }
}
结果:插入新数据
  • 新增字段
POST /person_test/6/_update
{
    "script" : "ctx._source.new_field = 'value_of_new_field'"
}
 
结果
{
  "_index": "person",
  "_id": "6",
  "_version": 4,
  "found": true,
  "_source": {
    "name": "zhangsan",
    "age": 24,
    "cards.sfz":"12121",
    "hairs": [
      "red",
      "blue"
    ],
    "new_field": "value_of_new_field"
  }
}
  • 删除字段
POST /person_test/_update_by_query
{
  "script":"ctx._source.remove('cards.sfz')",
  "query": {
            "bool": {
                "must": [
                    {
                        "exists": {
                            "field": "cards.sfz"
                        }
                    }
                ]
            }
        }
}
结果:_source中无cards.sfz字段

4.2、分页

//from\size
GET /test_student_new/_search
{
  "query": {
    "bool":{
      "must": [
        {"match": {
          "grade": "初二"
        }}
      ]
    }
  },
  "sort": [
    {
      "id": {
        "order": "asc"
      }
    }
  ],
  "from": 0,
  "size": 3
}
   
 //search_after
GET test_student_new/_search
{
  "query": {
    "bool":{
      "must": [
        {"match": {
          "grade": "初二"
        }}
      ]
    }
  },
  "sort": [
    {
      "id": {
        "order": "asc"
      }
    }
  ],
  "size": 2,
  "search_after":[
    3
    ]
}
  
  //scroll
POST test_student_new/_search?scroll=1m
{
  "size":2,
  "query": {
    "match_all": {
       
    }
  }
}
  
POST /_search/scroll
{
  "scroll":"1m",
  "scroll_id":"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAxETQWdzZhZWdPWlFRQW1kRkpaQkxjNU9PUQ=="
}

4.3、排序

4.4、reindex

  • 当需要修改字段类型时,需要重新创建索引,并同步数据。
#
#1、创建新索引
PUT student_test_new
{
"mappings": {
"properties": {
"age": {
"type": "long"
},
"bir": {
"type": "date"
},
"bri": {
"type": "date"
},
"grade": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"id": {
"type": "long"
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"nation": {
"type": "text"
}
}
}
}
 
//2、数据同步
POST _reindex
{
"source": {
"index": "student_test"
},
"dest": {
"index": "student_test_new"
}
}

4.5、rollover

  • 为索引添加alias是别名,别名绑定_rollver,手动执行_rollver 满足条件后可以创建新索引【是执行_rollver创建索引,不是插入数据】、【_ilm/policy 自动执行 默认是10分钟】。
  • _rollver 滚动索引时,要求创建索引以数字结尾,最多6位,滚动时,会在数字上加1,默认是000001这6位数字。
  • 当我们在ILM中开启了rollover后,流转到下一节点的min_age是在rollover触发后才开始计时的。
  • 当别名关联索引时【一对多】:通过索引【test_xxxxxx】随时可以写数据。但是通过别名【test_alais】,只有关联的索引【test_000001】是is_write_index,才能通过别名【test_alais】插入数据。同一个索引【test】的滚动【test_000001、test_000002】,默认只有一个is_write_index为true的索引【test_000002】,就是别名【test_alais】只能写到这个索引中【test_000002】。
  • 当索引添加别名时【一对多】:当添加的别名【test_alais1】设置了is_write_index属性,原别名【test_alais】会继续与索引关联,如果没有is_write_index属性,原别名【test_alais】就会与该索引解除关联,也就是通过原别名不能查询该索引的数据。
//创建模板【创建setting、mapping关联alais、ilm(rollover)】【_template---alais---rollover---data】
PUT _template/student_test_template
{
  "index_patterns": "student_test-*",//匹配索引的规则
  "order": 1,
  "settings": {
    "number_of_shards": 4,
    "number_of_replicas": 1,
    "lifecycle": {
      "name": "student_test_ilm_policy",//ilm
      "rollover_alias": "alias_student_test_rollover"//alias
    }
  },
  "mappings": {
    "properties": {
       "name": {
          "type": "keyword"
        },
        "age": {
          "type": "long"
        },
        "birthday": {
          "type": "date",
          "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
        }
    }
  },
   "aliases": {
    "alias_student_test_rollover" : {
      "is_write_index":true
    }
  }
}
 
 
==_ilm【自动_rollover】============================================================
 
//设置_ilm【自动滚动】
PUT /_ilm/policy/student_test_ilm_policy
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_age": "30d",
            "max_size": "100gb",
            "max_docs": 3
          }
        }
      },
      "delete": {
        "min_age": "120d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}
 
//添加数据【“别名”或者student_test-000001要包含后缀】
POST /alias_student_test_rollover/_doc/5
{
  "name": "zhangsan5",
  "age": 21,
  "birthday": 1666753812000
}
 
GET /alias_student_test_rollover/_search
 
GET /alias_student_test_rollover/_alias
 
GET /_ilm/policy
 
 
==手动_rollover============================================================
//添加aliases
PUT student_test-000001
{
  "aliases": {
    "alias_student_test_read": {
      "is_write_index":true
    }
  }
}
 
//添加_rollover【手动滚动,执行此句时,当满足条件就会创建新索引,创建索引是执行此触发的,插入数据,就会插入新索引】
POST /alias_student_test_read/_rollover
{
  "conditions": {
    "max_docs": 2,
    "max_age": "30d",
    "max_size": "500gb"
  }
}
 
POST /alias_student_test_read/_doc/12
{
  "name": "lisi12",
  "age": 21,
  "birthday": 1666753812000
}
 
 
GET /alias_student_test_read/_search

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表