elasticsearch简介
什么是elasticsearch
elasticsearch(简称es),其核心是 Elastic Stack,es是一个基于 Apache Lucene(TM)的开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据,本身扩展性很好,可以扩展到上百台服务器,处理 PB 级别的数据。es 存储的是序列化为 json 文档的复杂数据结构,而不是以行列数据的形式存储数据,当集群中有多个 es 节点时,存储的文档分布在整个集群中,可以立即从任何节点访问。es 使用 Lucene 作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 RESTful API 来隐藏 Lucene 的复杂性,从而让全文搜索变得简单。
当存储文档时速度极快,在1秒内就可以被索引和完全搜索, es 使用倒排索引的数据结构,它支持非常快速的全文搜索,倒排索引列出任何文档中出现的每个唯一单词,并标识每个单词出现的所有文档。
可以将索引看作是文档的优化集合,每个文档是字段的集合,这些字段是包含数据的键值对,默认情况下,es 对每个字段中的所有数据建立索引,每个索引字段都有一个专用的、优化的数据结构,例如,文本字段存储在倒排索引中,数字和地理字段存储在 BKD 数中,使用每个字段的数据结构来组装和返回搜索结果的能力是 es 效率极高的原因。
es 还具有无模式的能力,这意味着可以对文档自动创建索引,而不必显示地指定如何处理文档中可能出现的每个不同字段,当动态映射被启用时,es 会自动检测并向索引添加新的字段,这种默认行为使得创建索引和浏览数据变得很容易,只要开始创建索引文档时,es 就会检测布尔值、浮点值和整数值、日期和字符串,并将它们映射到合适的 es 数据类型。
es 和 mysq 对比
es 和 solr
Lucene 是 Apache 软件基金会 Jakarta 项目组的一个子项目,提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在 Java 开发环境里 Lucene 是一个成熟的免费开源工具。就其本身而言,Lucene 是当前以及最近几年最受欢迎的免费 Java 信息检索程序库。但 Lucene 只是一个提供全文搜索功能类库的核心工具包,而真正使用它还需要一个完善的服务框架搭建起来进行应用。
目前市面上流行的搜索引擎软件,主流的就两款:Elasticsearch 和 Solr,这两款都是基于 Lucene 搭建的,可以独立部署启动的搜索引擎服务软件。由于内核相同,所以两者除了服务器安装、部署、管理、集群以外,对于数据的操作 修改、添加、保存、查询等等都十分类似。
在使用过程中,一般都会将 Elasticsearch 和 Solr 这两个软件对比,然后进行选型。这两个搜索引擎都是流行的,先进的的开源搜索引擎。它们都是围绕核心底层搜索库 - Lucene 构建的 - 但它们又是不同的。像所有东西一样,每个都有其优点和缺点:
以下是它们两个之间的一些对比:
- 架构和可扩展性:es 采用分布式架构,具有水平扩展性,可以轻松地处理大规模数据。而 solr 采用分布式架构,可扩展性稍差。
- 数据处理和索引:es 具有实时数据处理功能,可以在数据写入后几秒内进行搜索。它使用倒排索引以及分片和复制机制来高效地存储和索引数据。而 solr 也支持实时数据处理,搜索延迟可能会更高一些。它使用倒排索引来存储和索引数据。
- 查询语言和功能:es 使用 JSON 格式的查询语言,称为 Query DSL,它提供了丰富的查询和过滤功能,包括全文搜索、聚合、地理位置搜索等。而solr 使用类似SQL的查询语言,称为 solr 查询语法,它也提供了类似的查询和过滤功能,相对于 es,其查询语法可能更简洁和直观。
- 生态系统和社区支持:es 拥有庞大的开发者社区和活跃的生态系统,提供了大量的插件和工具,以及广泛的文档和教程。而 solr 的生态系统相对较小,但它也有一些常用的插件和工具,并且有一定的社区支持。
- 使用场景:es 适用于构建实时搜索、日志分析、监控和大数据分析等应用程序,特别是需要快速实时查询和大规模数据处理的场景。而 solr 适用于构建企业级搜索应用程序,如电子商务网站、内容管理系统等,以及需要较为复杂的查询和过滤功能的场景。
需要根据具体的业务需求和性能要求来选择适合的搜索引擎。ES在实时性和可扩展性方面表现突出,适用于大规模数据处理和实时搜索场景;而Solr则更适合于传统的企业级搜索应用程序。
es 的优点
es 作为一款强大的分布式搜索引擎,具有以下优点:
- 强大的全文搜索功能:es 使用倒排索引和分词技术,能够高效地执行全文搜索,支持模糊匹配、多字段搜索、排序和聚合等功能。
- 实时性:es 支持实时数据处理,可以在数据写入后几秒内进行搜索。这使得它非常适合于实时监控、日志分析和实时搜索等场景。
- 分布式架构和可扩展性:es 基于分布式架构,可以水平扩展以处理大规模数据。它使用分片和复制机制来保证数据的可靠性和高可用性。
- 多样化的查询和聚合功能:es 提供丰富的查询和聚合功能,包括全文搜索、过滤、范围查询、地理位置搜索、聚合分析等。这些功能使得开发者能够灵活地进行数据查询和分析。
- 多语言支持:es 支持多种编程语言的客户端库,如 Java、Python、Ruby 等,使得开发者可以使用自己熟悉的语言与 es 进行交互。
- 易于使用和部署:es 具有简单的 API 和易于理解的文档模型,使得开发者能够快速上手。同时,es 提供了方便的安装和部署方式,如 Docker 容器、云服务等。
- 强大的生态系统和社区支持:es 拥有庞大的开发者社区和活跃的生态系统,提供了大量的插件和工具,以及广泛的文档和教程。这使得开发者能够轻松地扩展和定制 es。
总的来说,es 具有高性能、实时性、可扩展性和丰富的功能,适用于构建实时搜索、日志分析、监控和大数据分析等应用程序。它已经成为业界最流行的搜索引擎之一,并广泛用于各种大数据和实时数据处理场景。
es 的使用场景
es 目前的使用场景非常广泛,主要用于以下场景:
- 实时搜索和查询:es 能够快速地执行实时搜索和查询操作,适用于构建实时搜索引擎和相关应用,如电商网站的商品搜索、新闻网站的文章搜索等。
- 日志和事件数据分析:es 可以高效地处理大规模的日志和事件数据,支持实时数据分析和监控。它可以用于日志管理平台、运维监控系统、网络安全分析等。
- 大数据分析:es 可以与大数据处理框架(如 Hadoop、Spark)结合使用,进行复杂的数据分析和挖掘。它可以用于构建实时分析仪表盘、数据可视化应用等。
- 地理位置搜索和地理信息系统:es 提供了地理位置搜索的功能,可以根据地理坐标进行搜索和过滤。它可以用于构建位置服务、地理信息系统(GIS)等应用。
- 媒体内容搜索:es支持多媒体内容的搜索和检索,可以用于图片、音频、视频等媒体内容的搜索和标签化。
- 企业级搜索应用:es 提供了丰富的查询和过滤功能,适用于构建企业级搜索应用程序,如企业内部搜索、文档管理系统、内容管理系统等。
- 实时推荐系统:es 可以用于构建实时的个性化推荐系统,根据用户的行为和偏好进行实时推荐。
- 数据存储和索引:es 具有强大的数据存储和索引能力,可以用作主要的数据存储和检索引擎,适用于中小规模的数据应用。
总的来说,es 适用于需要高性能、实时性和可扩展性的搜索和分析场景。它具有丰富的功能和灵活的查询语言,可以满足各种搜索和分析需求。由于其分布式架构和强大的生态系统,es 已成为流行的搜索引擎之一,并被广泛应用于各种大数据和实时数据处理场景。
下载和安装
以下下载都是在windows系统下进行
es 下载
下载地址:https://www.elastic.co/cn/downloads/elasticsearch,这里选择7.8.0版本
下载解压得到目录相关功能:
bin:启动文件
config:配置文件
log4j2.properties:日志配置文件
jvm.options:java虚拟机的配置
elasticsearch.yml:es的配置文件
data:索引数据目录
lib:相关类库Jar包
logs:日志目录
modules:功能模块
plugins:插件
- 下载完成后打开
bin
目录,运行elasticsearch.bat
- 打开浏览器输入
http://localhost:9200/
,出现以下信息说明安装成功
{
"name": "",
"cluster_name": "elasticsearch",
"cluster_uuid": "zeetMv-jRrSQ212GE3cdcg",
"version": {
"number": "7.8.0",
"build_flavor": "default",
"build_type": "zip",
"build_hash": "757314695644ea9a1dc2fecd26d1a43856725e65",
"build_date": "2020-06-14T19:35:50.234439Z",
"build_snapshot": false,
"lucene_version": "8.5.1",
"minimum_wire_compatibility_version": "6.8.0",
"minimum_index_compatibility_version": "6.0.0-beta1"
},
"tagline": "You Know, for Search"
}
注意:9300 端口为 Elasticsearch 集群间组件的通信端口,9200 端口为浏览器访问的 http 协议RESTful端口。
了解 ELK
ELK 是 es、Logstash、Kibana 三大开源框架首字母大写简称。市面上也被成为 Elastic Stack。其中 es 是一个基于Lucene、分布式、通过 Restful 方式进行交互的近实时搜索平台框架。像类似百度、谷歌这种大数据全文搜索引擎的场景都可以使用 es 作为底层支持框架,可见 es 提供的搜索能力确实强大。Logstash 是 ELK 的中央数据流引擎,用于从不同目标(文件/数据存储/MQ)收集的不同格式数据,经过过滤后支持输出到不同目的地(文件/MQ/redis/elasticsearch/kafka等)。Kibana 可以将 es 的数据通过友好的页面展示出来,提供实时分析的功能。
市面上很多开发只要提到 ELK 能够一致说出它是一个日志分析架构技术栈总称,但实际上 ELK 不仅仅适用于日志分析,它还可以支持其它任何数据分析和收集的场景,日志分析和收集只是更具有代表性。并非唯一性。
Kibana 下载
Kibana 是一个针对 es 的开源分析及可视化平台,用来搜索、查看交互存储在 es 索引中的数据。使用 Kibana,可以通过各种图表进行高级数据分析及展示。Kibana 让海量数据更容易理解。它操作简单,基于浏览器的用户界面可以快速创建仪表板(dashboard)实时显示 es 查询动态。设置 Kibana非常简单。无需编码或者额外的基础架构,几分钟内就可以完成 Kibana 安装并启动 es 索引监测。
下载地址:https://www.elastic.co/cn/downloads/kibana
注意:Kibana 的版本一定要与 es 版本相同
- 打开
bin
目录,运行kibana.bat
- 打开浏览器输入
http://localhost:5601/
,出现 kibana 界面说明安装成功
- 界面默认为英文,改成中文:修改
/config/kibana.yml
,在文件最后面添加一行i18n.locale: "zh-CN"
,然后重新启动即可
IK 分词器下载
分词:即把一段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,这显然是不符合要求的,所以我们需要安装 ik 分词器来解决这个问题。
IK提供了两个分词算法:ik_smart 和 ik_max_word,其中 ik_smart 为最少切分,ik_max_word 为最细粒度划分
下载地址:https://github.com/infinilabs/analysis-ik/releases
下载版本与 es 保持一致
- 在下载的 es 的
plugins
目录,新建一个ik
目录,将下载的 ik 解压后的所有文件放入ik
目录中,重启 es
启动时看到这个证明安装成功
elasticsearch-head 下载
注意:需要 NodeJS 的环境
elasticsearch-head 是 es 的集群管理工具,可以用于数据的浏览查询!被托管在github上面!
github地址:https://github.com/mobz/elasticsearch-head
在你想要安装的目录下执行以下命令:
git clone https://github.com/mobz/elasticsearch-head.git
cd elasticsearch-head
npm install
npm run start
执行完成后,访问http://localhost:9100/
由于 es 进程和客户端进程端口号不同,存在跨域问题,所以我们要在 es 的配置文件中配置下跨域问题:
修改 elasticsearch 的 config 目录下的 elasticsearch.yml
,增加两行命令:
# 跨域配置:
http.cors.enabled: true
http.cors.allow-origin: "*"
启动 es,使用 head 工具进行连接测试!
常见问题
- Elasticsearch 是使用 java 开发的,且 7.8 版本的 ES 需要 JDK 版本 1.8 以上,默认安装包带有 jdk 环境,如果系统配置 JAVA_HOME,那么使用系统默认的 JDK,如果没有配置使用自带的 JDK,一般建议使用系统配置的 JDK。
- 双击启动窗口闪退,通过路径访问追踪错误,如果是“空间不足”,请修改 config/jvm.options 配置文件
# 设置 JVM 初始内存为 1G。此值可以设置与-Xmx 相同,以避免每次垃圾回收完成后 JVM 重新分配内存
# Xms represents the initial size of total heap space
# 设置 JVM 最大可用内存为 1G
# Xmx represents the maximum size of total heap space
-Xms1g
-Xmx1g
es 核心概念
es 是面向文档型数据库,一条数据在这里就是一个文档。es 里存储文档数据和关系型数据库 mysq 存储数据的概念进行对比
mysql | es |
---|---|
数据库(database) | 索引(indices) |
表(table) | types |
行(row) | documents |
字段(columns) | fields |
es 里的 index 可以看做一个库,而 types 相当于表,documents 则相当于表的行。这里 types 的概念已经被逐渐弱化,Elasticsearch 6.X 中,一个 index 下已经只能包含一个 type,Elasticsearch 7.X 中, types 的概念已经被删除了。
es集群中可以包含多个索引(数据库),每个索引中可以包含多个类型(表),每个类型下又包含多个文档(行),每个文档中又包含多个字段(列)。
用 JSON 作为文档序列化的格式,比如一条用户信息:
{
"name" : "John",
"sex" : "Male",
"age" : 25,
"birthDate": "1990/05/01",
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
物理设计:
es 在后台把每个索引划分成多个分片,每分分片可以在集群中的不同服务器间迁移
逻辑设计:
一个索引类型中,包含多个文档,比如说文档,文档2。当我们索引一篇文档时,可以通过这样的一各顺序找到 它: 索引 ▷ 类型 ▷ 文档id,通过这个组合我们就能索引到某个具体的文档。 注意:id 不必是整数,实际上它是个字符串。
文档
之前说 es 是面向文档的,那么就意味着索引和搜索数据的最小单位是文档,es 中,文档有几个重要属性:
- 自我包含,一篇文档同时包含字段和对应的值,也就是同时包含 key:value!
- 可以是层次型的,一个文档中包含自文档,复杂的逻辑实体就是这么来的!
- 灵活的结构,文档不依赖预先定义的模式,我们知道关系型数据库中,要提前定义字段才能使用,在 es 中,对于字段是非常灵活的,有时候,我们可以忽略该字段,或者动态的添加一个新的字段。
尽管我们可以随意的新增或者忽略某个字段,但是,每个字段的类型非常重要,比如一个年龄字段类型,可以是字符串也可以是整形。因 es 会保存字段和类型之间的映射及其他的设置。这种映射具体到每个映射的每种类型,这也是为什么在 es 中,类型有时候也称为映射类型。
类型
类型是文档的逻辑容器,就像关系型数据库一样,表格是行的容器。 类型中对于字段的定义称为映射,比如 name 映射为字符串类型。 我们说文档是无模式的,它们不需要拥有映射中所定义的所有字段,比如新增一个字段,那么 es 是怎么做的呢?es 会自动的将新字段加入映射,但是这个字段的不确定它是什么类型,es 就开始猜,如果这个值是18,那么 es 会认为它是整形。 但是 es 也可能猜不对, 所以最安全的方式就是提前定义好所需要的映射,这点跟关系型数据库殊途同归了,先定义好字段,然后再使用,别整什么幺蛾子。
索引
索引是映射类型的容器,es 中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置。 然后它们被存储到了各个分片上了。 我们来研究下分片是如何工作的。
物理设计 :节点和分片如何工作
一个集群至少有一个节点,而一个节点就是一个 es 进程,节点可以有多个索引默认的,如果你创建索引,那么索引将会有个5个分片(primary shard ,又称主分片)构成的,每一个主分片会有一个副本(replica shard,又称复制分片)
上图是一个有3个节点的集群,可以看到主分片和对应的复制分片都不会在同一个节点内,这样有利于某个节点挂掉 了,数据也不至于丢失。 实际上,一个分片是一个 Lucene 索引,一个包含倒排索引的文件目录,倒排索引的结构使得 es 在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字。 不过,等等,倒排索引是什么鬼?
倒排索引
es 使用的是一种称为倒排索引的结构,采用 Lucene 倒排索作为底层。这种结构适用于快速的全文搜索, 一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。 例如,现在有两个文档, 每个文档包含如下内容:
Study every day, good good up to forever # 文档1包含的内容
To forever, study every day, good good up # 文档2包含的内容
为了创建倒排索引,我们首先要将每个文档拆分成独立的词(或称为词条或者 tokens),然后创建一个包含所有不重复的词条的排序列表,然后列出每个词条出现在哪个文档:
现在,我们试图搜索 to forever,只需要查看包含每个词条的文档
两个文档都匹配,但是第一个文档比第二个匹配程度更高。如果没有别的条件,现在,这两个包含关键字的文档都将返回。
再来看一个示例,比如我们通过博客标签来搜索博客文章。那么倒排索引列表就是这样的一个结构:
如果要搜索含有 python 标签的文章,那相对于查找所有原始数据而言,查找倒排索引后的数据将会快的多。只需要查看标签这一栏,然后获取相关的文章 id 即可。
es 的索引和 Lucene 的索引对比
在 es 中, 索引 这个词被频繁使用,这就是术语的使用。 在 es 中,索引被分为多个分片,每份分片是一个 Lucene 的索引。所以一个 es 索引是由多个 Lucene 索引组成的。别问为什么,谁让 es 使用 Lucene 作为底层呢! 如无特指,说起索引都是指 es 的索引。
es 基本操作
一切操作都在 kibana 中 Dev Tools 下的 Console 里完成
restful 风格
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
基本 restful 命令说明:
索引操作 index
创建索引
put 方法创建索引
使用 put 创建索引时必须指明文档id,否则报错
# PUT 创建命令
# test1 索引名称
# type1 类型名称,默认为_doc,已经被废弃
# 1 文档id
PUT /test1/type1/1
{
"name":"zhangsan",
"age":18,
"birth":"2000-01-20"
}
结果:
#! Deprecation: [types removal] Specifying types in document index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, or /{index}/_create/{id}).
{
"_index" : "test1", // 索引
"_type" : "type1", // 类型
"_id" : "1", // id
"_version" : 1, // 版本
"result" : "created", // 操作类型
"_shards" : { // 分片信息
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
post 方法创建索引
post 如果没有指明文档id,会随机生成一个
POST /test2/type2
{
"name":"zhangsan",
"age":18,
"birth":"2000-01-20"
}
结果:
#! Deprecation: [types removal] Specifying types in document index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, or /{index}/_create/{id}).
{
"_index" : "test2",
"_type" : "type2",
"_id" : "3D_WQY4BOf0ywiICmI8O",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
对比 mysql :
PUT test1/type1/1 : 索引test1相当于关系型数据库的库,类型type1就相当于表 ,1 代表数据中的主键 id
这里需要补充的是 ,在 es5 版本前,一个索引下可以创建多个类型,但是在之后,一个索引只能对应一个类型,默认为 _doc,而 id 相当于关系型数据库的主键id若果不指定就会默认生成一个20位的uuid,属性相当关系型数据库的column(列)。
而结果中的 result 则是操作类型,现在是 created ,表示第一次创建。如果再次点击执行该命令那么 result 则会是 updated ,我们细心则会发现 _version 开始是1,现在你每点击一次就会增加一次。表示第几次更改。
查看索引信息
# get 索引名称
GET test1
{
"test1" : {
"aliases" : { },
"mappings" : {
"properties" : {
"age" : {
"type" : "long"
},
"birth" : {
"type" : "date"
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
},
"settings" : {
"index" : {
"creation_date" : "1710501416895",
"number_of_shards" : "1",
"number_of_replicas" : "1",
"uuid" : "hemDp4F3T5ePsAZmaO5Ijg",
"version" : {
"created" : "7080099"
},
"provided_name" : "test1"
}
}
}
}
可以看到 name、age、birth 字段指定了类型 text、long、date ,说明 es 会根据字段的值指定默认的类型
指定字段类型
如果想要自己指定字段的类型,使用映射
PUT /test3
{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "long"
},
"birth":{
"type": "date"
}
}
}
}
结果:
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "test3"
}
映射数据是字段名:json 数据,上面只指定了type
(数据类型),其实可以指定很多属性
- type:数据类型,es 中支持的数据类型非常丰富,主要使用以下几个:
- string 类型,又分两种:
- text:可分词
- keyword:不可分词,数据会作为完整字段进行匹配
- Numerical:数值类型,分两类:
- 基本数据类型:long、integer、short、byte、double、float、half_float
- 浮点数的高精度类型:scaled_float
- Date:日期类型
- Array:数组类型
- Object:对象
- string 类型,又分两种:
- index:是否索引,默认为 true,所有字段都会被索引,被索引则可以用来搜索,没有则不可以
- store:是否将数据进行独立存储,默认为 false,原始的文本会存储在 _source 里面,默认情况下其他提取出来的字段都不是独立存储的,是从 _source 里面提取出来的。当然你也可以独立的存储某个字段,只要设置 “store”: true 即可,获取独立存储的字段要比从 _source 中解析快得多,但是也会占用更多的空间,所以要根据实际业务需求来设置。
- analyzer:分词器,这里的 ik_max_word 即使用 ik 分词器
查看索引映射
# get /索引名称/_mapping
GET /test3/_mapping
结果:
{
"test3" : {
"mappings" : {
"properties" : {
"age" : {
"type" : "long"
},
"birth" : {
"type" : "date"
},
"name" : {
"type" : "text"
}
}
}
}
}
查看索引健康情况
GET _cat/indices?v
可以查看我们所有索引的状态健康情况,分片,数据储存大小等等。
删除索引
# delete 索引名称
DELETE test3
结果:
{
"acknowledged" : true
}
文档操作 documents
创建数据(put)
向 user 索引下创建3条数据
PUT /user/_doc/1
{
"name":"zhangsan",
"age":18,
"sex":"男",
"info":"一顿操作猛如虎,一看工资2500",
"tags":["计算机","运动","动漫"]
}
PUT /user/_doc/2
{
"name":"kunkun",
"age":3,
"sex":"男",
"info":"吉你实在实在太美",
"tags":["唱","跳","篮球"]
}
PUT /user/_doc/3
{
"name":"lisi",
"age":66,
"sex":"女",
"info":"清晨下的第一杯水",
"tags":["a","b","c"]
}
当执行命令时,如果数据不存在,则新增该条数据,如果数据存在则修改该条数据。
获取数据(get)
# get 索引名/类型名/id
GET /user/_doc/1
结果:
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_version" : 2,
"_seq_no" : 3,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
}
更新数据(update)
覆盖更新(put)
PUT /user/_doc/1
{
# 更新的数据
"name":"wangwu"
}
结果:
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_version" : 2, // 代表数据更改的次数
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 6,
"_primary_term" : 1
}
从结果中可以看到,我们更新的数据并不是更改了指定的字段,而是直接覆盖掉了原来的数据,这不符合我们的一般习惯,如果想要更新指定的字段,需要使用 post + _update 方式来更新
局部更新(post)
使用 post 命令,在 id 后面跟 _update,要修改的内容放到 doc 文档中即可。
POST /user/_doc/3/_update
{
"doc":{
"name":"zhangsan"
}
}
结果:
#! Deprecation: [types removal] Specifying types in document update requests is deprecated, use the endpoint /{index}/_update/{id} instead.
{
"_index" : "user",
"_type" : "_doc",
"_id" : "3",
"_version" : 2,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 7,
"_primary_term" : 1
}
条件查询
使用 GET 命令,后加上_search?q=要查询的条件
# get /索引名/文档名/_search查询条件
GET /user/_doc/_search?q=name:zhangsan
结果:
#! Deprecation: [types removal] Specifying types in search requests is deprecated.
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.9808291,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.9808291,
"_source" : {
"name" : "zhangsan",
"age" : 66,
"sex" : "女",
"info" : "清晨下的第一杯水",
"tags" : [
"a",
"b",
"c"
]
}
}
]
}
}
我们看一下结果 返回并不是 数据本身,是给我们了一个 hits ,还有 _score 得分,就是根据算法算出和查询条件匹配度高得分就搞。
这里的查询是模糊查询,并会根据 ik 分词器进行匹配,但由于我们查询的字段name
的类型是keyword
(不可分词),故必须要精确匹配才能查询到
复杂条件查询
构建查询
重新添加5条数据
通过 match 来构建查询条件,返回结果与上述一致
GET /user/_doc/_search
{
"query":{
"match": {
"name": "zhangsan"
}
}
}
结果:
#! Deprecation: [types removal] Specifying types in search requests is deprecated.
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.3862942,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.3862942,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
}
]
}
}
同样是模糊查询
GET /user/_doc/_search
{
"query":{
"match": {
"info": "一顿操作"
}
}
}
结果:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 1.9004039,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.9004039,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.9004039,
"_source" : {
"name" : "wei",
"age" : 18,
"sex" : "女",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.9004039,
"_source" : {
"name" : "zhangsan1111",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.3176721,
"_source" : {
"name" : "lisi",
"age" : 66,
"sex" : "女",
"info" : "清晨下的第一杯水",
"tags" : [
"a",
"b",
"c"
]
}
}
]
}
}
这里我们可以看到返回了4条数据,"info" : "清晨下的第一杯水"
也被查询了出来,其实是因为我们使用了 ik 分词器,根据“一顿操作”中的“一”匹配到了这条数据
一般的,我们推荐使用构建查询,以后在与程序交互时的查询等也是使用构建查询方式处理查询条件,因为该方式可以构建更加复杂的查询条件,也更加一目了然
查询全部
GET /user/_doc/_search
{
"query":{
"match_all":{}
}
}
结果:
#! Deprecation: [types removal] Specifying types in search requests is deprecated.
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"name" : "kunkun",
"age" : 3,
"sex" : "男",
"info" : "吉你实在实在太美",
"tags" : [
"唱",
"跳",
"篮球"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"name" : "lisi",
"age" : 66,
"sex" : "女",
"info" : "清晨下的第一杯水",
"tags" : [
"a",
"b",
"c"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.0,
"_source" : {
"name" : "wei",
"age" : 18,
"sex" : "女",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan1111",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
}
]
}
}
match_all 的值为空,表示没有查询条件,就像 select * from table_name 一样。
多字段匹配查询
multi_match 与 match 类似,不同的是它可以在多个字段中查询。
GET /user/_search
{
"query": {
"multi_match": {
"query": "zhangsan",
"fields": ["name","info"]
}
}
}
结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.3862942,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.3862942,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
}
]
}
}
再查询“一顿操作”
GET /user/_search
{
"query": {
"multi_match": {
"query": "一顿操作",
"fields": ["name","info"]
}
}
}
结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 1.9004039,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.9004039,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.9004039,
"_source" : {
"name" : "wei",
"age" : 18,
"sex" : "女",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.9004039,
"_source" : {
"name" : "zhangsan1111",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.3176721,
"_source" : {
"name" : "lisi",
"age" : 66,
"sex" : "女",
"info" : "清晨下的第一杯水",
"tags" : [
"a",
"b",
"c"
]
}
}
]
}
}
可以看到,只要有一个字段的值匹配成功就都可以查询出来
指定返回字段
GET /user/_doc/_search
{
"query":{
"match": {
"info": "一顿操作"
}
},
"_source": ["info","name"]
}
结果:
#! Deprecation: [types removal] Specifying types in search requests is deprecated.
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 1.9004039,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.9004039,
"_source" : {
"name" : "zhangsan",
"info" : "一顿操作猛如虎,一看工资2500"
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.9004039,
"_source" : {
"name" : "wei",
"info" : "一顿操作猛如虎,一看工资2500"
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.9004039,
"_source" : {
"name" : "zhangsan1111",
"info" : "一顿操作猛如虎,一看工资2500"
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.3176721,
"_source" : {
"name" : "lisi",
"info" : "清晨下的第一杯水"
}
}
]
}
}
排序查询
在sort
指定某个字段的排序规则,desc 是降序,asc 是升序
GET /user/_doc/_search
{
"query":{
"match": {
"info": "一顿操作"
}
},
"sort":{
"age":{
"order":"desc"
}
},
"_source": ["info","name","age"]
}
结果:
#! Deprecation: [types removal] Specifying types in search requests is deprecated.
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "3",
"_score" : null,
"_source" : {
"name" : "lisi",
"age" : 66,
"info" : "清晨下的第一杯水"
},
"sort" : [
66
]
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : null,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"info" : "一顿操作猛如虎,一看工资2500"
},
"sort" : [
18
]
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "5",
"_score" : null,
"_source" : {
"name" : "wei",
"age" : 18,
"info" : "一顿操作猛如虎,一看工资2500"
},
"sort" : [
18
]
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "4",
"_score" : null,
"_source" : {
"name" : "zhangsan1111",
"age" : 18,
"info" : "一顿操作猛如虎,一看工资2500"
},
"sort" : [
18
]
}
]
}
}
注意:在排序的过程中,只能使用可排序的属性进行排序。那么可以排序的属性有哪些呢?
- 数字
- 日期
- ID
其他都不行!
分页查询
from
是起始数据的下标,0代表从第一条数据开始,size
是每页有多少条数据
GET /user/_doc/_search
{
"query":{
"match": {
"info": "一顿操作"
}
},
"sort":{
"age":{
"order":"desc"
}
},
"_source": ["info","name","age"],
"from":0,
"size":2
}
结果:
#! Deprecation: [types removal] Specifying types in search requests is deprecated.
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "3",
"_score" : null,
"_source" : {
"name" : "lisi",
"age" : 66,
"info" : "清晨下的第一杯水"
},
"sort" : [
66
]
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : null,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"info" : "一顿操作猛如虎,一看工资2500"
},
"sort" : [
18
]
}
]
}
}
布尔查询
must 查询
must 相当于 mysql 中的 and,根据多个条件进行查询
GET /user/_doc/_search
{
"query":{
"bool":{
"must":[
{
"match":{
"name":"zhangsan"
}
},
{
"match":{
"age":18
}
}
]
}
}
}
结果:
#! Deprecation: [types removal] Specifying types in search requests is deprecated.
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 2.3862944,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : 2.3862944,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
}
]
}
}
should 查询
相当于 mysql 中的 or,只需要满足其中一个条件即可
GET /user/_doc/_search
{
"query":{
"bool":{
"should":[
{
"match":{
"name":"kunkun"
}
},
{
"match":{
"age":18
}
}
]
}
}
}
结果:
#! Deprecation: [types removal] Specifying types in search requests is deprecated.
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 1.3862942,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.3862942,
"_source" : {
"name" : "kunkun",
"age" : 3,
"sex" : "男",
"info" : "吉你实在实在太美",
"tags" : [
"唱",
"跳",
"篮球"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.0,
"_source" : {
"name" : "wei",
"age" : 18,
"sex" : "女",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.0,
"_source" : {
"name" : "zhangsan1111",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
}
]
}
}
must_not 查询
查询出不满足条件的所有数据
GET /user/_doc/_search
{
"query":{
"bool":{
"must_not":[
{
"match":{
"age":18
}
}
]
}
}
}
结果:
#! Deprecation: [types removal] Specifying types in search requests is deprecated.
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 0.0,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.0,
"_source" : {
"name" : "kunkun",
"age" : 3,
"sex" : "男",
"info" : "吉你实在实在太美",
"tags" : [
"唱",
"跳",
"篮球"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.0,
"_source" : {
"name" : "lisi",
"age" : 66,
"sex" : "女",
"info" : "清晨下的第一杯水",
"tags" : [
"a",
"b",
"c"
]
}
}
]
}
}
filter 查询
条件过滤查询,示例:查询出年龄大于20的数据
GET /user/_doc/_search
{
"query":{
"bool":{
"filter":{
"range":{
"age":{
"gt":20
}
}
}
}
}
}
结果:
#! Deprecation: [types removal] Specifying types in search requests is deprecated.
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.0,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.0,
"_source" : {
"name" : "lisi",
"age" : 66,
"sex" : "女",
"info" : "清晨下的第一杯水",
"tags" : [
"a",
"b",
"c"
]
}
}
]
}
}
-
gt 表示大于
-
gte 表示大于等于
-
lt 表示小于
-
lte 表示小于等于
如果想要查询出10~20之间的数据:
GET /user/_doc/_search
{
"query":{
"bool":{
"filter":{
"range":{
"age":{
"gt":10,
"lt":20
}
}
}
}
}
}
结果:
#! Deprecation: [types removal] Specifying types in search requests is deprecated.
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 0.0,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.0,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "5",
"_score" : 0.0,
"_source" : {
"name" : "wei",
"age" : 18,
"sex" : "女",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "4",
"_score" : 0.0,
"_source" : {
"name" : "zhangsan1111",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
}
]
}
}
精确查询 term
term
查询是直接通过倒排索引指定的词条,也就是精确查找。
term 与 match 区别:
- match 是经过分析(analyer)的,也就是说,文档是先被分析器处理了,根据不同的分析器,分析出的结果也会不同,在会根据分词结果进行匹配。
- term 是不经过分词的,直接去倒排索引查找精确的值。
这里我们重新定义一个索引,并添加两条数据:
PUT user1
{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"info":{
"type": "keyword"
}
}
}
}
PUT /user1/_doc/1
{
"name":"张三三",
"info":"一顿操作猛如虎,一看工资2500"
}
PUT /user1/_doc/2
{
"name":"李四四",
"info":"清晨的第一杯水"
}
其中,name 类型是 text,info 类型是 keyword
接下来我们查询 name 为张的数据
GET /user1/_search
{
"query": {
"term": {
"name": "张"
}
}
}
结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.8547784,
"hits" : [
{
"_index" : "user1",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.8547784,
"_source" : {
"name" : "张三三",
"info" : "一顿操作猛如虎,一看工资2500"
}
}
]
}
}
能够查询到 name 为张三三的数据
接下来我们查询 name 为张三三的数据
GET /user1/_search
{
"query": {
"term": {
"name": "张三三"
}
}
}
结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}
没有查询出任何数据,这是因为 name 的类型是 text,我们的 ik分词器对张三三进行了分词拆分,而 term 查询的条件张三三不可拆分,故查询不到
接下来查询 info 为“一”的数据
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}
结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}
查询不到任何数据
查询 info 为“一顿操作猛如虎,一看工资2500”的数据
GET /user1/_search
{
"query": {
"term": {
"info": "一顿操作猛如虎,一看工资2500"
}
}
}
结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.6931471,
"hits" : [
{
"_index" : "user1",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.6931471,
"_source" : {
"name" : "张三三",
"info" : "一顿操作猛如虎,一看工资2500"
}
}
]
}
}
结果可以查询到
所以 term 通常用来查询类型为 keyword 的字段
多关键字精确查询
terms 查询和 term 查询一样,但它允许你指定多值进行匹配。
如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件,类似于 mysql 的 in
GET /user1/_search
{
"query": {
"terms": {
"name": ["张","李"]
}
}
}
结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "user1",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "张三三",
"info" : "一顿操作猛如虎,一看工资2500"
}
},
{
"_index" : "user1",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"name" : "李四四",
"info" : "清晨的第一杯水"
}
}
]
}
}
fuzzy 查询
fuzzy 查询返回包含与搜索字词相似的字词的文档。
编辑距离是将一个术语转换为另一个术语所需的一个字符更改的次数。这些更改可以包括:
- 更改字符(box → fox)
- 删除字符(black → lack)
- 插入字符(sic → sick)
- 转置两个相邻字符(act → cat)
为了找到相似的术语,fuzzy 查询会在指定的编辑距离内创建一组搜索词的所有可能的变体或扩展。然后查询返回每个扩展的完全匹配。
通过 fuzziness 修改编辑距离。一般使用默认值 AUTO,根据术语的长度生成编辑距离。
GET /user/_search
{
"query": {
"fuzzy": {
"name": "zhangsan1"
}
}
}
结果:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.2130076,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.2130076,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
}
]
}
}
指定 fuzziness 编辑距离
GET /user/_search
{
"query": {
"fuzzy": {
"name": {
"value": "zhangsan11",
"fuzziness": 2
}
}
}
}
结果:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.1090355,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.1090355,
"_source" : {
"name" : "zhangsan1111",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0397208,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
}
}
]
}
}
GET /user/_search
{
"query": {
"fuzzy": {
"name": {
"value": "zhangsan11",
"fuzziness": 1
}
}
}
}
结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}
聚合查询
聚合允许使用者对 es 文档进行统计分析,类似与关系型数据库中的 group by,当然还有很多其他的聚合,例如取最大值、平均值等等。
对某个字段取最大值
GET /user/_search
{
"aggs": {
"max_age": { # 自定义名字
"max": {
"field": "age"
}
}
},
"size": 0
}
结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"max_age" : {
"value" : 66.0
}
}
}
可以看到aggregations
中最大值为66.0
取总和
GET /user/_search
{
"aggs": {
"sum_age": {
"sum": {
"field": "age"
}
}
},
"size": 0
}
结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"sum_age" : {
"value" : 123.0
}
}
}
年龄总和为123.0
取平均值
GET /user/_search
{
"aggs": {
"avg_age": {
"avg": {
"field": "age"
}
}
},
"size": 0
}
结果:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"avg_age" : {
"value" : 24.6
}
}
}
年龄平均值为24.6
对某个字段的值进行去重之后再取个数
GET /user/_search
{
"aggs": {
"distinct_age":{
"cardinality": {
"field": "age"
}
}
},
"size": 0
}
结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"distinct_age" : {
"value" : 3
}
}
}
去重后,数据个数为3个
State 聚合
stats 聚合,对某个字段一次性返回 count,max,min,avg 和 sum 五个指标
GET /user/_search
{
"aggs": {
"state_age":{
"stats": {
"field": "age"
}
}
},
"size": 0
}
结果:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"state_age" : {
"count" : 5,
"min" : 3.0,
"max" : 66.0,
"avg" : 24.6,
"sum" : 123.0
}
}
}
高亮查询
使用highlight
根据查询条件,指定你要高亮的字段
GET /user/_search
{
"query": {
"match": {
"info": "一顿操作"
}
},
"highlight": {
"fields": {
"info": {}
}
}
}
结果:
{
"took" : 43,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 1.9004039,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.9004039,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
},
"highlight" : {
"info" : [
"<em>一</em><em>顿</em><em>操</em><em>作</em>猛如虎,<em>一</em>看工资2500"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.9004039,
"_source" : {
"name" : "wei",
"age" : 18,
"sex" : "女",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
},
"highlight" : {
"info" : [
"<em>一</em><em>顿</em><em>操</em><em>作</em>猛如虎,<em>一</em>看工资2500"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.9004039,
"_source" : {
"name" : "zhangsan1111",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
},
"highlight" : {
"info" : [
"<em>一</em><em>顿</em><em>操</em><em>作</em>猛如虎,<em>一</em>看工资2500"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.3176721,
"_source" : {
"name" : "lisi",
"age" : 66,
"sex" : "女",
"info" : "清晨下的第一杯水",
"tags" : [
"a",
"b",
"c"
]
},
"highlight" : {
"info" : [
"清晨下的第<em>一</em>杯水"
]
}
}
]
}
}
可以看到查询条件“一顿操作”的每个字(分词结果)都进行了高亮处理,默认标签是<em>
自定义高亮样式
使用pre_tags
定义前缀标签,post_tags
定义后缀标签
GET /user/_search
{
"query": {
"match": {
"info": "一顿操作"
}
},
"highlight": {
"pre_tags": "<b class='key' style='color:red'>",
"post_tags": "</b>",
"fields": {
"info": {}
}
}
}
结果:
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 1.9004039,
"hits" : [
{
"_index" : "user",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.9004039,
"_source" : {
"name" : "zhangsan",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
},
"highlight" : {
"info" : [
"<b class='key' style='color:red'>一</b><b class='key' style='color:red'>顿</b><b class='key' style='color:red'>操</b><b class='key' style='color:red'>作</b>猛如虎,<b class='key' style='color:red'>一</b>看工资2500"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.9004039,
"_source" : {
"name" : "wei",
"age" : 18,
"sex" : "女",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
},
"highlight" : {
"info" : [
"<b class='key' style='color:red'>一</b><b class='key' style='color:red'>顿</b><b class='key' style='color:red'>操</b><b class='key' style='color:red'>作</b>猛如虎,<b class='key' style='color:red'>一</b>看工资2500"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.9004039,
"_source" : {
"name" : "zhangsan1111",
"age" : 18,
"sex" : "男",
"info" : "一顿操作猛如虎,一看工资2500",
"tags" : [
"计算机",
"运动",
"动漫"
]
},
"highlight" : {
"info" : [
"<b class='key' style='color:red'>一</b><b class='key' style='color:red'>顿</b><b class='key' style='color:red'>操</b><b class='key' style='color:red'>作</b>猛如虎,<b class='key' style='color:red'>一</b>看工资2500"
]
}
},
{
"_index" : "user",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.3176721,
"_source" : {
"name" : "lisi",
"age" : 66,
"sex" : "女",
"info" : "清晨下的第一杯水",
"tags" : [
"a",
"b",
"c"
]
},
"highlight" : {
"info" : [
"清晨下的第<b class='key' style='color:red'>一</b>杯水"
]
}
}
]
}
}
需要注意的是:自定义标签中属性或样式中的逗号一律用英文状态的单引号表示,应该与外部 es 语法的双引号区分开。
说明:Deprecation
注意 es 在第一个版本的开始每个文档都储存在一个索引中,并分配一个 映射类型,映射类型用于表示被索引的文档或者实体的类型,这样带来了一些问题, 导致后来在 es 6.0.0 版本中一个文档只能包含一个映射类型,而在 7.0.0 中,映射类型则将被弃用,到了 8.0.0 中则将完全被删除。
只要记得,一个索引下面只能创建一个类型就行了,其中各字段都具有唯一性,如果在创建映射的时候,如果没有指定文档类型,那么该索引的默认索引类型是 _doc ,不指定文档id 则会内部帮我们生成一个 id字符串。