在这里插入图片描述

You Know, for Search

ElasticSearch官网 开启搜索的新境界

   Elasticsearch 是一个开源的搜索引擎建立在一个全文搜索引擎库 Apache Lucene™ 基础之上。 Lucene 可以说是当下最先进、高性能、全功能的搜索引擎库。但是 Lucene 仅仅只是一个库。为了充分发挥其功能,你需要使用 Java 并将 Lucene 直接集成到应用程序中。 更糟糕的是,您可能需要获得信息检索学位才能了解其工作原理。Lucene 非常复杂。Elasticsearch 也是使用 Java 编写的,它的内部使用 Lucene 做索引与搜索,但是它的目的是使全文检索变得简单, 通过隐藏 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API。

Elasticsearch 不仅仅是 Lucene,并且也不仅仅只是一个全文搜索引擎。 它可以被下面这样准确的形容:

  • 一个分布式的实时文档存储,每个字段可以被索引与搜索
  • 分布式实时分析搜索引擎
  • 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据

   Elasticsearch将所有的功能打包成一个单独的服务,这样你可以通过程序与它提供的简单的 RESTful API 进行通信, 可以使用自己喜欢的编程语言充当 web 客户端,甚至可以使用命令行去充当这个客户端。

Kibana

Kibana官网

  Kibana是一个针对Elasticsearch的开源分析及可视化平台,用来搜索查看交互存储在Elasticsearch索引中的数据。使用Kibana,可以通过各种图表进行高级数据分析及展示。Kibana让海量数据更容易理解。它操作简单,基于浏览器的用户界面。可以快速创建仪表板、实时显示Elasticsearch查询动态。
在这里插入图片描述

Es核心概念

1、关系型数据库和ES对比

Relational DBElasticSearch
databaseindex
tablestypes
rowsdocuments
columnsfields
schemamapping
InnodbLucene

2、物理设计

  elasticsearch把每个索引划分成多个分片每个分片可以在集群中的不同服务器间迁移。默认的集群名elasticsearch,docker中默认的集群为docker-cluster。
在这里插入图片描述

3、逻辑设计

​ ​ 一个索引包含多个文档,而索引存储在分片当中。当我们索引一篇文档时可以通过索引-类型-文档ID的顺序找到文档。

3.1 文档

elasticsearch是面向文档的,那么就意味着索引和搜索数据的最小单位是文档

  • 自我包含:一篇文档同时包含字段和对应的值
  • 可以是层次型的:一个文档中包含自文档,复杂的逻辑实体就是这么来的
  • 灵活的结构:文档不依赖预先定义的模式
3.2 类型

​ ​ 类型是文档的逻辑容器,就像关系型数据库一样,表格是行的容器。类型中对于字段的定义称为映射,比如name映射为字符串类型。文档是无模式的,不需要拥有映射中所定义的所有字段,比如新增一个字段,ES会自动的将新字段加入映射,但是这个字段的不确定它是什么类型,ES就开始猜,如果这个值是一个数,那么ES会认为它是整形。但是ES也可能猜不对,所以最安全的方式就是提前定义好所需要的映射,然后再使用。

3.3 索引

​ ​ 索引即数据库,是映射类型的容器,elasticsearch中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置。然后它们被存储到了各个分片上了。

在这里插入图片描述

4、节点和分片

​​ ​ 一个集群至少有一个节点,而一个节点就是一个ES进程,节点可以有多个索引,如果你创建索引,那么索引将会由5个分片(primary shard)构成,每一个主分片会有一个副本(replica shard)
在这里插入图片描述

  上图是一个有3个节点的集群,可以看到主分片和对应的复制分片都不会在同一个节点内,这样有利于某个节点挂掉了,数据也不至于丢失。实际上,一个分片是一个Lucene索引,一个包含倒排索引的文件目录,倒排索引的结构使得elasticsearch在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字。

5、倒排索引

   索引存在的意义就是为了加快数据的查询。在关系型数据库中如果没有索引的话,为了查找数据我们需要每条数据去进行比对,运气不好的话可能需要扫描全表才能查找到想要的数据。以Mysql为例,它使用了B+树作为索引来加速数据的查询。

