Elastic Search
ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于Restful web接口。ElasticSearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。ElasticSearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。根据DB-Engines的排名显示,ElasticSearch是最受欢迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。它是 Elastic Stack的核心模块
index索引-type类型-document文档-field字段
一. helloword
我们先来快速启动一个ElasticSearch服务。系统环境为 CentOS release 6.8 (Final)
1.下载
官网:https://www.elastic.co/cn/products/elastic-stack/features
下载页面: https://www.elastic.co/cn/downloads/
点击下图红框处可以选择更多版本,笔记中使用了6.5.4版,Linux的安装包
2.安装
将下载的安装包 elasticsearch-6.5.4.tar.gz 上传到服务器的 /opt目录
使用命令: tar -zxvf elasticsearch-6.5.4.tar.gz -C /usr/local/
将安装包解压至/usr/local/目录
3.启动
在启动前,我们需要修改一些配置文件。
进入解压好的目录中的config目录
vim 打开 elasticsearch.yml,修改配置 network.host: 0.0.0.0 ,保证任意IP均可以访问服务。线上服务不要这样设置,要设成具体的 IP。
vim 打开 jvm.options,elasticserch默认分配1G内存,我们调小一点128m。
修改完后我们返回上级目录,执行 bin/elasticsearch 命令启动服务。
如上图,启动失败了,提示需要配置JAVA_HOME 环境变量,因为elasticSearch是java写的,运行时需要jvm,所以我们要先安装jdk,不多赘述,快速安装一下。
1.上传jdk安装包至 /opt目录 需要1.8以上版本,我们用的是jdk-8u211-linux-x64.tar.gz 2.解压至/usr/local/ 目录:tar -zxvf jdk-8u211-linux-x64.tar.gz -C /usr/local/ 3.配置环境变量 JAVA_HOME: 编辑 vim /etc/profile,底部增加内容 JAVA_HOME=/usr/local/jak1.8.0_211 export PATH=$JAVA_HOME/bin:$PATH 保存并推出。 执行 source /etc/profile 刷新配置文件。 执行java -version命令,显示版本则配置完成
再次执行启动命令。很遗憾又失败了,提示不能使用root用户运行。那就创建个其他用户吧。
创建其他用户,并将文件夹权限赋予相应用户。
#创建一个elasticsearch用户使用默认的elasticsearch用户组 useradd elasticsearch #修改文件所有权 cd .. chown elasticsearch elasticsearch-6.5.4/ -R
好的改完后重启,还是报错,这次是创建内存映射的最大数量太低。
我们接着改。
#先切换回root用户 #修改进程在VMAs(虚拟内存区)创建的内存映射最大数量 vim /etc/sysctl.conf vm.max_map_count=655360 #配置生效 sysctl -p #除了上面的方式,还可以直接 sudo sysctl -w vm.max_map_count=262144
切换回elasticSearch用户,重新启动项目,出现如下信息表示启动成功。
浏览器访问服务地址的9200端口,出现数据,OK完成启动。
这个是前台启动,启动没问题后我们加上-d参数后台启动 bin/elasticsearch -d
本次使用的系统环境是CentOS release 6.8 (Final),如果是其他环境可能遇到的问题不一样,一步一步解决即可。
4.关闭
关闭后台启动的elasticSearch。
#使用jps命令查询进程号 jps #再使用kill 进程号命令 关闭 kill 进程号
5. 安装elasticSearch-head工具
这个工具有好几种安装方法,为了使用简单,我们选择chrome插件。具体请百度。
安装成功后,点击插件,输入服务地址ip点击链接,ok。
使用此工具连接属于前后端分离,因而需要在后台配置跨域问题。
http.cros.enabled: true http.cros.allow-origin: "*" # 跨域坑了我好久。。。这个插件不用跨域,其他的连接方式或许需要
二、restFull API
elasticSearch 提供了丰富的restFull api可以用来操作服务。
我们使用chrome插件postman作为测试工具
1.创建非结构化索引
索引就相当于数据库中的表,要保存数据,就要先创建索引。
#创建索引 PUT /kase { "settings":{ "index": { "number_of_shards":"2", #分片数 "number_of_replicas":"0" # 副本数 } } } #删除索引 DELETE /kase
如下图,可以看到kase索引创建成功。
2.插入数据
`
#插入一个user POST /kase/user/1001 { "id": 1001, "name": "张三", "age": 20, "sex": "男" } #响应 { "_index": "kase", "_type": "user", "_id": "1001", "_version": 1, "result": "created", "_shards": { "total": 1, "successful": 1, "failed": 0 }, "_seq_no": 0, "_primary_term": 1 }
在数据浏览标签中可以看到数据已经插入进去了。前面的非结构化索引指的就是创建索引时并没有指定字段,但是在插入数据时,就根据插入的数据类型来自动确定了字段的类型。
路径不带id的插入,可以看到下面的响应中_id的值是自动生成的
POST /kase/user { "id": 1002, "name": "李四", "age": 30, "sex": "女" } #响应 { "_index": "kase", "_type": "user", "_id": "o1Dzwm8Bp_jVXyAu_0MD", "_version": 1, "result": "created", "_shards": { "total": 1, "successful": 1, "failed": 0 }, "_seq_no": 1, "_primary_term": 1 }
可以看到生成的唯一标识 _id与我们传递的参数id并不一致。
3.更新数据
在elasticSearch中数据是不能被更改的,但是可以通过覆盖的方式来对数据进行更新
3.1全量更新
url规则:PUT /索引/类型/id
PUT /kase/user/1001 { "id": 1001, "name": "张三", "age": 10, #年龄由20改为10 "sex": "男" } #响应 { "_index" : "user", "_type" : "_doc", "_id" : "1001", "_version" : 2, "result" : "updated", "_shards" : { "total" : 1, "successful" : 1, "failed" : 0 }, "_seq_no" : 2, "_primary_term" : 1 }
再修改一次
PUT /kase/user/1001 { "id": 1001, "name": "张三", "age": 12, #年龄改为12,同时不传递sex的值 } #响应 { "_index": "kase", "_type": "user", "_id": "1001", "_version": 3, "result": "updated", "_shards": { "total": 1, "successful": 1, "failed": 0 }, "_seq_no": 3, "_primary_term": 1 }
sex字段丢失了。为了保证数据不丢失,必须每次都附带全部的字段值,全量的覆盖,这不是我们想要的效果。
3.2局部更新
要进行局部的更新需要在路径上增加额外的参数 _update,传参也有所不同
POST /索引/类型/id/_update
POST /kase/user/1001/_update { "doc": { "age": 33 } } #响应 { "_index": "kase", "_type": "user", "_id": "3", "_version": 5, "result": "updated", "_shards": { "total": 1, "successful": 1, "failed": 0 }, "_seq_no": 4, "_primary_term": 1 }
4.删除数据
url规则:DELETE /索引/类型/id
DELETE /kase/user/1001
执行后将_id = 1001的数据删除掉。
如果删除一条不存在的数据,则会响应404,响应的结果会是 not_found
删除一个文档,不会立即从索引中删除,而是先标记为已删除,在后续操作中在统一删除。
5.查询
5.1 id搜索
url规则:GET /索引/类型/id
GET /kase/user/o1Dzwm8Bp_jVXyAu_0MD #响应 { "_index": "kase", "_type": "user", "_id": "o1Dzwm8Bp_jVXyAu_0MD", "_version": 1, "found": true, "_source": { "id": 1002, "name": "李四", "age": 30, "sex": "女" } }
5.2 全部搜索
搜索全部数据,默认只会返回10条数据,如果要获取更多数据,则要通过分页来获取。
GET /kase/user/_search
5.3 关键字搜索
关键字搜索是在全部搜索的基础上携带路径参数实现的
#搜索年龄=22的user对象。 GET /kase/user/_search?q=age:22
6.DSL搜索
elasticSearch提供了丰富且灵活的查询方式,叫做DSL查询,它允许你构建更加复杂、强大的查询。
查询年龄是33的user
POST /kase/user/_search { "query": { "match": { #匹配 "age": 33 } } }
查询年龄大于30的男性user
POST /kase/user/_search { "query":{ "bool":{ "filter":{ "range":{ "age":{ "gt":30 } } }, "must":{ "match":{ "sex":"男" } } } } } #响应 { "took": 30, "timed_out": false, "_shards": { "total": 2, "successful": 2, "skipped": 0, "failed": 0 }, "hits": { "total": 0, "max_score": null, "hits": [] } }
全文搜索匹配姓名为张三或者李四的文档
POST /kase/user/_search { "query":{ "match":{ "name":"张三 李四" } } } #响应 { "took": 17, "timed_out": false, "_shards": { "total": 2, "successful": 2, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1.3862944, "hits": [ { "_index": "kase", "_type": "user", "_id": "o1Dzwm8Bp_jVXyAu_0MD", "_score": 1.3862944, #匹配度 "_source": { "id": 1002, "name": "李四", "age": 30, "sex": "女" } }, { "_index": "kase", "_type": "user", "_id": "1001", "_score": 1.3862944, #匹配度 "_source": { "id": 1001, "name": "张三", "age": 33 } } ] } }
7.高亮显示
高亮显示是基于DSL搜索的,只需要在搜索的json参数中附带额外的高亮参数指定高亮的字段即可
POST /kase/user/_search { "query":{ "match":{ "name":"张三 李四" } }, #高亮 "highlight":{ "fields":{ "name":{} } } } #响应中额外存在高亮的标签 { "took": 63, "timed_out": false, "_shards": { "total": 2, "successful": 2, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1.3862944, "hits": [ { "_index": "kase", "_type": "user", "_id": "o1Dzwm8Bp_jVXyAu_0MD", "_score": 1.3862944, "_source": { "id": 1002, "name": "李四", "age": 30, "sex": "女" }, "highlight": { "name": [ "<em>李</em><em>四</em>" ] } }, { "_index": "kase", "_type": "user", "_id": "1001", "_score": 1.3862944, "_source": { "id": 1001, "name": "张三", "age": 33 }, "highlight": { "name": [ "<em>张</em><em>三</em>" ] } } ] } }
8.聚合
聚合相当于关系型数据库中的 group by 分组
{ "aggs":{ "all_interests":{ "terms":{ "fields":"age" } } } } #响应,最下面是聚合结果 { "took": 72, "timed_out": false, "_shards": { "total": 2, "successful": 2, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1, "hits": [ { "_index": "kase", "_type": "user", "_id": "o1Dzwm8Bp_jVXyAu_0MD", "_score": 1, "_source": { "id": 1002, "name": "李四", "age": 30, "sex": "女" } }, { "_index": "kase", "_type": "user", "_id": "1001", "_score": 1, "_source": { "id": 1001, "name": "张三", "age": 33 } } ] }, "aggregations": { "all_interests": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": 30,#聚合的值 "doc_count": 1 #获取到的个数 }, { "key": 33, "doc_count": 1 } ] } } }
三、核心详解
1.文档
文档document指的就是存储在elastisearch 分片中的数据,它是以json格式进行存储的,可以存储复杂的结构。
{ "_index": "kase", "_type": "user", "_id": "1001", "_version": 4, "found": true, "_source": { "id": 1001, "name": "张三", "age": 33, "car":{ "color":"red" } } }
文档中不仅仅存储有自己的属性,还存储有元数据(metadata)
节点 说明 _index 文档存储的地方(索引) _type 文档代表的对象的类型 _id 文档的唯一标识 索引 index 相当于mysql的数据库 , 而类型type 相当于表 , 区别在于当在type中定义了字段类型后,就不能修改了,而且是整个index通用的 . 比如 定义了一个 user类型,里面有一个 name字段是test的,那么在其他的类型中name也只能是test . 因而高版本的elasticSearch逐步取消了type 。可以将index理解为一个表 , type属性废弃不用。
2.响应
2.1 美化响应
可以在查询url后面添加pretty参数,使得返回的json更易查看。不添加的话,在浏览器中就直接是显示一行。
GET /kase/user/3?pretty
{
"_index": "kase",
"_type": "user",
"_id": "3",
"_version": 5,
"found": true,
"_source": {
"name": "王五",
"age": 30,
"six": "男",
"height": 10
}
}
2.2 指定响应字段
查询时每次返回所有字段,我们可以在url后增加_search参数来限定响应的字段
GET /kase/user/3?_source=name,age
{
"_index": "kase",
"_type": "user",
"_id": "3",
"_version": 5,
"found": true,
"_source": {
"name": "王五",
"age": 30
}
}
上面的返回值包含有元数据,能否去掉元数据的响应呢,也是可以的
GET /kase/user/3/_source? _source=name,age
{
"name": "王五",
"age": 30
}
3.判断文档是否存在
如果我们只需要判断文档是否存在,而不是查询文档内容,那么可以这样:
HEAD /kase/user/1
状态值 status 200 就表示文档存在,status 404则表示不存在。
4.批量操作
4.1 批量查询
POST /kase/user/_mget
#请求参数
{
"ids":["1","2","3"]
}
响应
{
"docs": [
{
"_index": "kase",
"_type": "user",
"_id": "1",
"_version": 2,
"found": true,
"_source": {
"name": "李四",
"age": 30,
"six": "女",
"height": "165"
}
},
{
"_index": "kase",
"_type": "user",
"_id": "2",
"_version": 1,
"found": true,
"_source": {
"name": "张三",
"age": 21,
"six": "男",
"height": "175"
}
},
{
"_index": "kase",
"_type": "user",
"_id": "3",
"_version": 5,
"found": true,
"_source": {
"name": "王五",
"age": 30,
"six": "男",
"height": 10
}
}
]
}
如果请求的id中有不存在的,并不会影响其他的响应
#请求参数
{
"ids":["1","2","3","300"]
}
#响应
{
"docs": [
{
"_index": "kase",
"_type": "user",
"_id": "1",
"_version": 2,
"found": true,
"_source": {
"name": "李四",
"age": 30,
"six": "女",
"height": "165"
}
},
{
"_index": "kase",
"_type": "user",
"_id": "2",
"_version": 1,
"found": true,
"_source": {
"name": "张三",
"age": 21,
"six": "男",
"height": "175"
}
},
{
"_index": "kase",
"_type": "user",
"_id": "3",
"_version": 5,
"found": true,
"_source": {
"name": "王五",
"age": 30,
"six": "男",
"height": 10
}
},
{
"_index": "kase",
"_type": "user",
"_id": "300",
"found": false
}
]
}
4.2 _bulk操作
在Elasticsearch中,支持批量的插入、修改、删除操作,都是通过_bulk的api完成的。 请求格式如下:(请求格式不符合json格式要求)
{ action: { metadata }}\n #命令行
{ request body }\n #参数行
{ action: { metadata }}\n
{ request body }\n
批量插入,注意每行末尾的换行符。
命令 GET /_bulk 如果命令行已经指定了索引和类型,则路径中可以不附带。如果路径中附带了则命令行中也可以不指定。
{"create":{"_index":"kase","_type":"user","_id":"6"}}
{"name":"唐僧","age":"23","six":"男","height":"174"}
{"create":{"_index":"kase","_type":"user","_id":"7"}}
{"name":"孙悟空","age":"1024","six":"男","height":"164"}
{"create":{"_index":"kase","_type":"user","_id":"8"}}
{"name":"猪八戒","age":"34","six":"男","height":"176"}
{"create":{"_index":"kase","_type":"user","_id":"9"}}
{"name":"沙悟净","age":"36","six":"男","height":"182"}
#响应
{
"took": 46,
"errors": false,
"items": [
{
"create": {
"_index": "kase",
"_type": "user",
"_id": "6",
"_version": 1,
"result": "created",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 5,
"_primary_term": 1,
"status": 201
}
},
{
"create": {
"_index": "kase",
"_type": "user",
"_id": "7",
"_version": 1,
"result": "created",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 5,
"_primary_term": 1,
"status": 201
}
},
{
"create": {
"_index": "kase",
"_type": "user",
"_id": "8",
"_version": 1,
"result": "created",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 6,
"_primary_term": 1,
"status": 201
}
},
{
"create": {
"_index": "kase",
"_type": "user",
"_id": "9",
"_version": 1,
"result": "created",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 7,
"_primary_term": 1,
"status": 201
}
}
]
}
批量删除 命令行为 delete,命令如下,此处不做测试。
{"delete":{"_index":"kase","_type":"user","_id":"1"}}
{"delete":{"_index":"kase","_type":"user","_id":"2"}}
{"delete":{"_index":"kase","_type":"user","_id":"3"}}
其他操作类似。
一次请求多少性能高?
- 整个批量请求需要被加载到接受我们请求节点的内存里,所以请求越大,给其它请求可用的内存就越小。有一 个最佳的bulk请求大小。超过这个大小,性能不再提升而且可能降低。
- 最佳大小,当然并不是一个固定的数字。它完全取决于你的硬件、你文档的大小和复杂度以及索引和搜索的负载。
- 幸运的是,这个最佳点(sweetspot)还是容易找到的:试着批量索引标准的文档,随着大小的增长,当性能开始降低,说明你每个批次的大小太大了。开始的数量可以在1000~5000个文档之间,如果你的文档非常大,可以 使用较小的批次。
- 通常着眼于你请求批次的物理大小是非常有用的。一千个1kB的文档和一千个1MB的文档大不相同。一个好的批次好保持在5-15MB大小间。
5. 分页
默认的 _search 查询只会返回10条数据,和SQL使用 LIMIT 关键字返回只有一页的结果一样,Elasticsearch接受 from 和 size 参数用来进行分页查询:
size: 结果数,默认就是10。
from: 跳过开始的结果数,默认是 0 。
如果想每页显示5条数据,分别获取 1,2,3页码的数据
GET /_search?size=5
GET /_search?size=5&from=5
GET /_search?size=5&from=10
应该当心分页太深或者一次请求太多的结果。结果在返回前会被排序。但是记住,一个搜索请求常常涉及多个分片。每个分片生成自己排好序的结果,它们接着需要集中起来排序以确保整体排序正确。
在集群系统中深度分页
为了理解为什么深度分页是有问题的,让我们假设在一个有5个主分片的索引中搜索。当我们请求结果的第一 (结果1到10)时,每个分片产生自己顶端10个结果然后返回它们给请求节点(requesting node),它再 排序这所有的50个结果以选出顶端的10个结果。现在假设我们请求第1000页——结果10001到10010。工作方式都相同,不同的是每个分片都必须产生顶端的 10010个结果。然后请求节点排序这50050个结果并丢弃50040个!你可以看到在分布式系统中,排序结果的花费随着分页的深入而成倍增长。这也是为什么网络搜索引擎中任何 语句不能返回多于1000个结果的原因。
6.映射(mapping)
前面我们创建的索引以及插入数据,都是由Elasticsearch进行自动判断类型,有些时候我们是需要进行明确字段型 的,否则,自动判断的类型和实际需求是不相符的。
自动判断的规则如下:
JSON type | Field type |
---|---|
Boolean: true or false | “boolean” |
Whole number: 123 | “long” |
Floating point: 123.45 | “double” |
String, valid date: “2014-09-15” | “date” |
String: “foo bar” | “string” |
Elasticsearch中支持的类型如下:
类型 | 表示的数据类型 |
---|---|
String | string , text , keyword |
Whole number | byte , short , integer , long |
Floating point | float , double |
Boolean | boolean |
Date | date |
- string类型在ElasticSearch 旧版本中使用较多,从ElasticSearch 5.x开始不再支持string,由text和 keyword类型替代。
- text 类型,当一个字段是要被全文搜索的,比如Email内容、产品描述,应该使用text类型。设置text类型 以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项。text类型的字段 不用于排序,很少用于聚合。
- keyword类型适用于索引结构化的字段,比如email地址、主机名、状态码和标签。如果字段需要进行过 滤(比如查找已发布博客中status属性为published的文章)、排序、聚合。keyword类型的字段只能通过精 确值搜索到。
创建明确类型的索引
PUT /chadan
{
"settings": {
"index": {
"number_of_shards": "2",
"number_of_replicas": "0"
}
},
"mappings": {
"person": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "integer"
},
"mail": {
"type": "keyword"
},
"hobby": {
"type": "text"
}
}
}
}
}
查看映射:
GET /kase/_mapping
#响应
{
"kase": {
"mappings": {
"user": {
"properties": {
"age": {
"type": "long"
},
"height": {
"type": "text",
#默认的给text类型字段下的fields还会创建一个 keyword类型,只取
#长度256
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"six": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
GET /chadan/_mapping
#响应
{
"chadan": {
"mappings": {
"person": {
"properties": {
"age": {
"type": "integer"
},
"hobby": {
"type": "text"
},
"mail": {
"type": "keyword"
},
"name": {
"type": "text"
}
}
}
}
}
}
插入数据
POST /chadan/person/_bulk
#请求
{"create":{"_id":"1"}}
{"name":"张三","age": 20,"mail": "111@qq.com","hobby":"羽毛球、乒乓球、足球"}
{"create":{"_id":"2"}}
{"name":"李四","age": 21,"mail": "222@qq.com","hobby":"羽毛球、乒乓球、足球、篮球"}
{"create":{"_id":"3"}}
{"name":"王五","age": 22,"mail": "333@qq.com","hobby":"羽毛球、篮球、游泳、听音乐"}
{"create":{"_id":"4"}}
{"name":"赵六","age": 23,"mail": "444@qq.com","hobby":"跑步、游泳"}
{"create":{"_id":"5"}}
{"name":"孙七","age": 24,"mail": "555@qq.com","hobby":"听音乐、看电影"}
#响应
{
"took": 68,
"errors": false,
"items": [
{
"create": {
"_index": "chadan",
"_type": "person",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1,
"status": 201
}
},
{
"create": {
"_index": "chadan",
"_type": "person",
"_id": "2",
"_version": 1,
"result": "created",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1,
"status": 201
}
},
{
"create": {
"_index": "chadan",
"_type": "person",
"_id": "3",
"_version": 1,
"result": "created",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1,
"status": 201
}
},
{
"create": {
"_index": "chadan",
"_type": "person",
"_id": "4",
"_version": 1,
"result": "created",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1,
"status": 201
}
},
{
"create": {
"_index": "chadan",
"_type": "person",
"_id": "5",
"_version": 1,
"result": "created",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 3,
"_primary_term": 1,
"status": 201
}
}
]
}
测试搜索
POST /chadan/_search
#请求
{
"query":{
"match":{
"hobby":"音乐"
}
}
}
#响应
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 2,
"successful": 2,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 2.5574043,
"hits": [
{
"_index": "chadan",
"_type": "person",
"_id": "5",
"_score": 2.5574043,
"_source": {
"name": "孙七",
"age": 24,
"mail": "555@qq.com",
"hobby": "听音乐、看电影"
}
},
{
"_index": "chadan",
"_type": "person",
"_id": "3",
"_score": 0.5753642,
"_source": {
"name": "王五",
"age": 22,
"mail": "333@qq.com",
"hobby": "羽毛球、篮球、游泳、听音乐"
}
}
]
}
}
7.结构化查询
7.1 term查询
term
主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed
的字符串(未经分析的文本数据类型):
{ "term": { "age": 26 }}
{ "term": { "date": "2014-09-01" }}
{ "term": { "public": true }}
{ "term": { "tag": "full_text" }}
POST /chadan/_search
#请求
{
"query":{
"term":{
"age":20
}
}
}
#响应
{
"took": 13,
"timed_out": false,
"_shards": {
"total": 2,
"successful": 2,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "chadan",
"_type": "person",
"_id": "3",
"_score": 1,
"_source": {
"name": "王五",
"age": 22,
"mail": "333@qq.com",
"hobby": "羽毛球、篮球、游泳、听音乐"
}
}
]
}
}
7.2 terms查询
terms
跟 term
有点类似,但 terms
允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配:
POST /chadan/_search
#请求
{
"query":{
"terms":{
"age":[23,22,25]
}
}
}
#响应
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 2,
"successful": 2,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "chadan",
"_type": "person",
"_id": "3",
"_score": 1,
"_source": {
"name": "王五",
"age": 22,
"mail": "333@qq.com",
"hobby": "羽毛球、篮球、游泳、听音乐"
}
},
{
"_index": "chadan",
"_type": "person",
"_id": "4",
"_score": 1,
"_source": {
"name": "赵六",
"age": 23,
"mail": "444@qq.com",
"hobby": "跑步、游泳"
}
}
]
}
}
7.3 range查询
range
过滤允许我们按照指定范围查找一批数据:
{
"query":{
"range":{
"age":{
"gte":20,
"lt":30
}
}
}
}
范围操作符包含: gt :: 大于 gte :: 大于等于 lt :: 小于 lte :: 小于等于
POST /chadan/_search
#请求
{
"query":{
"range":{
"age":{
"gte":22,
"lte":25
}
}
}
}
#响应
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 2,
"successful": 2,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1,
"hits": [
{
"_index": "chadan",
"_type": "person",
"_id": "3",
"_score": 1,
"_source": {
"name": "王五",
"age": 22,
"mail": "333@qq.com",
"hobby": "羽毛球、篮球、游泳、听音乐"
}
},
{
"_index": "chadan",
"_type": "person",
"_id": "4",
"_score": 1,
"_source": {
"name": "赵六",
"age": 23,
"mail": "444@qq.com",
"hobby": "跑步、游泳"
}
},
{
"_index": "chadan",
"_type": "person",
"_id": "5",
"_score": 1,
"_source": {
"name": "孙七",
"age": 24,
"mail": "555@qq.com",
"hobby": "听音乐、看电影"
}
}
]
}
}
7.4 exists 查询
exists
查询可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的 IS_NULL
条件
{
"exists":{
"field":"title"
}
}
这个查询只是针对已经查出一批数据来,但是想区分出某个字段是否存在的时候使用。
POST /chadan/_search
#请求
{
"query":{
"exists":{
"field":"age"
}
}
}
#响应
{
"took": 13,
"timed_out": false,
"_shards": {
"total": 2,
"successful": 2,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 5,
"max_score": 1,
"hits": [
{
"_index": "chadan",
"_type": "person",
"_id": "3",
"_score": 1,
"_source": {
"name": "王五",
"age": 22,
"mail": "333@qq.com",
"hobby": "羽毛球、篮球、游泳、听音乐"
}
},
{
"_index": "chadan",
"_type": "person",
"_id": "2",
"_score": 1,
"_source": {
"name": "李四",
"age": 21,
"mail": "222@qq.com",
"hobby": "羽毛球、乒乓球、足球、篮球"
}
},
{
"_index": "chadan",
"_type": "person",
"_id": "4",
"_score": 1,
"_source": {
"name": "赵六",
"age": 23,
"mail": "444@qq.com",
"hobby": "跑步、游泳"
}
},
{
"_index": "chadan",
"_type": "person",
"_id": "5",
"_score": 1,
"_source": {
"name": "孙七",
"age": 24,
"mail": "555@qq.com",
"hobby": "听音乐、看电影"
}
},
{
"_index": "chadan",
"_type": "person",
"_id": "1",
"_score": 1,
"_source": {
"name": "张三",
"age": 20,
"mail": "111@qq.com",
"hobby": "羽毛球、乒乓球、足球",
"height": "哈哈哈"
}
}
]
}
}
7.5 match查询
match
查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。 如果你使用 match
查询一个全文本字段,它会在真正查询之前用分析器先分析 match
一下查询字符:
{
"query":{
"match":{
"hobby":"足球 音乐"
}
}
}
如果用 match
下指定了一个确切值,在遇到数字,日期,布尔值或者 not_analyzed
的字符串时,它将为你搜索你 给定的值:
{ "match": { "age": 26 }}
{ "match": { "date": "2014-09-01" }}
{ "match": { "public": true }}
{ "match": { "tag": "full_text" }}
7.6 bool查询
bool 查询可以用来合并多个条件查询结果的布尔逻辑,它包含一下操作符: must :: 多个查询条件的完全匹配,相当于 and 。 must_not :: 多个查询条件的相反匹配,相当于 not 。 should :: 至少有一个查询条件匹配, 相当于 or 。 这些参数可以分别继承一个查询条件或者一个查询条件的数组:
POST /chadan/_search
#请求
{
"query":{
"bool":{
"must":{
"range":{
"age":{
"gt":20
}
}
},
"must_not":{
"term":{
"age":"24"
}
},
"should":[
{
"term":{
"mail":"444@qq.com"
}
}
]
}
}
}
#响应
{
"took": 5,
"timed_out": false,
"_shards": {
"total": 2,
"successful": 2,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 2.3862944,
"hits": [
{
"_index": "chadan",
"_type": "person",
"_id": "4",
"_score": 2.3862944,
"_source": {
"name": "赵六",
"age": 23,
"mail": "444@qq.com",
"hobby": "跑步、游泳"
}
},
{
"_index": "chadan",
"_type": "person",
"_id": "3",
"_score": 1,
"_source": {
"name": "王五",
"age": 22,
"mail": "333@qq.com",
"hobby": "羽毛球、篮球、游泳、听音乐"
}
},
{
"_index": "chadan",
"_type": "person",
"_id": "2",
"_score": 1,
"_source": {
"name": "李四",
"age": 21,
"mail": "222@qq.com",
"hobby": "羽毛球、乒乓球、足球、篮球"
}
}
]
}
}
文档信息
- 本文作者:chayedankase
- 本文链接:https://chayedankase.github.io/2020/01/20/Elastic-Search/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)