Featured image of post ElasticSearch学习

ElasticSearch学习

本文阅读量

ElasticSearch学习

一、概述

ElaticSearch,简称为es,es是一个开源的高扩展的份布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别(大数据时代)的数据。es也使用ava开发井使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 RESTfuL AP来隐 Lucene的复杂性,从而让全文搜索变得简单

谁在使用

  • 维基百科
  • github
  • 电商网站,检索商品
  • 日志数据分析, logstash采集日志,ES进行复杂的数据分析,ELK技术, elasticsearch+ logstash+ kibana
  • 商品价格监控网站,用户设定某商品的价格值,当低于该阈值的时候,发送通知消息给用户,比如说订阅牙膏的监控,如果高露洁牙膏的家庭套装低于50块钱,就通知我,我就去买

elasticsearch和solr比较

  • 当单纯的对已有数据进行搜索时,Solr更快
  • solr搭建需要zookeeper来帮忙管理,es本身就支持集群搭建,不需要三方介入
  • 当实时建立索引时,solr会产生io阻塞,查询性能较差,Elasticsearch具有明显的优势
  • 随着数据量的增加,Solr的搜索效率会变得更低,而Elasticsearch却没有明显的变化
  • 转变我们的搜索基础设施后从 Solr elasticsearch我们看见一个即时50×提高搜索性能!

对比总结

  1. es基本是开箱即用(解压就可以用!),非常简单。Solr安装略微复杂一丢丢!
  2. Solr利用 Zookeeper进行分布式管理,而 Elasticsearch自身带有分布式协调管理功能。
  3. Solr支持更多格式的数据,比如json、XML、CSV,而 Elasticsearch仅支持json文件格式。
  4. solr官方提供的功能更多,而 Elasticsearch本身更注重于核心功能,高级功能多有第三方插件提供,例如图形化界面需要kiana友好支撑
  5. Solr査询快,但更新索引时慢(即插入删除慢),用于电商等查询多的应用;
    • ES建立索引快(即查询慢),即实时性查询快,用于facebook新浪等搜索。
    • Solr是传统搜索应用的有力解决方案,但 Elasticsearch更适用于新兴的实时搜索应用
  6. solr比较成熟,有一个更大,更成熟的用户、开发和贡献者社区,而 Elasticsearch相对开发维护者较少,更新太快,学习使用成本高

二、安装

docker安装

Kibana是一个针对 Elasticsearch的开源分析及可视化平台,用来搜索、查看交互存储在 Elasticsearch索引中的数据。使用 Kibana可以通过各种图表进行高级数据分析及展示。 Kibana让海量数据更容易理解。它操作简单,基于浏览器的用户界面可以快速创建仪表板( dashboard)实时显示 Elasticsearch查询动态。设置 Kibana非常简单。无需编码或者额外的基础架构,几分钟内就可以完成Kibana安装并启动 Elasticsearch索引监测

docker-compose.yml文件如下:

镜像获取地址:https://hub.daocloud.io/

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
version: "3.0"
services:
    elasticsearch:
        image: daocloud.io/library/elasticsearch:7.6.2
        restart: always
        container_name: elasticsearch
        ports:
            - 9200:9200
    kibana:
        image: daocloud.io/library/kibana:7.6.2
        restart: always
        container_name: kibana
        ports:
            - 5601:5601
        environment:
            - elasticsearch_url=http://192.168.1.6:9200
        depends_on:
            - elasticsearch    	

注意:

  1. -后面要有空格
  2. 使用空格代替tab键
1
2
# 安装,docker-compose目录下执行
docker-compose up -d

安装可视化界面es head的插件

1
2
3
4
#1、拉取镜像
docker pull mobz/elasticsearch-head:5
#2、创建,启动镜像
docker run --restart=always --name es-head -di -p 9100:9100 docker.io/mobz/elasticsearch-head:5

安装ec-ik分词器

1
2
3
4
5
6
7
8
# 进入ec容器
docker exec -it elasticsearch /bin/bash
# 进入bin目录
cd bin
# 安装插件
./elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.4/elasticsearch-analysis-ik-6.5.4.zip
-> Downloading https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.4/elasticsearch-analysis-ik-6.5.4.zip
# 重启ec