​​ ​ 所谓正排索引就像书中的目录一样,根据页码查询内容,但是倒排索引确实相反的,它是通过对内容的分词,建立内容到文档ID的关联关系。这样在进行全文检索的时候,根据词典的内容便可以精确以及模糊查询,非常符合全文检索的要求。
在这里插入图片描述
​​ ​ 倒排索引的结构主要包括了两大部分一个是Term Dictionary,另一个是Posting ListTerm Dictionary记录了所用文档的单词以及单词和倒排列表的关系Posting List则是记录了term在文档中的位置以及其他信息,主要包括文档ID,词频(term在文档中出现的次数,用来计算相关性评分),位置以及偏移。

Restful风格

​​ ​ 一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

methodurl地址描述
PUTlocalhost:9200/索引名称/类型名称/文档id创建文档(指定文档id)
POSTlocalhost:9200/索引名称/类型名称创建文档(随机文档id)
POSTlocalhost:9200/索引名称/类型名称/文档id/_update修改文档
DELETElocalhost:9200/索引名称/类型名称/文档id删除文档
GETlocalhost:9200/索引名称/类型名称/文档id查询文档通过文档id
POSTlocalhost:9200/索引名称/类型名称/_search查询所有数据

环境搭建

docker + elasticsearch7.6 + kibana7.6

1、拉取镜像

# 注意 elasticsearch和kibana版本要一致

docker pull elasticsearch:7.6.0
docker pull kibana:7.6.0

2、启动容器

# 启动elasticsearch
# --name 指定容器的名称
# -e 指定环境变量,容器中可以使用该环境变量。
# ES_JAVA_OPTS 代表设定ES的最大与最小的heap
# discovery.type 设置发现类型

docker cp container:/usr/share/elasticsearch/config /docker/elasticsearch/config

docker run -d --name es \
-p 9200:9200 -p 9300:9300 \
-v /docker/elasticsearch/config:/usr/share/elasticsearch/config \
-v /docker/elasticsearch/logs:/usr/share/elasticsearch/logs \
-v /docker/elasticsearch/data:/usr/share/elasticsearch/data \
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
-e "discovery.type=single-node" \
elasticsearch:7.6.0

# 启动kibana
docker run -d --name=kibana \
-v /docker/kinana/config/kibana.yml:/usr/share/kibana/config/kibana.yml \
-p 5601:5601 \
kibana:7.6.0

3、使用kibana连接ES

# 进入kibana容器 修改config 
# 也可以将配置文件映射到本地修改
docker exec -it CONTAINER-ID bash

vi /config/kibana.yml
# 修改elasticsearch.hosts参数
# 将array中的ip修改为你的es的ip地址

# 汉化 添加i18n.locale 
i18n.locale: "zh-CN"

# 修改完成保存
# 重启容器
docker restart KIBANA-CONTAINER-ID

在这里插入图片描述

ES数据类型

​​ ​ 在创建文档时,ES会根据字段的值动态的推断出它的类型,即动态映射,但这样可能出现推断不符合预期的问题,例如日期类型,所以我们需要根据实际情况选择是否主动指定字段的类型。

1、text

​​ ​ 当一个字段的内容需要被全文检索时,可以使用text类型,支持长内容的存储,比如检索文章内容、商品信息等。该类型的字段内容在保存时会被分词器分析,并且拆分成多个词项, 然后根据拆分后的词项生成对应的索引,根据关键字检索时可能会将关键字分词,用分好的词从之前生成的索引中去匹配,进而找到对应的文档。对于text类型的字段无法通过指定文本精确的检索到。text类型的字段不能直接用于排序、聚合操作。这种类型的字符串也称做analyzed字符串。

2、keyword

​ ​ keyword类型适用于结构化的字段,比如手机号、商品id等,默认最大长度为256。keyword类型的字段内容不会被分词器分析、拆分,而是根据原始文本直接生成倒排索引,所以keyword类型的字段可以直接通过原始文本精确的检索到。keyword类型的字段可用于过滤、排序、聚合操作。这种字符串称做not-analyzed字符串。

3、日期

ES 中的date类型默认支持如下两种格式

  • strict_date_optional_time,表示 yyyy-MM-dd’T’HH:mm:ss.SSSSSSZ 或者 yyyy-MM-dd 格式的日期
  • epoch_millis,表示时间戳毫秒

