ES之DSL简单查询

举报
object 发表于 2024/03/05 20:32:20 2024/03/05
【摘要】 DSL全称Domain Specific Language,即领域特定语言,ES基于JSON提供完整的DSL来定义查询。因为一些工作需要,后续对DSL做了一些了解,感觉如果结合ES一起进行使用,练习会更好。首先是简单查询,类似于sql中的条件匹配,且只有一个条件# match_all 全量查询,无匹配条件。 类似sql无where{ "query":{ "match_a...

DSL全称Domain Specific Language,即领域特定语言,ES基于JSON提供完整的DSL来定义查询。因为一些工作需要,后续对DSL做了一些了解,感觉如果结合ES一起进行使用,练习会更好。

首先是简单查询,类似于sql中的条件匹配,且只有一个条件

# match_all  全量查询,无匹配条件。 类似sql无where
{
    "query":{
        "match_all":{
          
        }
    }
}

# match 针对某一个字段进行的关键字匹配  。 类似sql中的contanins
{
    "query":{
        "match":{
           "username":"小明"
        }
    }
}

# term 精确匹配,针对某一个字段的值进行精确匹配 。 类似sql中的username=
{
    "query":{
        "term":{
           "username":"小明"
        }
    }
}

#terms 精确匹配多个值,针对某一个字段的值进行精确匹配。 类似sql中的 in ("")
{
    "query":{
        "terms":{
           "username":["小明","小红"]
        }
    }
}

# wildcard 通配符匹配,针对某一个字段的值进行模糊匹配。 类似sql中的like
{
    "query":{
        "wildcard":{
           "username": "*小明*"
        }
    }
}

# prefix 前缀查询,针对某一个字段的值进行前缀匹配。类似sql中的like '%x'
# todo 后续补充:这里使用的时候发现了一些问题,例如查不出来数据,即使只匹配一个开头字母,暂未确定什么原因,暂记,后续补充,大概率是字段的类型有关
{
    "query":{
        "prefix":{
           "username": "王小"
        }
    }
}

# fuzzy 模糊查询,针对某一个字段的值进行模糊匹配,这个sql中就没有了,比喻的话就是相似度匹配。fuzziness为可允许的错误的数,也可设置为Auto
# todo 后续补充:这里使用的时候,可能还涉及其他的干扰,例如一个词允许的错误次数,例如,一个字段只有3个字,即使fuzziness设置了3,但是如果有两个字错误,依旧是搜索不出来的,  但是如果字段的数比较多的情况,又可以搜出来,所以应该是有其他的限制
{
    "query":{
        "fuzzy":{
           "username": {
               "value":"王大明",
               "fuzziness":1
            }
        }
    }
}

#regexp 正则匹配,针对某一个字段进行正则匹配,sql中没有,跟普通的正则差不多。
{
    "query":{
        "regexp":{
           "username": "^[a-zA-Z]{3}$"
        }
    }
}

其次是稍微复杂的一点的场景,例如需要多个条件同时匹配的场景,类似于where的and or连接,用的较多,可以把上述简单合并一起使用。

# bool 布尔查询,联合多个简单子查询得出结果,需要结合3个关键连接词使用must、must_not、should、filter。
# must类比sql中的and。 must中列举的多个条件都要满足
# must_not类比sql中的 and not。 must_not中列举的多个条件都要不满足
# should类比sql中的or。  should中列举的多个条件任意一个满足即可
# filter类比sql中的and。   filter中列举的多个条件都要满足。

// todo 后续补充:filter和must是一样的,为什么要有两个呢,这里涉及分数算法,这个概念暂时还不太懂,后续了解了补充。反正filter和must_not不涉及得分,可以优先使用,效率更高一点。
{
    "query":{
        "bool":{ # 布尔查询,  可以嵌套多个关键连接词,  关键连接词可以嵌套多个条件匹配,连接词之间最后用and连接。
           "must": [{"match":{"username":"小明"}},{"term":{"sex":"男"}}],
           "must_not": [{"match":{"username":"小明"}},{"term":{"sex":"男"}}],
           "should": [{"match":{"username":"小明"}},{"term":{"sex":"男"}}],
           "filter": [{"match":{"username":"小明"}},{"term":{"sex":"男"}}]
        }
    }
}

补充:should与must、filter公用会导致预期输出不一致的情况。这个有点是意料之外的,按照开发的逻辑去进行条件筛选,但是should的时候,这种逻辑却行不通了

以下举例进行说明:

// 举例说明,我需要查询一个姓张或者姓李的同学 且需要性别是男的
按照逻辑,应该就是  should任意满足姓氏 + must 满足 行性别
{
	"query": {
		"bool": {
			"should": [
				{
					"regexp": {
						"request": "张.*"
					}
				},
				{
					"regexp": {
						"request": "李.*"
					}
				}
			],
			"must": [
				{
					"term": {
						"gender": "男"
					}
				}
			]
		}
	},
	"_source": [
		"@timestamp",
		"name",
		"gender"
	]
}