三、ES核心概念

elasticsearch是面向文档,关系型数据库和elasticsearch客观的对比!一切都是json!

Relational DB Elasticsearch
数据库(database) 索引(index)
表(tables) types(慢慢会被弃用,5.x一个index中可以创建多个,6.x中一个index中包含1个,7中废弃)
行(rows) documents
字段(columns) fields(倒排索引)

elasticsearch(集群)中可以包含多个索引(数据库),每个索引中可以包含多个类型(表),每个类型下又包含多个文档(行),每个文档中又包含多个字段(列)。

物理设计:

elasticsearch在后台把每个索引划分成多个分片。每个分片可以在集群中的不同服务器间迁移

一个人就是一个集群!默认的集群名称就是elaticsearch

逻辑设计:

一个索引类型中,包含多个文档,当我们索引一篇文档时,可以通过这样的一个顺序找到它:索引-》类型-》文档id,通过这个组合我们就能索引到某个具体的文档。注意:ID不必是整数,实际上它是一个字符串。

最初1个集群里放了1亿条数据,这时候使用分片,在ec服务1中分片1中含有5k万,从分片2中包含5kw条数据(服务2中分片1中5kw备份),ec服务2中分片中含有5kw,从分片2中包含5kw条数据(服务1中的分片1中备份)

文档

就是我们的一条条的记录

之前说elasticsearch是面向文档的,那么就意味着索弓和搜索数据的最小单位是文档, elasticsearch中,文档有几个重要属性:

  • 自我包含, - -篇文档同时包含字段和对应的值,也就是同时包含key:value !
  • 可以是层次型的,-一个文档中包含自文档,复杂的逻辑实体就是这么来的! {就是一 个json对象! fastjson进行自动转换!}
  • 灵活的结构,文档不依赖预先定义的模式,我们知道关系型数据库中,要提前定义字段才能使用,在elasticsearch中,对于字段是非常灵活的,有时候,我们可以忽略该字段,或者动态的添加一个新的字段。

尽管我们可以随意的新增或者忽略某个字段,但是,每个字段的类型非常重要,比如一一个年龄字段类型,可以是字符串也可以是整形。因为elasticsearch会保存字段和类型之间的映射及其他的设置。这种映射具体到每个映射的每种类型,这也是为什么在elasticsearch中,类型有时候也称为映射类型。

类型

类型是文档的逻辑容器,就像关系型数据库一样,表格是行的容器。类型中对于字段的定 义称为映射,比如name映射为字符串类型。我们说文档是无模式的 ,它们不需要拥有映射中所定义的所有字段,比如新增一个字段,那么elasticsearch是怎么做的呢?elasticsearch会自动的将新字段加入映射,但是这个字段的不确定它是什么类型, elasticsearch就开始猜,如果这个值是18 ,那么elasticsearch会认为它是整形。但是elasticsearch也可能猜不对 ,所以最安全的方式就是提前定义好所需要的映射,这点跟关系型数据库殊途同归了,先定义好字段,然后再使用,别整什么幺蛾子。

索引

就是数据库!

索引是映射类型的容器, elasticsearch中的索引是一个非常大的文档集合。索|存储了映射类型的字段和其他设置。然后它们被存储到了各个分片上了。我们来研究下分片是如何工作的。

物理设计:节点和分片如何工作

一个集群至少有一 个节点,而一个节点就是一-个elasricsearch进程 ,节点可以有多个索引默认的,如果你创建索引,那么索引将会有个5个分片( primary shard ,又称主分片)构成的,每一个主分片会有-一个副本( replica shard ,又称复制分片)

https://snailsir.oss-cn-beijing.aliyuncs.com/typora/20200828224136138.png

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

倒排索引

elasticsearch使用的是一种称为倒排索引的结构,采用Lucene倒排索作为底层。这种结构适用于快速的全文搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。 例如,现在有两个文档,每个文档包含如下内容:

