学习背景
由于最近工作需要,需要大量使用elasticsearch代替mysql数据库进行数据存储和查询操作,但是目前关于相关的学习文档较少,官方文档看着也较为费劲。所以记录一下我的学习记录。
介绍
Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式的全文搜索引擎,基于restful web接口。Elasticsearch是用Java语言开发的,基于Apache协议的开源项目,是目前最受欢迎的企业搜索引擎。Elasticsearch广泛运用于云计算中,能够达到实时搜索,具有稳定,可靠,快速的特点。
关于Elasticsearch和可视化项目kibana的安装这里就不多做介绍,需要的可以自行百度检索安装。
与Mysql数据库的对应关系
Elasticsearch | Mysql |
---|---|
索引(index) | 数据库(databases) |
类型(type) | 表(table) |
文档(document) | 行(row) |
在Elasticsearch 7.X以后,官方将过度性移除类型(type),在8.X以后将完成移除type。此时Elasticsearch 数据结构将为两层结构:索引和文档。
Elasticsearch基本操作
- 在学习Elasticsearch基本操作命令之前需要了解restful风格接口在其应用中的规范
请求类型 | 作用 | 差异 |
---|---|---|
GET | 查询操作 | 无 |
PUT | 添加和修改操作 | 在进行添加操作时必须指定文档id,否则报错 |
POST | 基本的查询操作 | 在添加时可以不指定文档id,系统会随机为其生成一个文档id |
DELETE | 删除操作 | 无 |
1. 增删改操作
1.1 增加操作(POST、PUT)
将name=小明,age=12数据插入到索引为test,类型为dd中保存
- 使用POST添加并且不带文档id
POST test/dd/
{
"name" : "小明",
"age" : 12
}
返回的结果:
{
"_index" : "test",
"_type" : "dd",
"_id" : "z3EduXoBtsMqPf0VWJNp",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
其结果中的result字段代表了该操作的状态和类型,"created"表示新添加的并且成功了。并且该数据所在的索引、类型和文档id都进行了显示。当POST没有指定文档id时,系统会自动创建一个随机id。
- 使用POST添加并且带文档id
POST test/dd/1
{
"name" : "小强",
"age" : 13
}
其结果为:
{
"_index" : "test",
"_type" : "dd",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1
}
可以看出当带上文档id时,其就使用用户指定的id。
- 使用PUT添加
PUT test/dd/2
{
"name" : "小文",
"age" : 14
}
其结果和前面一样,这里就不再赘述。
当使用PUT进行添加操作时,如果不指定文档id时,将会报错。如下所示:
{
"error" : "Incorrect HTTP method for uri [/test/dd/?pretty=true] and method [PUT], allowed: [POST]",
"status" : 405
}
1.2 修改操作(POST、PUT)
修改操作和新增操作一样,主要区别是,修改的前提必须在需要修改的索引-类型-文档id下已经存在数据。并且其返回结果的表示为:updated
1.3 删除操作(DELETE)
删除数据的语句可分为两类:
1、DELETE 索引 //删除整个所有下的全部数据,相当于mysql的drop database 数据库名
2、DELETE 索引/类型/文档id //删除某个文档id下的数据,相当于mysql的delete操作(删除一行)
Elasticsearch不支持删除类型操作,需要删除类型的话,就把该类型下的所有数据删除即可。
2. 查询操作
- 为了更好的进行后续操作,本文将导入官方提供的测试数据。执行下面语句完成导入
/**
*_bulk为elasticsearch的批量插入操作关键字,在索引为info,类型为account下插入数据
*/
POST /info/account/_bulk
复制测试文件里的所有内容
- 测试数据文件地址:链接:https://pan.baidu.com/s/1SW1hC9io1iEt1aSwYcvAPA 提取码:1234
2.1 简单查询
- 全索引查询
GET /bank/_search
其结果如下所示:由于数据太多,这里只展示第一条数据,后续数据省略。
{
"took" : 339,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1000,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "info",
"_type" : "account",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"account_number" : 1,
"balance" : 39225,
"firstname" : "Amber",
"lastname" : "Duke",
"age" : 32,
"gender" : "M",
"address" : "880 Holmes Lane",
"employer" : "Pyrami",
"email" : "amberduke@pyrami.com",
"city" : "Brogan",
"state" : "IL"
}
},
......
我们从hits内的hits可以查看到数据对应的键值对。下面将通过这些键值对完成复杂操作的查询。
- 指定文档id查询
- 假如我们想查询第一条数据,则可以通过制定文档id完成制定数据查询
GET /info/account/1
2.2 DSL语句查询
- elasticsearch查询模板
GET /索引名/_search //带前缀 "_" 的都是elasticsearch自带的关键字
{
"query": {
"想要查询的类型":{
}
}
}
- 全数据查询(match_all)
GET /info/_search
{
"query": {
"match_all": {}
}
}
- 指定字段查询
1、通过match关键字查询
GET /info/_search
{
"query": {
"match": {
"FIELD": "TEXT"
}
}
}
2、通过term关键字查询
GET /info/_search
{
"query": {
"term": {
"FIELD": "TEXT"
}
}
}
3、通过match_phrase关键字查询
GET /info/_search
{
"query": {
"match_phrase": {
"FIELD": "TEXT"
}
}
}
上面三种关键字都可以对指定字段进行查询,其主要区别如下表所示:
关键字 | 特点 | 案例(查询"雷神") |
---|---|---|
match | 模糊查询(text类型),会对待字段进行分词操作,只要包含该字段和其分词后的字都可以查询到。 | ,只要包含"雷",“神”, "雷神"这些关键字的字段都会被查询到 |
term | 精准查询,将待查询的字段视为keyword类型,不会对其进行分词操作 | 只能查询到包含"雷神"的字段 |
match_phrase | 精准查询,会进行分词操作,与match主要不同的是,其会查询包含所有分词后的语句并且其顺序要和原有顺序一致,相当于一种段落精准查询。 | 进行分词为"雷",“神”, “雷神”,可以查询到顺序和分词顺序一致的结果 |
- bool查询
bool类型共包含四种查询类型:must、must_not、should、filter。
GET /info/_search
{
"query": {
"bool": {
"must": [ //必须包含
{
"match": {
"FIELD": "TEXT"
}
}
],
"must_not": [ //一定不包含
{
"match": {
"FIELD": "TEXT"
}
}
],
"should": [ //可以包含,相当于or
{
"match": {
"FIELD": "TEXT"
}
}
],
"filter": [ //指定字段进行查询
{
"term": {
"FIELD": "VALUE"
}
}
]
}
}
}
从上面可以看出,关键字filter的和query的功能相似,但是其由一定的差异,如下表
关键字 | 特点 |
---|---|
query | 进行基本的查询操作,同时对查询的结果贡献相关性得分,从而使结果进行排序 |
filter | 只有查询操作,其查询的结果不会贡献相关性得分 |
- 范围查询
如果我们想想Mysql一样使用">“或者”<"符合来查询范围区间时,在elasticsearch中可以使用关键字"range"来完成,具体格式如下,该关键字和match等关键字一样,可以使用在同样位置。
"range": {
"age": {
"gt(e)": value1, //大于(等于)value1
"lt(e)": value2 //小于(等于)value2
}
}
-
聚合查询
- 聚合查询其含义与Mysql的聚合函数相似,可以为我们提高对数据的分析操作。其关键字为"aggs"
其格式如下所示:
- 聚合查询其含义与Mysql的聚合函数相似,可以为我们提高对数据的分析操作。其关键字为"aggs"
"aggs": { //聚合查询关键之,用来声明一个聚合查询段落
"name": { //自己定义的聚合查询的名称
"AGG_TYPE": { //聚合的类型
"field": field, //指定需要聚合操作的字段名称
}
}
}
elasticsearch提供了较多的聚合类型,常见的类型分为如下关键字:terms、sum、max、min、avg等。其中后四种用法相同,如上模板所示,其功能主要对指定的字段进行相应的求和、求最大值、最小值、均值等。但是对于terms关键字用法有些区别,具体如下:
"aggs": {
"aggGender": {
"terms": {
/**
* 需要聚合操作的字段名,**需要注意的是:对于text类型的字段,
* 需要使用filed.keyword类型进行聚合,类似于查询关键字term**
*/
"field": "field",
"size": n //聚合后展示的数量大小,默认10
}
}
}
例如下面的一段查询操作:
GET info/_search
{
"query": {
"match": {
"address": "mill"
}
},
"aggs": {
"aggGender": {
"terms": {
"field": "gender.keyword",
"size": 10
}
}
}
}
上面的操作含义为:首先对address字段进行模糊查询,后面的聚合操作时基于前面的查询结果进行聚合操作。按照性别进行聚合,也就是将"男"和"女"字段的数据进行分组。结果如下所示
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4, //共查询到4条数据
"relation" : "eq"
},
"max_score" : 5.4032025,
"hits" : [
{
这里为了方便展示全部数据,将查询到的结果数据进行省略
}
]
},
//聚合函数结果
"aggregations" : {
"aggGender" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "M",
"doc_count" : 3 //表示查询出来的4条数据中有3条是男性
},
{
"key" : "F",
"doc_count" : 1 //表示查询出来的4条数据中有1条是女性
}
]
}
}
}
- 嵌套聚合操作
当我们想对上一步聚合后的结果进行更加细粒度化,就可以在原有基础上再封装一层聚合操作。比如我们需要得到从上述查询出来的数据中,男女不同性别的平均薪水。
GET info/_search
{
"query": {
"match": {
"address": "mill"
}
},
"aggs": {
"aggGender": {
"terms": {
"field": "gender.keyword",
"size": 10
},
//在上一步聚合后,再进行一层嵌套聚合操作
"aggs": {
"aggBalanceAvg": {
"avg": { //基于前面的聚合数据获取薪资均值
"field": "balance" //薪资字段balance
}
}
}
}
}
}
输出结果如下所示:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 5.4032025,
"hits" : [
{
这里为了方便展示全部数据,将查询到的结果数据进行省略
}
]
},
"aggregations" : {
"aggGender" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "M",
"doc_count" : 3,
"aggBalanceAvg" : { //二次子聚合名称
"value" : 25087.0 //男性中的平均薪水
}
},
{
"key" : "F",
"doc_count" : 1,
"aggBalanceAvg" : {
"value" : 25571.0 //女性中的平均薪水
}
}
]
}
}
}
好了,学到这里,你对es的基本入门操作已经大概了解了,下一遍将会继续讲解elasticsearch的高级客户端的基本使用,也即整合到springboot的使用。
这是本人的第一篇博客,有不足之处还望各位老哥多多指点,后续将会持续输出更多互联网公司必备的技术和学习心得。