如果我们要存储类似2020-12-01 20:10:15这种格式的日期就会有问题,我们可以在创建索引时指定字段为date类型以及可以匹配的日期格式:

PUT /info
{
  "mappings": {
    "properties": {
      "publishDate":{
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
      }
    }
  }
}

需要注意的是,如果不主动指定字段类型为date,ES 默认使用text类型去保存日期的值

4、布尔

boolean类型true、false两个值

5、数值

类型取值范围
byte-2^7 ~ 2^7-1
short-2^15 ~ 2^15-1
integer-2^31 ~ 2^31-1
long-2^63 ~ 2^63-1
float32位单精度IEEE 754浮点类型
double64位双精度IEEE 754浮点类型
half_float16位半精度IEEE 754浮点类型
scaled_float缩放类型的的浮点数

一般情况下,如果可以满足需求,则优先使用范围小的类型,来提高效率。

6、数组

​​ ​ 在 ES 中并没有数组类型,但我们却可以按数组格式来存储数据,因为 ES 中默认每个字段可以包含多个值,同时要求多个值得类型必须一致。例如可以按照如下方式指定一个字段的值为数组:

"label": [
    "ElastcSearch",
    "7.6.0版本"
 ]

7、对象

​​ ​ 由于 ES 中以 JSON 格式存储数据,所以一个JSON对象中的某个字段值可以是另一个JSON对象。

8、范围

类型技能
integer_range-2^31 ~ 2^31-1
long_range-2^63 ~ 2^63-1
float_range32位单精度IEEE 754浮点类型
double_range64位双精度IEEE 754浮点类型
date_range自系统历元以来无符号64位整数范围内的毫秒数
ip_rangeIPv4、IPv6 的一系列IP地址值

例如可以创建索引时定义一个日期范围的字段类型

PUT /test
{
  "mappings": {
    "properties": {
      "reader_num":{
        "type": "integer_range"
      }
    }
  }
}

添加文档时指定字段的值

"reader_age_range": {
    "gte": 10,
    "lte": 50
}

Restful操作ES

1、节点相关

参数v会返回带有标题的更全面的信息
在查询字符串中增加pretty参数,会让Elasticsearch美化输出JSON响应以便更加容易阅读

1.1 查询所有节点
GET /_cat/nodes?v
1.2 查看节点健康
GET _cat/health?v
1.3 查看所有索引信息
GET /_cat/indices?v
1.4 查看所有模板
GET _cat/templates
1.5 查看索引分片信息
# 查看全部
GET /_cat/shards?v

# 查看指定索引
GET /_cat/shards/<index>?v
1.6 查看插件信息
GET /_cat/plugins?v

2、索引相关

2.1 查看所有索引
GET /_cat/indices?v

# 按索引占用内存大小
GET /_cat/indices?v&h=i,tm&s=tm:desc

# 按文档数量排序索引
GET /_cat/indices?v&s=docs.count:desc

# 根据健康状态查询索引  green/yellow/red
GET /_cat/indices?v&health=green
2.2 查看索引文档数
# 查看所有索引文档总数
GET /_all/_count

# 查看指定索引文档总数
GET /<index>/_count
2.3 查看索引分片信息
GET /_cat/shards/<index>?v
2.4 查看指定索引
# mappings和settings信息
GET /<index>

# 查看mapping
GET test/_mapping?pretty
2.5 删除索引
DELETE /<index>
2.6 创建索引
# 指定索引分片和副本数量
PUT /<index>
{
  "settings": {
    "number_of_shards": 2,   # 分片
    "number_of_replicas": 2    # 副本
  }
}

# 指定分片数量与mapping映射
{
  "settings": { 
    "number_of_shards": 1,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "field1":{
        "type": "type2"
      },
      "field2":{
        "type": "type2"
      }
    }
  }
}
2.7 修改分片副本
PUT <index>/_settings
{
  "index": {
        "number_of_replicas": 2
      }
}
2.8 新增mapping
POST /<index>/_mapping
{
  "properties": {
      "field":{
        "type": "type"
      }
    }
}

3、操作文档

3.1 创建文档