1
2
Study every day, good good up to forever  # 文 档1包含的内容
To forever, study every day,good good up  # 文档2包含的内容

为了创建倒排索引,我们首先要将每个文档拆分成独立的词(或称为词条或者tokens) ,然后创建一一个包含所有不重 复的词条的排序列表,然后列出每个词条出现在哪个文档:

term doc_1 doc_2
Study x
To x x
every
forever
day
study x
good
every
to x
up

现在,我们试图搜索 to forever,只需要查看包含每个词条的文档

term doc_1 doc_2
to x
forever
total 2 1

两个文档都匹配,但是第一个文档比第二个匹配程度更高。如果没有别的条件,现在,这两个包含关键字的文档都将返回。 再来看一个示例,比如我们通过博客标签来搜索博客文章。那么倒排索引列表就是这样的一个结构:

博客文章(原始数据) 博客文章(原始数据) 索引列表(倒排索引) 索引列表(倒排索引)
博客文章ID 标签 标签 博客文章ID
1 python python 1,2,3
2 python linux 3,4
3 linux,python
4 linux

如果要搜索含有python标签的文章,那相对于查找所有原始数据而言,查找倒排索引后的数据将会快的多。只需要查看标签这一栏,然后获取相关的文章ID即可。完全过滤掉无关的所有数据,提高效率!

elasticsearch的索引和Lucene的索引对比

在elasticsearch中,索引(库)这个词被频繁使用,这就是术语的使用。在elasticsearch中 ,索引被分为多个分片,每份分片是-个Lucene的索引。所以一个elasticsearch索引是由多 个Lucene索引组成的。别问为什么,谁让elasticsearch使用Lucene作为底层呢!如无特指,说起索引都是指elasticsearch的索引。

接下来的一切操作都在kibana中Dev Tools下的Console里完成。基础操作!

1
2
3
4
1、将存放的数据,以一定的方式进行分词,并且将分词的内容存放到一个单独的分词库中
2、当用户去查询的时候,会将用户的查询关键字进行分词
3、然后去分词库中去匹配内容,最终得到数据的id标识
4、根据id标识去存放数据的位置拉取指定的数据

ik分词器

是什么

分词:即把一-段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如“我爱狂神”会被分为"我",“爱”,“狂”,“神” ,这显然是不符合要求的,所以我们需要安装中文分词器ik来解决这个问题。

如果要使用中文,建议使用ik分词器!

IK提供了两个分词算法: ik_ smart和ik_ max_ word ,其中ik_ smart为最少切分, ik_ max_ _word为最细粒度划分!一会我们测试!

什么是IK分词器:

  • 把一句话分词
  • 如果使用中文:推荐IK分词器
  • 两个分词算法:ik_smart(最少切分),ik_max_word(最细粒度划分)

【ik_smart】测试:

ik_max_word为最细粒度划分!穷尽词库的可能!

【ik_max_word】测试:

ik分词器增加自己的配置

1、在ec/plugins/ik/config/创建snailsir.dic自己的字典文件

2、在ec/plugins/ik/config/IKAnalyzer.cfg.xml按照如下配置:

1
2
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict">snailsir.dic</entry>

以后的话,我们需要自己配置分词就在自己定义的dc文件中进行配置即可!

四、基本使用

Rest风格说明

一种软件架构风格,而不是标准。更易于实现缓存等机制

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 查询所有的数据

数据类型

  • 字符串类型

    text:一般用于全文索引,将当前field进行分词

    keyword:当前的field不会被分词

  • 数值类型

    long:

    integer:

    short:

    byte:

    double:

    float:

    half_floa:比float精度小一半

    scaled_float

  • 日期类型

    date:

  • 布尔类型

    boolean

  • 二进制类型

    binary:暂时支持base64 encode的字符串

  • 范围类型

    lang_range:赋值的时候无需指定具体内容,只需要存储一个范围即可, 指定gt,lt,elt,egt integer_range:

    double_range:

    float_range:

    date_range:

    ip_range:

  • geo用来存储经纬度

  • ip类型

    ip:可以存储ipv4或者ipv6

关于索引的基本操作

创建一个索引

创建

