目录
1. Elasticsearch的数据类型 2
1.1. 理解index、type、文档 2
1.2. 为了加深理解index、type、文档概念,下面列出了常用的ES URL概览 2
1.3. 向ES中存储数据 3
1.4. ES数据格式 3
1.5. ES数据类型映射 4
1.6. ES实际是如何存储复杂对象的? 8
2. ES查询 9
2.1. ES简单查询例子 9
2.2. 查询方式的分类 10
2.2.1. 概念解释: 10
2.2.2. Query和Filter(bool查询)的区别 10
2.3. ES Query查询语法 10
2.4. Bool(filter)查询语法 12
2.4.1. BooleanQuery查询例子 12
2.4.2. BooleanQuery查询语法 14
2.4.3. BooleanQuery例子解析 14
3. 结束语
- Elasticsearch的数据类型
1.1理解index、type、文档
站在使用者的角度考虑的话,index和type仅仅是url路径中的一个值。站在开发者的角度讲,index就相当于表,type就相当于表的一个字段,定义了这些数据的类型。文档则通常为JSON格式表示的数据。
补充说明:ES 7.X版本为什么已经移除了Type的概念?
1)ES中同一个index中不同type是存储在同一个索引中的(lucene的索引文件),因此不同type中相同名字的字段的定义(mapping)必须一致;
2)外在同一个index中存储含有不同字段的文档会导致稀疏数据并干扰Lucene高效压缩文档的能力,降低es效率。
1.2为了加深理解index、type、文档概念,下面列出了常用的ES URL
| 大类 | 小类 | url规则 | 请求方式 | Body体 |
| 索引操作 | 创建索引 | http://ip:port/{索引名} | Put | 在请求体里面传入设置或类型映射(可选,可设置主分片数、副本数、分词器、字段类型等) {"settings": {},"mappings": {}} |
| 删除索引 | http://ip:port/{索引名} | Delete | 无 | |
| 查询索引列表 | http://ip:port/_cat/indices?v | |||
| 文档管理 | 添加 | http://ip:port/{索引}/{type}/{_id} | Put | json形式的文档 |
| 修改 | http://ip:port/{索引}/{type}/{_id} | Post | json形式的文档 | |
| 删除 | http://ip:port/{索引}/{type}/{_id} | Delete | 无 | |
| 根据_id取文档 | http://ip:port/{索引}/{type}/{_id} | Get | 无 | |
| 使用批处理_bulk | http://ip:port/{索引}/_bulk | PutPost | 固定格式的消息体或文件 | |
| 查询数据 | 查询 | http://ip:port/{索引}/{type}/_search | Post | json形式的查询语句{"query":{查询体}} |
| 查询全部match_all | http://ip:port/{索引}/_search | Post | {"query":{"match_all":{}}} | |
| 其他查询 | 查询的url规则都一样,不同的是body体,后面会详细介绍。 | Post |
1.3 通过http请求,向ES中存储数据
向ES中存储数据时,传入的json如下:
PUT my-index-000001/_doc/1
{
"age": 79,
"name": {
"full": "John Smith",
"first": "John",
"last": "Smith"
},
"address": "35 Chauncey Street, Grazierville, Missouri",
"createdAt": "2021-03-16",
"updatedAt": "22021-03-16 01:16:30"
}
通过向ES中存储数据,大家思考下ES拿到数据之后,如何处理?
字段的识别数据类型转换根据字段类型进行分词处理并索引。
1.4 ES数据格式
ES的数据格式基本有如下几种类型:
字符串: string数值: double、long等布尔型: BooleanObject:对应上面的user、user.name为Object类型数组日期类型:ES日期类型可以有3种表示方式:1)格式化的date字符串,例如"2018-01-01"或者"2018-01-01 12:00:00" 。 2)一个long型的数字,代表从1970年1月1号0点到现在的毫秒数。 3)一个integer型的数字,代表从1970年1月1号0点到现在的秒数。
ES复杂数据类型:
binary: double、long等keyword: Boolean等等
思考:
1)json包含哪些数据类型?ES包含哪些数据类型?ES会如何存储JSON数据呢?
备注:在es内部,date被转为UTC,并被存储为一个长整型数字,代表从1970年1月1号0点到现在的毫秒数;date类型字段上的查询会在内部被转为对long型值的范围查询,查询的结果类型是字符串。
1.5 ES数据类型映射
上述JSON存储到ES后,会映射为如下格式:
{
"account": {
"mappings": {
"_doc": {
"properties": {
"address": {"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},
"age": {"type":"long"},
"createdAt": {"type":"date"},
"name": {"properties":{"first":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"full":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"last":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}}}},
"updatedAt": {"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}}
}
}
}
}
}
1.6 ES实际是如何存储复杂对象的?
实际上,针对上面类型映射,在ES底层实际映射为简单的健值对,如下:
{
"age": 79,
"name.full":"John Smith",
"name.first":"John",
"name.last":"Smith",
"address": "35 Chauncey Street, Grazierville, Missouri",
"createdAt": "2021-03-16",
"updatedAt": "22021-03-16 01:16:30"
}
对于对象数组:
{
"followers": [
{ "age": 35, "name": "Mary White"},
{ "age": 26, "name": "Alex Jones"},
{ "age": 19, "name": "Lisa Smith"}
]
}
会变成
{
"followers.age": [19, 26, 35],
"followers.name": [alex, jones, lisa, smith, mary, white]
}
{age: 35} 和 {name: Mary White} 之间的相关性已经丢失了,所以在查询操作的时候,我们不能得到一个准确的答案:“是否有一个26岁名字叫 Alex Jones 的追随者?”。
2. ES查询介绍
介绍了ES的存储数据类型,下面给大家介绍下ES的查询,下面先看一个查询的例子。
2.1 ES简单查询例子
post /account/_doc/_search?pretty
{
"query": {
"match": {
"address":"Gallatin Place"
}
},
"from": 0,
"size": 10
}
上面仅仅是一个最简单的查询,实际上ES可支持很多类型的查询操作,下面先介绍下ES的查询分哪两类。
2.1查询方式的分类
ES查询方式分为query、filter(bool查询)两类。这两种查询有什么区别呢?下面先看下两者个概念解释。
2.2.1 概念解释:
Query:在查询上下文中,查询子句回答“此文档与此查询子句匹配程度如何?”的问题。除了决定文档是否匹配外,查询子句还会在 _score 元字段中计算相关性分数。
Filter:在过滤器上下文中,查询子句回答“此文档是否与此查询子句匹配?”的问题。答案是简单的 Yes 或 No —— 不计算分数。Filter主要对文档进行过滤,例如 这个时间戳是否在 2015 年到 2016 年的范围内? 状态字段是否设置为“已发布”?
2.2.2 Query和Filter(bool查询)的区别
1)使用方面:
filter仅仅只是按照搜索条件过滤出需要的数据而已,不计算任何相关度分数,对相关度没有任何影响;
query会去计算每个document相对于搜索条件的相关度,并按照相关度进行排序。
一般来说,如果你是在进行搜索,需要将最匹配搜索条件的数据先返回,那么用query;如果你只是要根据一些条件筛选出一部分数据,不关注其排序,那么用filter。
2)性能方面:
filter不计算相关度分数,不按照相关度分数进行排序,同时还有内置的自动cache最常使用filter的数据;
query相反,要计算相关度分数,按照分数进行排序,而且无法cache结果。
2.3 ES Query查询语法
ES的查询根据是否进行分词,分为“Full text query”和“Term-level query”。
Full text query:对文字进行分词之后,再进行查询。例如查询“hello world”的时候,会通过空格分为“hello”和“world”两个词之后,再进行查询。
Term-level query:查询不会再对query内容进行analysis分词处理,直接将query当做一个单词处理。例如:例如查询“hello world”的时候,仅匹配“hello world”。
| 大类 | 小类 | 解释 | 例子 |
| 分词匹配检索 | match_all | ||
| match | 对内容分词之后,查询ES进行匹配 | {"query": {"match": {"address":"Gallatin Place"}}} | |
| query_string | 分词后进行匹配,支持AND、OR等DSL语言。不指定fields,默认会在当前index所有可以查询的字段中进行查询。 | {"query": {"query_string": {"query":"Gallatin AND Place","fields":["address"]}}} | |
| match_phrase | 查询短语 | {"query": {"match_phrase": {"address":{"query":"33 Street","slop": 1}}}} | |
| combined_fields | 查询多个字段 | {"query": {"combined_fields" : {"query":"database systems","fields":[ "title", "abstract", "body"],"operator": "and"}}} | |
| 精确匹配查询 | exists | {"query": {"exists": {"field": "user"}} | |
| Fuzzy | 模糊匹配 | {"query": {"fuzzy": {"address": {"value": "roa"}}}} | |
| Ids | 查询id | {"query": {"ids" : {"values" : ["1", "4", "100"]}}} | |
| prefix前缀查询 | 前缀查询 | {"query": {"prefix": {"name": {"value": "sc"}}}} | |
| range | range范围查询 | {"query": {"range": {"age": {"gte": 10, "lte": 20, "boost": 2.0}}}} | |
| term查询(不进行分词) | 适合对价格、商品ID、用户名查询 | {"query": {"term": {"address": {"value": "street", "boost": 1.0} }}} | |
| wildcard通配符查询 | 通配符查询 | { "query": { "wildcard": { "company": "inv*re" } } } |
通过上面的例子可以看出,ES查询的基本语法为如下格式的JSON:
{
"query": {
"查询操作类型(match、query_string等)": {
}
}
}
2.4 Bool(filter)查询语法
bool查询映射到Lucene的BooleanQuery查询,用来查询与boolean条件匹配的文档;它由一个或者多个boolean子句组成。下面看下BooleanQuery查询例子。
2.4.1 BooleanQuery查询例子
POST _search
{
"query": {
"bool" : {
"must" : {
"term" : { "user.id" : "kimchy" }
},
"filter": {
"term" : { "tags" : "production" }
},
"must_not" : {
"range" : {
"age" : { "gte" : 10, "lte" : 20 }
}
},
"should" : [
{ "term" : { "tags" : "env1" } },
{ "term" : { "tags" : "deployed" } }
],
"minimum_should_match" : 1,
"boost" : 1.0
}
}
}
2.4.2 BooleanQuery查询语法
通过上面的例子可以看出,bool元素包含must、must_not、filter、should四个操作符,操作符中又可以包含查询条件(可以包含match、term、range等等查询语法,又可包含bool查询子句)。
| Boolean查询条件操作符 | 解释 | 上述例子解析 |
| must | 文档必须匹配must中的条件,并计算分值。 must里的条件必须全部为true才能返回。 | 查询user.id匹配kimchy的记录。 |
| filter | 必须匹配,但对返回的score无影响,只是根据过滤标准来排除或包含文档。(不计分) | 返回所有文档的tags字段包含production内容的记录。 |
| should | should则是包含的条件里有一个条件为true就返回,如果满足这些语句中的任意语句,将增加 _score的分值。可以通过设置minimum_should_match的值(数值或者百分比),来设置需要满足should中的几个条件。 *当bool查询包含should,不包含must或者filter的时候,minimum_should_match的默认值为1,否则为0。 | 文档的tags字段为env1或者deployed |
| must_not | 文档 必须不 匹配这些条件才能被包含进来。 | 年龄不在10-20岁之间 |
2.4.3 BooleanQuery例子解析
1)下面的查询用于查找 title 字段匹配 how to make millions 并且不被标识为 spam 的文档。那些被标识为 starred 或在2014之后的文档,将比另外那些文档拥有更高的排名,如果 两者 都满足,那么它排名将更高。
{"query": {
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }},
{ "range": { "date": { "gte": "2014-01-01" }}}
]
}}}
2)带filter的查询
{"query": {
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }}
],
"filter": {
"range": { "date": { "gte": "2014-01-01" }}
}
}}}
range 查询移到 filter 语句中,date的内容将不再影响文档的score分值。
3)bool嵌套
{"query": {
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }}
],
"filter": {
"bool": {
"must": [
{ "range": { "date": { "gte": "2014-01-01" }}},
{ "range": { "price": { "lte": 29.99 }}}
],
"must_not": [
{ "term": { "category": "ebooks" }}
]
}}}}}
结束语
上面为面向入门者写的简单的ES入门手册,掌握了上述内容之后,应付正常工作和使用已经绰绰有余了。
需要了解更多ES内容的同学可以移步官网(Quick start | Elasticsearch Guide [8.1] | Elastic),对于英文阅读困难的同学可访问中文网站(基础入门 | Elasticsearch: 权威指南 | Elastic),但中文网站的内容比较老旧,是基于 Elasticsearch 2.x 版本编写。
如果对您有用,麻烦点赞!