​ POST和PUT请求都可以新增文档,区别在于PUT创建文档必须指定Id,而POST可以指定也可以不指定不指定ID则会随机生成一个Id。关于mapping:

  • 若没有提前设定索引字段类型而直接添加文档,ES会自动判断数据类型并映射,新字段永久补充到mapping
  • 若添加的文档字段数量大于提前设定索引中字段数量,多出的字段ES会自动映射
  • 若添加的数据字段数量小于提前设定索引中字段数量,可成功入库
3.1.1 POST创建文档
# 指定文档Id
POST /<index>/_doc/<Id>
{
		"key":"value"
}

# 使用随机Id
POST /<index>/_doc
{
		"key":"value"
}
3.1.2 PUT创建文档
PUT /<index>/_doc/<Id>
{
  "key":"value"
}
3.2 查询文档
3.2.1 查询所有文档

  在查询文档的时候,有时候会发现每次查询的数据有可能不一致,解决这个问题,我们可以在请求参数中加入perference参数,值可以是_primary或者_replica,用来保证每次查询的数据一致性

# 可传参数q
# 根据字段值查询 ?q=<条件>:<值>
# 查询范围 field[MIN TO MAX]
# 分页 from size
# 排序 sort  sort=<field>: desc
# 仅输出某些字段  _source=field,field
GET /<index>/_search
3.2.2 根据Id查询指定文档
GET  /<index>/_doc/<id>
3.3 更新文档
3.3.1 更新全部字段
# PUT和POST请求都可以执行,全部字段均会被修改更新,可以新增字段,当ID未匹配到则执行新增
# 若Id存在,会将文档修改为以下内容
POST /<index>/_doc/<Id>
{
  "key":"value"
}
3.3.2 更新部分字段
# 仅支持POST请求,只修改部分字段数据, 字段不存在则在则创建
# 当ID未匹配上时,报错
POST /<index>/_update/<Id>/
{
  "doc": {
    "key":"value"
  }
}
3.3.3 并发更新

ES使用版本version来管理文档,在更新的时候可以加上版本来控制并发