1
2
3
4
5
6
7
PUT /person
{
  "settings": {
    "number_of_replicas": 1,
    "number_of_shards": 5
  }
}  

查看索引信息

1
GET /person

删除索引

1
DELETE /person

创建索引规则

创建

指定字段的类型novel,就比如sql创表

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
PUT /book
{
  "settings": {
    # 备份数
    "number_of_replicas": 1,
    # 分片数
    "number_of_shards": 5
  },
  # 指定数据结构
  "mappings": {
  	# 类型 type
    "novel":{
  	  # 文档存储的field
      "properties":{
  			#field属性名
        "name":{
  				# 类型
          "type":"text",
  				# 指定分词器
          "analyzer":"ik_max_word",
  				# 是否为该字段建立索引
          "index":true,
  				# 是否在文档中存储该字段
  				"store": false
        },
        "author":{
          "type":"keyword"
        },
        "count":{
          "type":"long"
        },
        "sale":{
          "type":"date",
          # 时间类型的格式化方式
          "format":"yyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
        }
      }
    }
  }
}

查看

获得这个规则!可以通过GET请求获得具体的信息

1
GET book

关于文档的基本操作

新建文档

自动生成id

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
POST /book/novel
{
  "name":"回到明朝当王爷",
  "author":"月关",
  "count": 1000000,
  "sale": "2000-01-01"
}
# 返回结果
{
  "_index" : "book",
  "_type" : "novel",
  "_id" : "Ang7GnoBzF3XFwZP8bC2",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

手动指定_id

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
PUT /book/novel/1
{
  "name":"锦衣夜行",
  "author":"月关",
  "count": 1000000,
  "sale": "2008-01-01"
}
# 返回结果
{
  "_index" : "book",
  "_type" : "novel",
  "_id" : "1",
  "_version" : 1,
  "result" : "created", # 创建
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}

修改文档

覆盖修改

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
PUT /book/novel/1
{
  "name":"锦衣夜行",
  "author":"月关",
  "count": 1000000,
  "sale": "2008-09-30"
}

# 返回结果
{
  "_index" : "book",
  "_type" : "novel",
  "_id" : "1",
  "_version" : 2, #version已经发生改变了,说明修改了
  "result" : "updated",#更新
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}

doc修改方式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
POST /book/novel/1/_update
{
  "doc": {
    "count": 1500000
  }
}
# 返回结果
{
  "_index" : "book",
  "_type" : "novel",
  "_id" : "1",
  "_version" : 3,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 3,
  "_primary_term" : 1
}

删除文档

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
DELETE /book/novel/Ang7GnoBzF3XFwZP8bC2
# 返回结果
{
  "_index" : "book",
  "_type" : "novel",
  "_id" : "Ang7GnoBzF3XFwZP8bC2",
  "_version" : 2,
  "result" : "deleted",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 4,
  "_primary_term" : 1
}

五、查询

构建测试数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
PUT /sms-logs-index
{
  "settings": {
    "number_of_replicas": 1,
    "number_of_shards": 3
  },
  "mappings": {
    "sms-logs-type":{
      "properties":{
        "name":{
          "type":"keyword"
        },
        "createDate":{
          "type":"date"
        },
        "free":{
          "type":"long"
        },
        "ipAddr":{
          "type":"ip"
        },
        "longCode":{
          "type":"keyword"
        },
        "mobile":{
          "type":"keyword"
        },
        "operatorId":{
          "type":"integer"
        },
        "province":{
          "type":"keyword"
        },
        "replyTotal":{
          "type":"integer"
        },
        "sendDate":{
          "type":"date"
        },
        "smsContent":{
          "type":"text",
          "analyzer":"ik_max_word"
        },
        "state":{
          "type":"integer"
        }
      }
    }
  }
}



POST /sms-logs-index/sms-logs-type/1
{
  "name":"途虎养车",
  "createDate": 1587137793030,
  "sendDate": "2000-01-01",
  "longCode": "1500632323",
  "mobile": "17600696127",
  "state": 0,
  "operatorId": 1,
  "province": "北京",
  "ipAddr": "10.23.35.104",
  "smsContent":"ajsda阿拉基倒垃圾手动阀爱神的箭疯狂拉时间段爱劳动纠纷",
  "free": 3,
  "replyTotal": 10
}

简单查询

1
GET  /sms-logs-index/sms-logs-type/_search?q=name:北京

query_string查询

1
2
3
4
5
6
7
8
9
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "query_string":{
      "default_field": "name",
      "query": "北京 or 上海"
    }
  }
}