# 猜一下上述代码片段输出什么? 1.姓张和姓李 且有小字  2.姓张和姓李所有同学  3.名字有小的所有同学
# 答案是3,所有名字有小的同学。
# 猜测原因 1.should和must之间的连接是or  2.跟执行should或者must的顺序有关
# 测试结论: 1.如果or的话,只需要验证一下3个毫无关系的名字即可, 例如叫must王小明  should张飞 should李晓,验证结论 无效,只输出了王小明    2.如果是跟顺序有关, 调换must和should的顺序,无效,输出与为调换前一致


# todo 暂时未定位到什么原因,结果大佬的文章分析,应该和DSL的解析有关,但是对于具体细节并没有说太多。
# 不过对于相关的解决办法目前倒是明确的,就是使用嵌套的方法,使用bool + must +bool进行嵌套,达到预期的数据输出,调整后代码如下:
{
	"query": {
		"bool": {
			"must": [ # 新增must嵌套,外层连接
				{
					"bool": { # 每一组筛选 使用bool进行判断   多个bool组成一个大的must
						"should": [
							{
								"regexp": {
									"name": "张.*"
								}
							},
							{
								"regexp": {
									"name": "李.*"
								}
							}
						]
					}
				},
				{
					"bool": {
						"must": [
							{
								"term": {
									"gender": "男"
								}
							}
						]
					}
				}
			]
		}
	},
	"_source": [
		"@timestamp",
		"name",
		"gender"
	]
}

以上基本上就是经常使用的一些语法查询,对于一些简单的查询应该足够了,不过工作中可能还会涉及更复杂的场景,例如分页、排序、范围查询、时间查询、聚合查询、多重嵌套条件等等。

# range、sort、from、_source等常用的语法关键字列举。避免麻烦,相关的一些字段就一起列出来了,就不一一列举了
{
	"query": {
		"bool": { # 布尔查询
			"filter": [ #过滤
				{
					"range": { #范围查询
						"@timestamp": {
							"gt": "2024-03-05 17:00:00", #大于   当前时间可用now。  可进行运算如 now-1h:当前时间减少一个小时
							"lt": "2024-03-05 18:00:00",# 小于     m h d w y 分别代表分时天周年
							"time_zone": "+08:00", # 位置时间  非必填
							"format": "yyyy-MM-dd HH:mm:ss" #时间格式  非必填
						}
					}
				}
			],
			"must_not": [
				{
					"exists": { #查询结果是否包含某一类字段值,这里是过滤字段不是值, 感觉很少用到
						"field": "area"
					}
				}
			]
		}
	},
	"sort": [ #排序
		{
			"timestamp": { # 字段名
				"order": "asc" #升序降序
			}
		}
	],
	"from": 1, #页码
	"size": 3, #页条数
	"_source": [ #返回字段
		"@timestamp",
		"body_bytes_sent",
		"request",
		"response"
	]
}

剩下的话,还剩一个聚合关键字aggs。类比于sql中的group by having函数。使用上就更加复杂了,这里就做简单描述了。todo 后续就机会再进行补充。

# aggs函数,就是将筛选出来的数据再次就行聚合分组,可以用来计算总数,平均数,最大值等等
# 直接类比sql的where  + group by having。我觉得十分类似,先bool后aggs
{
	"query": {
		"bool": {
			"filter": [
				{
					"range": {
						"@timestamp": {
							"gt": "2024-03-05 17:00:00",
							"lt": "2024-03-05 18:00:00",
							"time_zone": "+08:00",
							"format": "yyyy-MM-dd HH:mm:ss"
						}
					}
				}
			]
		}
	},
	"aggs": { # 聚合函数
		"NAME": { # 聚合的字段名,任意取即可
			"terms": { # 按照字段进行聚合分组
				"field": "request",  # 分组的字段
				"size":3 # 保留的行数
			}
		}
	}
}

上述比较重要的就是terms,表明是通过字段进行分组,除了terms,还有其他的字段,作用各不相同

terms聚合:按字段进行分组,并统计每个分组的文档数量。
date_histogram聚合:按时间间隔对日期字段进行分组,并统计每个时间间隔内的文档数量。
range聚合:将字段的值划分为不同的范围,并统计每个范围内的文档数量。
histogram聚合:将数值字段的值划分为不同的区间,并统计每个区间内的文档数量。
avg聚合:计算数值字段的平均值。
sum聚合:计算数值字段的总和。
min聚合:找到数值字段的最小值。
max聚合:找到数值字段的最大值。
cardinality聚合:计算字段的基数(不重复值的数量)。
top_hits聚合:返回每个分组中的顶部文档。
extended_stats聚合:计算数值字段的统计信息,包括平均值、标准差、最小值、最大值等。
percentiles聚合:计算数值字段的百分位数。
geo_distance聚合:按地理距离对地理坐标字段进行分组,并统计每个距离范围内的文档数量。
filter聚合:根据指定的过滤条件对文档进行聚合。
nested聚合:在嵌套的文档结构中进行聚合操作。
value_count聚合:计算某个字段的值的数量。
stats聚合:计算数值字段的统计信息,包括平均值、总和、最小值、最大值和文档数量。
scripted_metric聚合:使用自定义脚本计算聚合结果。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。