参数说明:

  • _seq_no,严格递增的顺序号,每个文档一个,Shard级别严格递增,保证后写入的Doc的==_seq_no==大于先写入的Doc的_seq_no
  • primary_term和==_seq_no==`都是一个整数,每当Primary Shard发生重新分配时,比如重启,Primary选举等,_primary_term会递增1
# 根据查询结果返回的seq_no和primary_term参数更新
POST /<index>/_doc/<Id>?if_seq_no=11&if_primary_term=1
{
  "doc":{
   "field": "value"
  }
}
3.4 删除文档
DELETE /<index>/_doc/<Id>
3.5 批量操作
3.5.1.批量查找
# 多Id查询

# 单索引查询
POST /<index>/_mget
{
  "ids": [
    "<Id1>",
    "<Id2>",
    "<Id3>"
  ]
}

# 多索引查询
{
  "docs": [
    {
      "_index": "<index-1>",
      "_id": "<Id>"
    },
    {
      "_index": "<index-2>",
      "_id": "<Id>"
    }
  ]
}
3.5.2 批量新增
# 若索引存在则更新、反之就新增
POST _bulk
{ "create" : { "_index" : "<index>", "_id": "<Id>" } }
{"key": "value"}
{ "create" : { "_index" : "<index>", "_id": "<Id>" } }
{"key": "value"}
3.5.3 批量更新
# 命令下一行需要紧跟着data数据
POST _bulk
{"update": {"_index": "<index>","_id": "<Id>"}}
{"doc": {"key": "value"}}
{"update": {"_index": "<index>","_id": "<Id>"}}
{"doc": {"key": "value"}}
3.5.4 批量删除
POST _bulk
{ "delete" : {"_index": "<index>","_id": "<Id>"}}
{ "delete" : {"_index": "<index>","_id": "<Id>"}}
3.5.5 批量增删改
# 使用_bulk命令可以进行文档的批量增删改

POST _bulk
{ "create" : {"_index" : "<index>", "_id": "<Id>"}}
{"key": "value"}
{ "update" : {"_index" : "<index>", "_id": "<Id>"}}
{ "doc" : {"key": "value"}}
{ "delete" : {"_index" : "my_index3", "_id": "<Id>"}}
3.6 滚动查询

  在大数据量检索的时候,当数据条数大于1w的时候,es默认检索1w条,且total中也显示上限时1w,所以在我们只需要根据条件获取数量的时候,可以在query同级下加track_total_hits参数,解除上线。当我们要获取所有数据的时候,可以使用es原生API中的滚动查询导出所有数据。

滚动查询使用示例:

scroll参数: 保持游标查询窗口。 size:需要获取的数据的条数, 当数据为空时即导出完毕。

POST /test/_search?scroll=1m
{
  "query": { 
    "match_all": {}
  },
  "size":2
}

POST /_search/scroll
{
    "scroll": "1m",
    "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAYDVQWVXFxZjZlTmJRUUdTZElXa0xmV05JQQ=="
}

Elasticsearch Query DSL

​​ ​ 当我们开始在 Elasticsearch 插入数据,你就可以通过_search 方式发送请求来进行搜索,如果使用匹配搜索功能,在请求体中使用 Elasticsearch Query DSL 指定搜索条件。你也可以在请求头指定要搜索的索引名称。

1、DSL条件

2.1 条件语句
关键字查询范围备注
match_all查询所有
match匹配查询
bool联合查询
term词条精准查询
range范围查询
2.2 约束条件
参数意义备注
must必须同时满足AND
must_not必须同时不满足NOT
should满足其中一个条件OR
filter过滤条件必须匹配
2.3 响应体参数
参数意义备注
took整个搜索花费的毫秒数
timed_out查询是否超时
_shards参与查询的分片数 成功/失败
hits查询匹配的根节点
hits[]hits数组包含了匹配到的前10条数据
hits.total.value表示匹配到的文档总数
hits._index索引
hits_type文档类型
hits._id文档标识符
hits._source文档存储的实际数据
hits._score相关性得分

2、全文搜索

2.1 简单查询
2.1.1 查询所有结果
GET /<index>/_search
{
  "query": { 
    "match_all": {}
  } 
}
2.1.2 根据条件查询
GET /<index>/_search
{
  "query": {
    "match": {
      "name": "ichpan"
    }
  }
}
2.1.3 指定查询字段
GET /<index>/_search
{
  "query": {
    "match_all": {}
  },
  "_source": [
    "field1",
    "field2"
  ]
}
2.1.4 多词查询

多个条件之间使用空格分割

GET /test/_search
{
  "query": {
    "match": {
      "name": "ichpan yjiahao"
    }
  }
}
2.1.5 排序

排序后score返回的值为null,order支持 desc/asc

GET /test/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ]
}
2.1.6 数据分词结果
GET _analyze
{
  "analyzer": "standard",
  "text": "楼下安同学"
}
2.2 批量查询
2.2.1 多ID查询
GET /<index>/_search
{
  "query": {
    "ids": {
      "values":[Id1, Id2, Id3]
    }
  }
}
2.2.2 单索引批量查询
GET /<index>/_mget
{
  "ids": [
  	Id1,
    Id2,
    Id3
  ]
}
2.2.3 跨索引批量查
# 同时查询<index1>与<index2>两个索引下的数据
GET /_mget
{
  "docs": [
    {
      "_index": "index1",
      "_id": "1"
    },
    {
      "_index": "index2",
      "_id": "2"
    }
  ]
}

GET /_msearch
{"index":"index1"}
{"query":{"match_all":{}}}
{"index":"index2"}
{"query":{"match_all":{}}}
2.3 匹配查询

​​ ​ 像matchquery_string 这样的查询是高层查询,它们了解字段映射的信息:如果查询日期或整数字段,它们会将查询字符串分别作为日期或整数对待。如果查询一个未分析的精确值字符串字段,它们会将整个查询字符串作为单个词项对待。但如果要查询一个已分析的全文字段,它们会先将查询字符串传递到一个合适的分析器,然后生成一个供查询的词项列表。一旦组成了词项列表,这个查询会对每个词项逐一执行底层的查询,再将结果合并,然后为每个文档生成一个最终的相关度评分。

​​ ​ 匹配查询match是个核心查询。无论需要查询什么字段, match 查询都应该会是首选的查询方式。它是一个高级全文查询 ,这表示它既能处理全文字段,又能处理精确字段

2.3.1 关键字分词查询
# 单个词
GET /<index>/_search
{
  "query": {
    "match": {
      "profession": "楼下安同学"
    }
  }
}

# 多个词用逗号隔开
GET /student_info/_search
{
  "query": {
    "match": {
      "profession": "你好, 楼下安同学"
    }
  }
}
2.3.2 提高查询精度

​​ ​ match查询还可以接受operator 操作符作为输入参数,默认情况下该操作符是or。我们可以将它修改成 and 让所有指定词项都必须匹配

# 查询索引test中name为ichpan和yjiahao的文档
GET /test/_search
{
  "query": {
    "match": {
      "name": {
        "query": "ichpan yjiahao",
        "operator": "and"
      }
    }
  }
}
2.3.3 控制精度

​​ ​ 我们既想查询包含那些可能相关的文档,同时又排除那些不太相关的文档。

​​ ​ match查询支持minimum_should_match最小匹配参数,这让我们可以指定必须匹配的词项数用来表示一个文档是否相关。我们可以将其设置为某个具体数字,也可设置为一个百分数。

GET /test/_search
{
  "query": {
    "match": {
      "name": {
        "query": "ichpan yjiahao",
        "minimum_should_match": "30%"
      }
    }
  }
}
2.3.4 多字段查询

指定字段中出现的值

# 匹配字段name和desc字段中包含拆出来的词语的结果
GET /<index>/_search
{
	"query": {
	  "multi_match": {
	    "query": "楼下安同学",
	    "fields": ["name", "desc" ]
		}
	}
}
2.3.5 短语查询

​ ​ match_phrase短语搜索,要求所有的分词必须同时出现在文档中,同时位置必须紧邻一致

GET /<index>/_search
{
	"query": {
	  "match_phrase": {
	    "name": "楼下安同学"
		}
	}
}
2.3.6 高亮查询

返回带有html标签的检索结果

GET /test/_search
{
  "query": {
    "match": {
      "name": "ichpan"
    }
  },
  "highlight": {
    "fields": {
      "name": {}
    }
  }
}
2.3.7 前缀匹配
GET /<index>/_search
{
  "query": {
    "match_phrase_prefix": {
      "name": "ich"
    }
  }
}
2.4 布尔值查询

布尔值查询bool,筛选必须符合条件的数据 布尔条件可选条件见 2.1条件语句

# 布尔条件可选条件见 2.1条件语句
GET /test/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "ichpan"
          }
        }
      ]
    }
  },
  "from": 0,
  "size": 10
}
2.5 过滤查询

​​ ​ 过滤只会筛选出符合的文档,并不计算得分,且它可以缓存文档。所以,从性能考虑,过滤比查询更快。过滤适合在大范围筛选数据,而查询则适合精确匹配数据。一般应用时,应先使用过滤操作过滤数据,然后使用查询匹配数据。

GET /test/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match_all": {}
        }
      ],
      "filter": {
        "range": {
          "age": {
            "gte": 10,
            "lte": 30
          }
        }
      }
    }
  }
}
2.6 模糊查询
GET /<index>/_search
{
  "query": {
    "fuzzy": {
      "name": "安"
    }
  }
}
2.7 精确查询

​​ ​ termfuzzy 这样的底层查询不需要分析阶段,它们对单个词项进行操作。用term查询词项Foo只要在倒排索引中查找准确词项 ,并且用 TF/IDF 算法为每个包含该词项的文档计算相关度评分 _score

2.7.1 查找单个
# 关键词查询
GET /<index>/_search
{
	"query": {
		"term": {
			"name.keyword": "foo"
		}
	}
}
2.7.2 查找多个
# 多条件查询
GET /<index>/_search
{
	"query": {
		"terms": {
			"age": [19,20,21,22]
		}
	}
}
2.8 范围查询

大于-gt,小于-lt,大于等于-gte,小于等于-lte

2.8.1 数字范围
GET /<inex>/_search
{
  "query": {
    "range": {
      "age":{
        "gte": 19,
        "lte": 21
      }
    }
  }
}
2.8.2 时间范围
GET /<index>/_search
{
  "query": {
    "range": {
      "birthday":{
        "gte": "2001-06-15",
        "lte": "2001-09-20"
      }
    }
  }
}
2.8.3 from … to
  • 包含边界
GET /<index>/_search
{
  "query": {
    "range": {
      "age":{
        "from": 19,
        "to": 21
      }
    }
  }
}
  • 不包含边界
GET /<index>/_search
{
  "query": {
    "range": {
      "age": {
        "from": 19,
        "to": 21,
        "include_lower": false,
        "include_upper": false
      }
    }
  }
}
2.9 通配符查询

? 匹配任意数量字符,*用来匹配零个或者多个字符,主要用于英文检索

# *匹配
GET /student_info/_search
{
  "query": {
    "wildcard": {
      "name": "小*"
    }
  }
}

# ?匹配
GET /student_info/_search
{
  "query": {
    "wildcard": {
      "name": "ich?n"
    }
  }
}
2.10 约束条件查询

约束条件参考 DSL条件-约束条件

2.8 分页查询

2.11 分页查询
2.11.1 简单分页

对于非深度分页,简单查询时,一般使用from和size进行分页查询

  • from: 分页起始位置

  • size: 每页数据大小

2.11.2 分页查询
GET /<index>/_search
{
  "query": {
    "match_all": {}
  },
  "from": 1,
  "size": 2
}
2.11.3 深度分页

​ ES对于from和size的个数是有限制的,默认限制二者之和不能超过1w,超过后会报错max_result_window参数作为保护措施,虽然这个参数可以修改,也可以在配置文件配置。但是最好不要这么做,当所请求的数据总量大于1w时,应使用ES游标(scroll查询)来代替from+size。如果需要深度分页对服务器压力会变大。如果确认需要设置,则需要提前预估启动内存大小。

2.11.4 游标查询

​ scoll 游标查询,指定 scroll=时间 ,指定保存的分钟数,第一次发起请求放回的是数据+_scroll_id ,后面通过 _scroll_id 去请求数据,适合大批量查询。游标查询,其实是在 es 里面缓存了结果 ,然后一次一次的去取,所以发起第一次请求的时候只有size ,没有from,后面的请求只有 scroll_id 和 scroll 时间

# 年龄大于18,每页2条,保存1分钟
GET /<index>/_search?scroll=1m
{
  "query": {
    "range": {
      "age":{
        "gt": 18
      }
    }
  },
  "size": 2
}
2.11.5 search_after分页

​ from + size的分页方式虽然是最灵活的分页方式,但当分页深度达到一定程度将会产生深度分页的问题。scroll能够解决深度分页的问题,但是其无法实现实时查询,即当scroll_id生成后无法查询到之后数据的变更,因为其底层原理是生成数据的快照。ES-5.X之后 search_after应运而生,使用search_after必须添加排序"sort"条件

# 指定了根据ID升序,年龄倒序,2个排序条件,search_after中[起始ID,起始年龄]

GET /student_info/_search
{
  "query": {
    "match_all": {}
  },
  "search_after": [
    11000,20
  ],
  "size": 2,
  "sort": [
        {"_id": "asc"},
        {"age": "desc"}
    ]
}
2.11.6 三种分页方式比较
  • from size
    • 性能:低
    • 优点:灵活性好,实现简单
    • 缺点:深度分页问题
    • 使用场景:数据量比较小,能容忍深度分页问题
  • scroll
    • 性能:中
    • 优点:解决深度分页问题
    • 缺点:无法反应数据的实时性、维护成本高,需要维护一个 scroll_id跳页查询问题
    • 使用场景:海量数据的导出需要查询海量结果集的数据
  • search_after
    • 性能:性能最好不存在深度分页问题能够反映数据的实时变更
    • 优点:解决深度分页问题
    • 缺点:实现复杂,需要有一个全局唯一的字段连续分页的实现相对复杂,因为每一次查询都需要上次查询的结果跳页查询问题
    • 使用场景:海量数据的分页
2.12 聚合查询

​ 聚合查询是开发中常见的场景,一般包含。求和、最大值、最小值、平均值、总记录数等。text类型是不支持聚合的,size: 0 参数表示不用返回文档列表,只返回汇总的数据即可

2.12.1 ES聚合查询写法
"aggregations" : {
    "<aggregation_name>" : {                                 <!--聚合的名字 -->
        "<aggregation_type>" : {                             <!--聚合的类型 -->
            <aggregation_body>                               <!--聚合体:对哪些字段进行聚合 -->
        }
        [,"meta" : {  [<meta_data_body>] } ]?                <!--元 -->
        [,"aggregations" : { [<sub_aggregation>]+ } ]?       <!--在聚合里面在定义子聚合 -->
    }
}
2.12.2 求和


GET /<inedx>/_search
{
  "size": 0,
  "aggs": {
    "sum_age": {
      "sum": {
        "field":"age"
      }
    }
  }
}
2.12.3 最大值
GET /<index>/_search
{
  "size": 0,
  "aggs": {
    "max_age": {
      "max": {
        "field":"age"
      }
    }
  }
}
2.12.4 最小值
GET /<index>/_search
{
  "size": 0,
  "aggs": {
    "min_age": {
      "min": {
        "field":"age"
      }
    }
  }
}
2.12.5 平均值
GET /<index>/_search
{
  "size": 0,
  "aggs": {
    "avg_age": {
      "avg": {
        "field":"age"
      }
    }
  }
}
2.12.6 去重数值
# 类似mysql的 count distinct
GET /<index>/_search
{ 
  "size": 0,
  "aggs": {
    "age_count": {
      "cardinality": {
        "field": "age.keyword"
      }
    }
  }
}
2.12.7 多值查询
GET /<index>/_search
{
  "size": 0,
  "aggs": {
    "max_age": {
      "max": {
        "field": "age"
      }
    },
    "min_age": {
      "min": {
        "field": "age"
      }
    },
    "avg_age": {
      "avg": {
        "field": "age"
      }
    }
  }
}
2.12.8 返回多个聚合值

​ ​ stats 统计,请求后会直接显示多种聚合结果,总记录数,最大值,最小值,平均值,汇总值

GET /<index>/_search
{
  "size": 0,
  "aggs": {
    "age_stats": {
      "stats": {
        "field":"age"
      }
    }
  }
}
2.12.9 百分比

​ ​ 对指定字段的值按从小到大累计每个值对应的文档数的占比,返回指定占比比例对应的值

GET /<index>/_search
{
  "size": 0,
  "aggs": {
    "age_percentiles": {
      "percentiles": {
        "field": "age"
      }
    }
  }
}
2.12.10.文档值占比

​​ ​ 这里指定值,查占比。注意占比是小于文档值的比例

GET /<index>/_search
{
  "size": 0,
  "aggs": {
    "age_percentiles": {
      "percentile_ranks": {
        "field": "age",
        "values": [
          22,
          25,
          33
        ]
      }
    }
  }
}
2.12.11 中位数查询
{
  "size": 0,
  "aggs": {
    "load_time_outlier": {
      "percentiles": {
        "field": "salary",
        "percents": [
          50,
          99
        ],
        "keyed": false
      }
    }
  }
}
2.12.12 分组取Top
案例1:根据性别分组。展示工资排名top3
GET /<index>/_search?size=0
{
  "aggs": {
    "top_tags": {
      "terms": {
        "field": "sex"
      },
      "aggs": {
        "top_sales_hits": {
          "top_hits": {
            "sort": [
              {
                "salary": {
                  "order": "desc"
                }
              }
            ],
            "_source": {
              "includes": [
                "name",
                "sex",
                "salary"
              ]
            },
            "size": 3
          }
        }
      }
    }
  }
}
2.12.13 分组之聚合
# 根据性别分组求平均工资

GET /<index>/_search
{
  "size":0,
  "aggs": {
    "top_tags": {
      "terms": {
        "field": "sex"
      },
      "aggs": {
        "avg_salary": {
          "avg": {
            "field": "salary"
          }
        }
      }
    }
  }
}
2.12.14 总记录数查询
# 统计年龄 >=25 的记录数
GET /<index>/_count
{
  "query": {
    "range": {
      "age":{
        "gte": 25
      }
    }
  }
}

# 统计年龄 >=25 的记录数
GET /<index>/_search?size=0
{
  "query": {
    "range": {
      "age":{
        "gte": 25
      }
    }
  }
}

总结

​​ ​ 此文档对ElasticSearch基础操作进行了全面总结,在各种语言中操作ElasticSearch主要写DSL语句,掌握底层原理有助于我们快速研发。文档持续更新补充中…

在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