simple_query_string

1
2
3
4
5
6
7
8
9
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "simple_query_string":{
      "query": "北京",
      "fields": ["name","province"]
    }
  }
}

term&terms查询

term查询

完全匹配,搜索之前不会对你搜索的关键字进行分词,对你的关键字去文档分词库中去匹配内容。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
POST /sms-logs-index/sms-logs-type/_search
{
  "from": 0,
  "size": 5,
  "query": {
    "term":{
      "province":{
        "value":"北京"
      }
    }
  }
}

terms查询

和term查询机制一样,都不会将指定的查询关键字进行分词,直接去分词库中匹配,找到相应的文档内容。

terms是在针对一个字段包含多个值得时候使用,类似于sql中的in查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
POST /sms-logs-index/sms-logs-type/_search
{
  "from": 0,
  "size": 5,
  "query": {
    "terms": {
      "province": [
        "北京",
        "上海"
      ]
    }
  }
}

注意

并不是只对keyword类型有效,text也有效,但是text类型是使用了分词器进行了分词,可能会匹配不到数据(分词器,将词拆分后的词没有要查询的词)

match查询

会根据你查询的字段类型,采用不同的查询方式

  • 日期或数值,会将基于字符串查询的内容转换成日期或数值对待
  • keyword(不能被分词器分词),不会对你指定的查询关键词进行分词
  • text(可以被分词),match会将你指定的查询内容根据一定的方式去分词,然后去分词库中匹配指定的内容

match,实际底层就是多个term查询,将多个term查询的结果给你封装到一起

match_all查询

查询全部内容(只返回name与province字段),不指定任何查询条件

1
2
3
4
5
6
7
8
9
POST /sms-logs-index/sms-logs-type/_search
{
  "from": 0,
  "size": 5,
  "query": {
    "match_all": {}
  },
  "_source": ["name","province"] # 或者 "_source": {"includes": ["name","province"]}
}

指定一个field作为筛选条件,返回的字段不包含name与province

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
POST /sms-logs-index/sms-logs-type/_search
{
  "from": 0,
  "size": 5,
  "query": {
    "match": {
      "smsContent": "疯狂"
    }
  },
  "_source": {"excludes": ["name","province"]}
}

布尔match查询

既包含中国又包含健康的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
POST /sms-logs-index/sms-logs-type/_search
{
  "from": 0,
  "size": 5,
  "query": {
    "match": {
      "smsContent": {
        "query": "中国 健康",
        "operator": "and"
      }
    }
  }
}

包含中国或健康的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
POST /sms-logs-index/sms-logs-type/_search
{
  "from": 0,
  "size": 5,
  "query": {
    "match": {
      "smsContent": {
        "query": "中国 健康",
        "operator": "or"
      }
    }
  }
}

multi_match查询

针对多个field进行检索(多个field对应一个搜索文本)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
POST /sms-logs-index/sms-logs-type/_search
{
  "from": 0,
  "size": 5,
  "query": {
    "multi_match": {
      "query": "北京",
      "fields": ["province","smsContent"]
    }
  }
}

其他查询

id查询

1
GET /sms-logs-index/sms-logs-type/1

ids查询

1
2
3
4
5
6
7
8
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "ids": {
      "values": [1,2,3]
    }
  }
}

prefix查询

前缀查询,可以通过一个关键字去指定一个field的前缀,从而查询到指定的文档。

类似于百度搜索,我们输入一个词,百度给出关联

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "prefix": {
      "name": {
        "value": "途虎"
      }
    }
  }
}

fuzzy查询(模糊查询)

我们输入字符的大概,es就可以根据输入的内容大概去匹配结果

