写在前面
ES 的查询语言称为 DSL 即 Domain Specific Language 领域专用语言。
本文查询将以下列索引结构为例子展开。
PUT /dying_gq_bookstore
{
"mappings": {
"properties": {
"book_id": {
"type": "long"
},
"title": {
"type": "text"
},
"tag": {
"type": "keyword"
},
"book_type": {
"type": "keyword"
},
"content": {
"type": "text"
},
"status": {
"type": "keyword"
},
"price": {
"type": "long"
},
"stock":{
"type": "long"
}
}
}
}
POST /dying_gq_bookstore
{"index": {}}
{"book_id": 1,"title": "离开时请叫醒我","tag": "情感","book_type": "畅销文学","content": "不论如何,离开时请告诉我","status": 1,"price": "48","stock": 20000}
{"index": {}}
{"book_id": 1,"title": "我想要两个西柚","tag": "情感","book_type": "畅销文学","content": "i want to see you","status": 1,"price": "39","stock": 6000}
{"index": {}}
{"book_id": 1,"title": "清风徐来","tag": "情感","book_type": "畅销文学","content": "随波逐流","status": 1,"price": "36","stock": 18622}
{"index": {}}
{"book_id": 1,"title": "迷途","tag": "悬疑","book_type": "畅销文学","content": "生活如待宰羔羊","status": 2,"price": "54","stock": 543}
{"index": {}}
{"book_id": 1,"title": "八百万种死法","tag": "悬疑","book_type": "外国文学","content": "这个城市八百万种人,八百万种死法","status": 2,"price": "69","stock": 888}
{"index": {}}
{"book_id": 1,"title": "福尔摩斯与他的狗","tag": "悬疑","book_type": "外国文学","content": "这只狗已经老去好久了","status": 2,"price": "198","stock": 88932}
{"index": {}}
{"book_id": 1,"title": "只能陪你走一程","tag": "治愈","book_type": "治愈文学","content": "抱歉我只能陪你走到这了","status": 2,"price": "38","stock": 765}
{"index": {}}
{"book_id": 1,"title": "一点点","tag": "交通","book_type": "交通治安","content": "能拉但只能拉一点点","status": 1,"price": "99","stock": 18833}
{"index": {}}
{"book_id": 1,"title": "成华大道","tag": "交通","book_type": "交通治安","content": "我该不该走成华大道","status": 1,"price": "23","stock": 2334}
{"index": {}}
{"book_id": 1,"title": "华仙桥","tag": "交通","book_type": "交通治安","content": "华仙桥旅行指南","status": 1,"price": "19","stock": 210}
核心类型
- 字符串:string,string类型包含 text 和 keyword。
- text:该类型被用来索引长文本,在创建索引前会将这些文本进行分词,转化为词的组合,建立索引;允许es来检索这些词,text类型不能用来排序和聚合。
- keyword:该类型不能分词,可以被用来检索过滤、排序和聚合,keyword类型不可用text进行分词模糊检索。
- 数值型:long、integer、short、byte、double、float
- 日期型:date
1.单条件查询
注以下部分查询会给出 类似 SQL 辅助理解,因倒排索引 term 匹配关系 SQL 是近似并非一定等效,一定注意
模糊匹配
以下模糊匹配都是基于 分词器 对 原本内容分词后 的 term 做匹配得出的效果。 (声明一下,一下所有查询都可以通过合适的语法组合起来进行更加复杂的查询)
match 分词模糊匹配
对搜索词分词后,查询匹配搜索列分词后的 term 如下例子 “你好呀世界” 可能会被分词为 “你好”、“世界”、“你”、“好”。(根据分词器的不同分词不同) 然后匹配倒排索引的 content 的 term。
POST /dying_gq_bookstore/_search
{
"from": 0, //分页 从 0 个开始
"size": 20, // 后 20 条数据 意义同 mysql limit 0,20
"query": {
"match": {// 对搜索词分词后 匹配字段内容分词后的 term
"content": "你好呀世界" // 字段 :匹配内容
}
}
}
类似 SQL:SELECT * FROM dying_gq_bookstore WHERE content LIKE '%你好呀世界%' LIMIT 0,20
prefix 前缀模糊匹配
POST /dying_gq_bookstore/_search
{
"from": 0, //分页 从 0 个开始
"size": 20, // 后 20 条数据 意义同 mysql limit 0,20
"query": {
"prefix": {// 匹配字段内容分词后 term 的前缀
"content": "你好呀世界" // 字段 :匹配内容
}
}
}
类似 SQL:SELECT * FROM dying_gq_bookstore WHERE content LIKE '你好呀世界%' LIMIT 0,20
regexp 正则匹配
POST /dying_gq_bookstore/_search
{
"from": 0, //分页 从 0 个开始
"size": 20, // 后 20 条数据 意义同 mysql limit 0,20
"query": {
"regexp": {// 分词 正则匹配字段内容分词 term
"content": "[1-9]" // 字段 :匹配内容
}
}
}
精确匹配
term 单个字段单条件精确匹配
精确匹配分词后的 term,和 match 的区别在于不会对你要匹配的词进行分词。
POST /dying_gq_bookstore/_search
{
"from": 0, //分页 从 0 个开始
"size": 20, // 后 20 条数据 意义同 mysql limit 0,20
"query": {
"match": {// 匹配字段内容分词后的 term
"content": "你好呀世界" // 字段 :匹配内容
}
}
}
类似 SQL:SELECT * FROM dying_gq_bookstore WHERE content = '你好呀世界' LIMIT 0,20
terms 单个字段多条件精确匹配
数组内的值相当于 or 的关系
POST /dying_gq_bookstore/_search
{
"from": 0, //分页 从 0 个开始
"size": 20, // 后 20 条数据 意义同 mysql limit 0,20
"query": {
"terms": {
"content": [
"你好",
"时间",
"不错"
]
}
}
}
类似 SQL:SELECT * FROM dying_gq_bookstore WHERE content = '你好' OR content = '时间' OR content = '不错' LIMIT 0,20
range 范围内精确匹配
POST /dying_gq_bookstore/_search
{
"from": 0, //分页 从 0 个开始
"size": 20, // 后 20 条数据 意义同 mysql limit 0,20
"query": {
"range": {
"book_id": {
"gte": 10, //大于
"lte": 20 //小于
}
}
}
}
类似 SQL:SELECT * FROM dying_gq_bookstore WHERE between 10 and 20 LIMIT 0,20
2.组合条件查询
bool 查询
可组合条件:
must : 各个条件都必须满足,即各条件是and的关系 should : 各个条件有一个满足即可,即各条件是or的关系 must_not : 不满足所有条件,即各条件是not的关系 filter : 不计算相关度评分,它不计算_score即相关度评分,效率更高
POST /dying_gq_bookstore/_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Search" }},
{ "match": { "content": "Elasticsearch" }}
],
"filter": [
{ "term": { "status": "published" }},
{ "range": { "book_id": { "gte": "200" }}}
]
}
}
}
3.聚合搜索
bucket: bucket 为分组后个每个桶 metric: metric 为每个桶中的统计分析(如 数量、最大值、最小值等)
注意 text 类型无法聚合
样例 1 :普通聚合
样例 1 查询语义为:
对 dying_gq_bookstore 索引按字段 tag 分组聚合 ,统计不同 tag 的数量并 按数量 升序排序。
POST /dying_gq_bookstore/_search
{
"size": 0, // 此处设置 size = 0 表示不返回 ES 数据文档,仅返回聚合后数据
"aggs": { // 聚合
"group_by_tag": { // 聚合名称,可以自定义名称
"terms": {//聚合匹配方式
"field": "tag", // 列名
"order": { // 排序
"_count": "asc" // 根据个数 升序 _count 为固定写法
}
}
}
}
}
样例 2 : 嵌套聚合 下钻分析
样例 2 查询语义为:
对 dying_gq_bookstore 索引按字段 tag 分组聚合 。并在此基础上对每个桶做统计分析即 metric ,求每个 bucket tag 分组下的书籍平均价格 并 按平均价格 升序排序。这种嵌套聚合的方式又称为 下钻分析
POST /dying_gq_bookstore/_search
{
"size": 0,
"aggs": { // 聚合
"group_by_tag": { // 聚合名称,可以自定义名称
"terms": {//聚合匹配方式
"field": "tag", // 列名
"order": { // 排序
"avg_by_price": "asc" // 求取的平均价格 升序 这里命名的作用就显示出来了
}
},
"aggs": { // 再次聚合 嵌套聚合 下钻分析
"avg_by_price": { // 聚合名称
"avg": { // 求平均值
"field": "price" // 平均值列名称
}
}
}
}
}
}
样例 3 : 多重嵌套聚合
样例 3 查询语义为:
以 tag 标签分组后 分析该标签下书籍平均价格,并以 tag 分组的 bucket 为基础 以 book_type 再进行子分组,子分组以 book_type 平均价格升序排序,外部 tag 分组以 tag 平均价格升序排序。
POST /dying_gq_bookstore/_search
{
"size": 0,
"aggs": {
"group_by_tag": {
"terms": {
"field": "tag",
"order": {
"avg_by_price_tag": "asc"
}
},
"aggs": {
"avg_by_price_tag": {
"avg": {
"field": "price"
}
},
"group_by_book_type":{
"terms": {
"field": "book_type",
"order": {
"avg_by_price_book_type": "asc"
}
},
"aggs": {
"avg_by_price_book_type": {
"avg": {
"field": "price"
}
}
}
}
}
}
}
}
为帮助理解,这里贴出查询结果如下:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 10,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"group_by_tag" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "治愈",
"doc_count" : 1,
"avg_by_price_tag" : {
"value" : 38.0
},
"group_by_book_type" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "治愈文学",
"doc_count" : 1,
"avg_by_price_book_type" : {
"value" : 38.0
}
}
]
}
},
{
"key" : "情感",
"doc_count" : 3,
"avg_by_price_tag" : {
"value" : 41.0
},
"group_by_book_type" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "畅销文学",
"doc_count" : 3,
"avg_by_price_book_type" : {
"value" : 41.0
}
}
]
}
},
{
"key" : "交通",
"doc_count" : 3,
"avg_by_price_tag" : {
"value" : 47.0
},
"group_by_book_type" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "交通治安",
"doc_count" : 3,
"avg_by_price_book_type" : {
"value" : 47.0
}
}
]
}
},
{
"key" : "悬疑",
"doc_count" : 3,
"avg_by_price_tag" : {
"value" : 107.0
},
"group_by_book_type" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "畅销文学",
"doc_count" : 1,
"avg_by_price_book_type" : {
"value" : 54.0
}
},
{
"key" : "外国文学",
"doc_count" : 2,
"avg_by_price_book_type" : {
"value" : 133.5
}
}
]
}
}
]
}
}
}
样例 4 : 聚合多分析
样例 4 查询语义为:
以 book_type 分组,统计分析组内 价格最大值、最小值、和总值
POST /dying_gq_bookstore/_search
{
"size": 0,
"aggs": {
"group_by_book_type": {
"terms": {
"field": "book_type",
"order": {
"price_min": "asc"
}
},
"aggs": {
"price_max": {
"max": {
"field": "price"
}
},
"price_min":{
"min": {
"field": "price"
}
},
"price_sum":{
"sum": {
"field": "price"
}
}
}
}
}
}
样例 5 : 聚合分组最大值
样例 5 查询语义为:
以 book_type 分组,统计分析组内价格最大的书籍并展示
POST /dying_gq_bookstore/_search
{
"size": 0,
"aggs": {
"group_by_book_type": {
"terms": {
"field": "book_type"
},
"aggs": {
"top_price": {
"top_hits": {
"size": 1,
"sort": [{
"price": {
"order": "desc"
}
}]
}
}
}
}
}
}
样例 6 : 区间分组统计
样例 6 查询语义为:
以 price 书籍价格分组 分组间隔为 20 :[0,20) [20,40) [40,60) ……。并统计分组内最大书籍价格
POST /dying_gq_bookstore/_search
{
"size": 0,
"aggs": {
"histogram_by_price": {
"histogram": {
"field": "price",
"interval": 20 // 分组间隔
},
"aggs": {
"max_by_price": {
"max": {
"field": "price"
}
}
}
}
}
}
为帮助理解,这里贴出查询结果如下:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 10,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"histogram_by_price" : {
"buckets" : [
{
"key" : 0.0,
"doc_count" : 1,
"max_by_price" : {
"value" : 19.0
}
},
{
"key" : 20.0,
"doc_count" : 4,
"max_by_price" : {
"value" : 39.0
}
},
{
"key" : 40.0,
"doc_count" : 2,
"max_by_price" : {
"value" : 54.0
}
},
{
"key" : 60.0,
"doc_count" : 1,
"max_by_price" : {
"value" : 69.0
}
},
{
"key" : 80.0,
"doc_count" : 1,
"max_by_price" : {
"value" : 99.0
}
},
{
"key" : 100.0,
"doc_count" : 0,
"max_by_price" : {
"value" : null
}
},
{
"key" : 120.0,
"doc_count" : 0,
"max_by_price" : {
"value" : null
}
},
{
"key" : 140.0,
"doc_count" : 0,
"max_by_price" : {
"value" : null
}
},
{
"key" : 160.0,
"doc_count" : 0,
"max_by_price" : {
"value" : null
}
},
{
"key" : 180.0,
"doc_count" : 1,
"max_by_price" : {
"value" : 198.0
}
}
]
}
}
}
补充:查询去重
此处语义为:查询前 5 条书籍价格最高的,并按 tag 去重
POST /dying_gq_bookstore/_search
{
"size": 5,
"collapse": { // 指定去重字段
"field": "tag"
},
"sort": [
{
"price": {
"order": "desc"
}
}
]
}
总结
总体来说 ES 的查询 DSL 为我们提供了丰富的语法和聚合语义。
基础查询 使用 query
对应常用查询条件 match
、prefix
、regexp
、trem
、trems
、range
。
组合条件 bool
条件连接 must
、should
、must_not
、filter
同时配合上述 query 条件进行查询。
聚合分析 aggs
聚合条件 trems
、avg
、sum
、max
、min
、top_hits
、histogram
、date_histogram
这三大类相互 嵌套组合 基本可以覆盖我们绝大多数场景了。更多进阶用法可以到官网进行学习查询。官方文档地址
我是 dying 搁浅 我看了一场 97 小时的电影也没能等来你的点赞关注收藏,我想这不是你不够喜欢我,而是我看的电影不够长……