ElasticSearch - 开启搜索的新境界
ElasticSearch技术沉淀,入门手册/爱心
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是一个针对Elasticsearch的开源分析及可视化平台,用来搜索、查看交互存储在Elasticsearch索引中的数据。使用Kibana,可以通过各种图表进行高级数据分析及展示。Kibana让海量数据更容易理解。它操作简单,基于浏览器的用户界面。可以快速创建仪表板、实时显示Elasticsearch查询动态。
Es核心概念
1、关系型数据库和ES对比
Relational DB | ElasticSearch |
---|---|
database | index |
tables | types |
rows | documents |
columns | fields |
schema | mapping |
Innodb | Lucene |
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 List。Term Dictionary记录了所用文档的单词以及单词和倒排列表的关系。Posting List则是记录了term在文档中的位置以及其他信息,主要包括文档ID,词频(term在文档中出现的次数,用来计算相关性评分),位置以及偏移。
Restful风格
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
method | url地址 | 描述 |
---|---|---|
PUT | localhost:9200/索引名称/类型名称/文档id | 创建文档(指定文档id) |
POST | localhost:9200/索引名称/类型名称 | 创建文档(随机文档id) |
POST | localhost:9200/索引名称/类型名称/文档id/_update | 修改文档 |
DELETE | localhost:9200/索引名称/类型名称/文档id | 删除文档 |
GET | localhost:9200/索引名称/类型名称/文档id | 查询文档通过文档id |
POST | localhost: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 |
float | 32位单精度IEEE 754浮点类型 |
double | 64位双精度IEEE 754浮点类型 |
half_float | 16位半精度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_range | 32位单精度IEEE 754浮点类型 |
double_range | 64位双精度IEEE 754浮点类型 |
date_range | 自系统历元以来无符号64位整数范围内的毫秒数 |
ip_range | IPv4、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 匹配查询
像match或 query_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 精确查询
term 或fuzzy 这样的底层查询不需要分析阶段,它们对单个词项进行操作。用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语句,掌握底层原理有助于我们快速研发。文档持续更新补充中…
更多推荐
所有评论(0)