有错别字也会匹配结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "fuzzy": {
      "name": {
        "value": "途虎养成"
      }
    }
  }
}

POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "fuzzy": {
      "name": {
        "value": "途虎样车",
        "prefix_length": 3 # 前3个字符不能错误
      }
    }
  }
}

wildcard查询(通配查询)

和mysql中的like一样,可以在查询时,在字符串中指定通配符*和占位符?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "wildcard": {
      "name": {
        "value": "途虎*"
      }
    }
  }
}

POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "wildcard": {
      "name": {
        "value": "途虎??"
      }
    }
  }
}

range查询(范围查询)

只针对数值类型,对某一个field进行大于或小的范围查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "range": {
      "free": {
        "gte": 10,
        "lte": 20
      }
    }
  }
}

regexp查询(正则查询)

1
2
3
4
5
6
7
8
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "regexp": {
      "mobile": "175[0-9]{8}"
    }
  }
}

分页查询

from+size在es查询数据过程:

  1. 将用户指定的关键词进行分词
  2. 将词汇去分词库中进行检索,得到多个文档的id
  3. 去各个分片中去拉取指定的数据(耗时较长)
  4. 将数据根据score进行排序(耗时较长)
  5. 根据from的值,将查询到的数据舍弃一部分
  6. 返回结果

scroll查询过程:

  1. 将用户指定的关键词进行分词

  2. 将词汇去分词库中进行检索,得到多个文档的id

  3. 将文档的id存放在一个es的上下文中

  4. 根据你指定的size的个数去es中检索指定个数的数据,拿完数据的文档id,会从上下文移除

  5. 如果需要下一页的数据,直接去es的上下文中找后续内容

  6. 循环第4步与第5步

scroll查询方式,不适合做实时的查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# 返回第一页数据,并将文档id信息存放在es上下文中,并且指定生存时间1分钟
POST /sms-logs-index/sms-logs-type/_search?scroll=1m
{
"query": {
 "match_all": {}
},
"size": 2,
"sort": [
 {
   "free": {
     "order": "desc"
   }
 }
]
}
#返回数据
{
"_scroll_id" : "根据第一步得到的scorll_id去指定",
"took" : 2,
"timed_out" : false,
"_shards" : {
 "total" : 3,
 "successful" : 3,
 "skipped" : 0,
 "failed" : 0
},
"hits" : {
 "total" : 1,
 "max_score" : null,
 "hits" : [
   {
     "_index" : "sms-logs-index",
     "_type" : "sms-logs-type",
     "_id" : "1",
     "_score" : null,
     "_source" : {
       "name" : "途虎养车",
       "createDate" : 1587137793030,
       "sendDate" : "2000-01-01",
       "longCode" : "1500632323",
       "mobile" : "17600696127",
       "state" : 0,
       "operatorId" : 1,
       "province" : "北京",
       "ipAddr" : "10.23.35.104",
       "smsContent" : "ajsda阿拉基倒垃圾手动阀爱神的箭疯狂拉时间段爱劳动纠纷",
       "free" : 3,
       "replyTotal" : 10
     },
     "sort" : [
       3
     ]
   }
 ]
}
}


# 下一页数据
POST /_search/scroll
{
"scroll_id":"DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAGJBFm1DdTRTNEhFVFBXd2hqemJ3RkxKc0EAAAAAAABiQxZtQ3U0UzRIRVRQV3doanpid0ZMSnNBAAAAAAAAYkIWbUN1NFM0SEVUUFd3aGp6YndGTEpzQQ==",
"scroll": "1m"
}
# 返回结果
{
"_scroll_id" : "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAGMyFm1DdTRTNEhFVFBXd2hqemJ3RkxKc0EAAAAAAABjNBZtQ3U0UzRIRVRQV3doanpid0ZMSnNBAAAAAAAAYzMWbUN1NFM0SEVUUFd3aGp6YndGTEpzQQ==",
"took" : 2,
"timed_out" : false,
"_shards" : {
 "total" : 3,
 "successful" : 3,
 "skipped" : 0,
 "failed" : 0
},
"hits" : {
 "total" : 1,
 "max_score" : null,
 "hits" : [ ]
}
}

# 删除scroll在es上下文中的数据
DELETE /_search/scroll/根据第一步得到的scorll_id去指定
# 如果先执行删除,然后再执行上方的查询,会返回错误,因为已经把上下文给删了

复合查询

bool查询

复合过滤器:将你的多个查询条件,以一定的逻辑组合在一起

  • must:所有的条件,用must组合在一起,表示and的意思
  • must_not:将must_not中的条件,表示全部都不能匹配,表示not的意思
  • should:所有的条件,用should组合在一起,表示or的意思
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 查询省份为武汉或者北京
# 运营商不是联通
# name中包含中国和平安
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "province": {
              "value": "北京"
            }
          }
        },
        {
          "term": {
            "province": {
              "value": "武汉"
            }
          }
        }
      ],
      "must_not": [
        {
          "term": {
            "operatorId": {
              "value": "2"
            }
          }
        }
      ],
      "must": [
        {
          "match": {
            "name": "中国"
          }
        },{
          "match": {
            "name": "平安"
          }
        }
      ]
    }
  }
}

boostring查询

可以帮助我们去影响查询后的score

  • positive:只有匹配上positive的查询内容,才会被放到返回的结果中
  • negative:如果匹配上positive并且也匹配上了negative,就可以降低这样的文档score
  • negative_boost:指定系数,必须小于1.0

关于查询时,分数是如何计算的:

  • 搜索的关键字在文档中出现的频次越高,分数越高
  • 指定的文档内容越短,分数就越高
  • 我们在搜索时,指定的关键字也会被分词,这个被分词的内容,被分词库匹配的个数越多,分数越高
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 查询neme中含有’收货安装‘,如果同时含有’王五‘,就对该条数据降级(分数-0.5)
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "boosting": {
      "positive": {
        "match": {
          "name": "收货安装"
        }
      },
      "negative": {
        "match": {
          "name": "王五"
        }
      },
      "negative_boost": 0.5
    }
  }
}

filter查询

query查询,会计算文档的匹配度得到一个分数,并且根据分数进行排序,不会缓存

filter:根据你的查询条件去查询文档,不去计算分数,而且filter会对经常被过滤的数据进行缓存

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# max_score为0
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term":{
            "name":"途虎养车"
          }
        },
        {
          "range":{
            "free":{
              "lte":5
            }
          }
        }
      ]
    }
  }
}

高亮查询

就是将用户输入的关键字,以一定的特殊样式展示给用户,让用户知道为什么这个结果被检索出来。

高亮展示的数据,本身就是文档中的一个field,单独将filed以highlight的形式返回给你。

es提供了一个highlight属性,和query同级别。

  • fragment_size:指定高亮数据展示多个个字符出来
  • pre_tags:指定前缀标签
  • post_tags:指定后缀标签,
  • Fields:指定哪几个field以高亮形式返回
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "match": {
      "smsContent":"ajsda"
    }
  },
  "highlight": {
    "fields": {
      "smsContent":{}
    },
    "pre_tags": "<font color='red>",
    "post_tags": "</font>",
    "fragment_size": 15
  }
}

聚合查询

去重计数查询

先将返回的文档中的一个指定field进行去重,统计一共有多少条

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 统计一共有多少省份
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "province_num": {
      "cardinality": {
        "field": "province"
      }
    }
  }
}
# 返回结果
"aggregations" : {
  "province_num" : {
  	"value" : 1
  }
}

范围统计

统计一定范围内出现的文档个数,比如,针对某个field的值在0-100,100-200,200-300之间文档出现的个数分别是多少 返回统计可以针对普通的数值(range),针对时间类型(date_range),针对ip类型(ip_range)都可以做相应的统计。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 统计free(-无穷,5)有多少个,(5,10]有多少个,[10,正无穷)有多少个
# from包含,to不包含
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "free_num": {
      "range": {
        "field": "free",
        "ranges": [
          {
            "to": 5
          },
          {
            "from": 5,
            "to": 10
          },
          {
            "from": 10
          }
        ]
      }
    }
  }
}

# 返回结果
"aggregations" : {
    "free_num" : {
      "buckets" : [
        {
          "key" : "*-5.0",
          "to" : 5.0,
          "doc_count" : 1
        },
        {
          "key" : "5.0-10.0",
          "from" : 5.0,
          "to" : 10.0,
          "doc_count" : 0
        },
        {
          "key" : "10.0-*",
          "from" : 10.0,
          "doc_count" : 0
        }
      ]
    }
  }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "date_num": {
      "date_range": {
        "field": "createDate",
        "format": "yyy", 
        "ranges": [
          {
            "to": 1900
          },
          {
            "from": 1900,
            "to": 2000
          },
          {
            "from": 2000
          }
        ]
      }
    }
  }
}
# 返回结果
"aggregations" : {
    "date_num" : {
      "buckets" : [
        {
          "key" : "*-1900",
          "to" : -2.2089888E12,
          "to_as_string" : "1900",
          "doc_count" : 0
        },
        {
          "key" : "1900-2000",
          "from" : -2.2089888E12,
          "from_as_string" : "1900",
          "to" : 9.466848E11,
          "to_as_string" : "2000",
          "doc_count" : 0
        },
        {
          "key" : "2000-*",
          "from" : 9.466848E11,
          "from_as_string" : "2000",
          "doc_count" : 1
        }
      ]
    }
  }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "ip_num": {
      "ip_range": {
        "field": "ipAddr",
        "ranges": [
          {
            "to": "10.126.2.8"
          },
          {
            "from": "10.126.2.8"
          }
        ]
      }
    }
  }
}
# 返回结果
 "aggregations" : {
    "ip_num" : {
      "buckets" : [
        {
          "key" : "*-10.126.2.8",
          "to" : "10.126.2.8",
          "doc_count" : 1
        },
        {
          "key" : "10.126.2.8-*",
          "from" : "10.126.2.8",
          "doc_count" : 0
        }
      ]
    }
  }

统计聚合查询

查找指定field的最大值,最小值,平均值…

使用extended_stats实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "calc_result":{
      "extended_stats": {
        "field": "free"
      }
    }
  }
}
# 返回结果
"aggregations" : {
    "calc_result" : {
      "count" : 1,
      "min" : 3.0,
      "max" : 3.0,
      "avg" : 3.0,
      "sum" : 3.0,
      "sum_of_squares" : 9.0,
      "variance" : 0.0,
      "std_deviation" : 0.0,
      "std_deviation_bounds" : {
        "upper" : 3.0,
        "lower" : 3.0
      }
    }
  }

地图经纬度搜索

geo_distance

以一个点为中心画圆,查询在该圆内的数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
POST /map/map/_search
{
  "query":{
    "geo_distance":{
      "location":{   # 确定一个点
        "lon":116.433733,
        "lat":39.908404
      },
      "distance": 2000,  # 确定半径
      "distance_type": "arc" #指定形状为圆形
    }
  }
}

geo_bounding_box

以两个点确定一个矩形,获取矩形内的全部数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
POST /map/map/_search
{
  "query":{
    "geo_bounding_box":{
      "location":{
        "top_left":{
          "lon":116.433733,
          "lat":39.908404
        },
        "bottom_right":{
          "lon":115.433733,
          "lat":38.908404
        }
      }
    }
  }
}

geo_polygon

以多个点确定一个多边形,获取多边形内的全部数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /map/map/_search
{
  "query":{
    "geo_polygon":{
      "location":{
        "points":[
          {
            "lon":116.433733,
            "lat":39.908404
          },
          {
            "lon":115.433733,
            "lat":38.908404
          },
          {
            "lon":114.433733,
            "lat":37.908404
          }
        ]
      }
    }
  }
}

通过查询删除

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
POST /sms-logs-index/sms-logs-type/_delete_by_query
{
  "query":{
    "range":{
      "free":{
        "lt":4
      }
    }
  }
}

https://www.processon.com/view/5dbba19fe4b0c555374987e3#map

使用 Hugo 构建
主题 StackJimmy 设计