ElasticSearch系列整体栏目
内容 | 链接地址 |
---|---|
【一】ElasticSearch下载和安装 | https://zhenghuisheng.blog.csdn.net/article/details/129260827 |
【二】ElasticSearch概念和基本操作 | https://blog.csdn.net/zhenghuishengq/article/details/134121631 |
【二】ElasticSearch的高级查询Query DSL | https://blog.csdn.net/zhenghuishengq/article/details/134159587 |
ElasticSearch的高级句法查询Query DSL
- [一,ElasticSearch高级查询语法Query DSL](#一,ElasticSearch高级查询语法Query DSL)
-
- [一,Query DSL的基本使用](#一,Query DSL的基本使用)
-
- 1.1,深分页查询Scroll
- 1.2,match条件查询
- 1.3,match_phrase短语查询
- 1.4,multi_match多字段查询
- [1.5,query_string 查询](#1.5,query_string 查询)
- 1.6,term精确匹配
- 1.7,prefix前缀查询
- 1.8,通配符查询wildcard
- 1.9,范围查询range
- 1.10,fuzzy模糊查询
- 1.11,highlight查询
- [2,Query DSL多条件查询(高级查询)](#2,Query DSL多条件查询(高级查询))
-
- [2.1,Bool Query布尔查询](#2.1,Bool Query布尔查询)
- [2.2,Boosting Query权重查询](#2.2,Boosting Query权重查询)
- [2.3,Dis max query 最佳匹配](#2.3,Dis max query 最佳匹配)
- [2.4,Cross Field跨字段匹配](#2.4,Cross Field跨字段匹配)
一,ElasticSearch高级查询语法Query DSL
前面两篇主要讲解了es的安装以及一些基本的概念,接下来这篇讲解的是es的高阶语法,QueryDSL。在这里主要是用ik分词器讲解,暂不使用默认的分词器。
一,Query DSL的基本使用
在安装了kibana之后,内部会有一个search的语句,用来查询数据
java
GET _search
{
"query": {
"match_all": {}
}
}
其结果如下,默认是返回前10条数据,类似于做了分页,默认加了一个from0和一个size10,并且在es中,size默认是小于或者等于10000,如果超过这个值,就会直接抛异常
1.1,深分页查询Scroll
上面说了默认采用的是from加size的方式来解决分页数据返回的问题,但是size的数据是有大小的限制的,当然也可以通过以下命令来调节size的大小
java
PUT /zhs/_settings
{
"index.max_result_window" :"20000"
}
虽然这种方式可以暂时调节size大小,但是治标不治本,因为依旧是会存在限制,并且由于数据量太大,还可能将内存撑爆。因此后面引入了这种Scroll游标的方式来查询全量数据
java
GET /zhs_db/_search?scroll=1m //1m表示查询时间窗口保持1分钟
{
"query": {"match_all": {}},
"size": 10 //批量查询10条数据
}
在将查询的值返回中可以看出,会生成一个_scroll_id,以及返回一些分片数,查询的总条数等
就是比如说第一次查询10条数据,随后记录最后一条数据的id,然后在这个时间窗口期内,携带这个id再去库中拉取后十条数据,往复如此。不管是关系系数据库还是非关系型数据库,其设计思想都是这样
拉取的数据会存储在快照里面,后面的操作都是操作这个快照中缓存的数据。因此为了保证性能问题,会牺牲一些精准度,因为后面写进来的数据不在这个快照里面。
1.2,match条件查询
在使用这个match之前,先创建一个索引,并设置分词器为ik分词器
java
DELETE /zhs_db
PUT /zhs_db
{
"settings" : {
"index" : {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
先插入几条数据,先用最基础的Put的方式插入五条数据
java
PUT /zhs_db/_doc/1
{
"address":"东岳泰山"
}
PUT /zhs_db/_doc/2
{
"address":"西岳华山"
}
PUT /zhs_db/_doc/3
{
"address":"南岳衡山"
}
PUT /zhs_db/_doc/4
{
"address":"北岳恒山"
}
PUT /zhs_db/_doc/5
{
"address":"中岳嵩山"
}
在确定要查询某一条数据时,可以先通过这个分词分析看看是如何进行分词的
java
POST _analyze
{
"analyzer": "ik_max_word",
"text": "中岳嵩山"
}
那么可以直接通过这个match的方式批量查询数据
java
GET /zhs_db/_search
{
"query": {
"match": {
"address": "中岳"
}
}
}
如果是要查询特定的某个值,可以直接再加一个operator属性,并且value设置成and,如果没有设置这个属性,那么默认值就是的or
java
GET /zhs_db/_search
{
"query": {
"match": {
"address": {
"query": "中岳嵩山",
"operator": "and"
}
}
}
}
除了上面的operator之外,还可以使用 minimum_should_match ,用于最小分词匹配。就是说分词器默认分为中岳和嵩山两个,只需要满足其中一个就能被查出来
java
address:{
"query":"中岳嵩山",
"minimum_should_match": 1
}
1.3,match_phrase短语查询
在使用这个短语查询时,需要通过分词器分析,判断两个词的下标是否连续
java
GET /zhs_db/_search
{
"query": {
"match_phrase": {
"address": "中岳嵩山"
}
}
}
如通过这个ik分词器分析,可以得知这两个分开的词的position是连续的,分别为0和1,如果不连续,则不能将值查询出
当然为了解决这个间隔问题,可以直接通过设置 slop 属性来设置允许多少个空格进行匹配
java
address:{
"query":"中岳嵩山",
"slop": 1
}
1.4,multi_match多字段查询
上面主要讲解的是单字段查询,但是在实际开发中一般都是多字段查询,其语句如下
java
GET /zhs_db/_search
{
"query": {
"multi_match": {
"query": "中岳嵩山",
"fields": ["address","name"]
}
}
}
1.5,query_string 查询
queryString相当于是一个multi_match的一个综合版,如果没有指定具体的字段,则会在全字段中查询
java
GET /zhs_db/_search
{
"query": {
"query_string": {
"query": "中岳"
}
}
}
可以设置默认的字段,也可以指定多个字段
java
"query_string": {
//"default_field": "address",
"fields": ["name","address"],
"query": "中岳"
}
1.6,term精确匹配
上面的match属于是模糊匹配,而使用精确匹配的,就是这个term。
在ES的Mapping Type 中 keyword , date ,integer, long , double , boolean or ip 这些类型不分词,只有text类型分词。因此term在对这些数据进行查询时,就是精确匹配
java
GET /zhs_db/_search
{
"query": {
"term": {
"address": "中岳"
}
}
}
如果想要对全字段进行精确匹配,可以添加一个keyword 关键字
java
"address.keyword": "中岳嵩山"
在es中,查询会有算分操作,而算分操作会影响到性能问题,而精确匹配是不需要算分的,可以将query转成filter,从而忽略算分所带来的影响
java
"query":{
"constant_score":{
"filter":{
}
}
}
如果短时间内存在多次term的查询,那么就会将这部分数据缓存起来
1.7,prefix前缀查询
前缀查询就是查询以某个字段开头的数据,因此用不上底层的倒排字典,而是将所有的数据遍历一遍,将符合的数据返回。由于用不上倒排索引,因此对性能是有一定的影响的
java
PUT /zhs_db/_search
{
"query":{
"prefix":{
"address":{
"value":"嵩山"
}
}
}
}
1.8,通配符查询wildcard
通配符查询就和这个前缀查询一样,都是利用不上这个倒排索引,而是将所有的数据遍历查询一遍,符合的数据返回。
java
GET /zhs_db/_search
{
"query": {
"wildcard": {
"address": {
"value": "*山*"
}
}
}
}
1.9,范围查询range
可以直接通过这个range关键字实现范围查询,
- gte 大于等于
- lte 小于等于
- gt 大于
- lt 小于
- now 当前时间
java
POST /zhs_db/_search
{
"query": {
"range": {
"age": {
"gte": 25,
"lte": 28
}
}
}
}
1.10,fuzzy模糊查询
fuzzy表示允许在打错字的情况下,将想要查询的数据查询出来。
java
GET /zhs_db/_search
{
"query": {
"fuzzy": {
"address": {
"value": "松山",
"fuzziness": 1 //表示允许错一个字
}
}
}
}
除了使用上面这种方式,还能用match的方式实现这种错别字的模糊查询
java
GET /zhs_db/_search
{
"query": {
"match": {
"address": {
"query": "松山",
"fuzziness": 1
}
}
}
}
1.11,highlight查询
就是将query查询出来的结果,通过highlight的方式实现高亮
java
GET /products/_search
{
"query": {
"term": {
"name": {
"value": "牛仔"
}
}
},
"highlight": {
"fields": {
"*":{}
}
}
}
2,Query DSL多条件查询(高级查询)
2.1,Bool Query布尔查询
在一个bool查询中,可以是一个或者多个查询字句的组合,字句总共有四种,分别是 must、should、must_not、filter,前两者使用时内部会进行算分的操作,后二者不会
must相当于是and操作,即所有几句中的查询条件都要满足。如下must中是一个数组,每个子查询中就是一个正常的query dsl查询,如必须满足中地址字段中带有公园,remark字段中带有北的数据
java
GET /zhs_db/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address": "公园"
}
},
{
"match": {
"remark": "北"
}
}
]
}
}
}
shouuld 表示的就是一个or的应用,表示只需要满足其中的一个查询字句就能将结果返回
java
GET /zhs_db/_search
{
"query": {
"bool": {
"should": []
}
}
}
2.2,Boosting Query权重查询
权重查询是一种控制手段,通过设置boost权重的值来影响最终的查询结果,权重的设置如下
- 当设置的boost大于1时,查询的的相关性会提高
- 当设置的boost大于0而小于1时,查询的相关性会降低
- 当设置的boost的值为负数时,贡献负分
举一个例子,查询一篇文章时,将会员的文章显示在普通用户文章的前面,如下面的代码,先创建一个文章索引,随后插入两条数据,一条是vip用户的,一条是普通用户的,文章标题一样
java
PUT /article_db
POST /article_db/_bulk
{"index": {"_id": "1"}}
{"title":"java入门","comment":"精通java","type":"vip"}
{"index": {"_id": "2"}}
{"title":"java入门","comment":"精通java","type":"ordinary"}
那么在查询时,想将vip用户的文章排在前面,就可以直接通过设置这个boost权重进行设置,将vip用户的权重值设置为大于1,这样在算分时,算的分值就更大
java
GET /article_db/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"title": "java入门"
}
},
{
"match": {
"type": {
"query": "vip",
"boost": 3
}
}
},
{
"match": {
"type": {
"query": "ordinary",
"boost": 1
}
}
}
]
}
}
}
如下图所示,vip的算分为2.6,而普通用户的算分在1.2。如果算分值一样,谁id小谁在前面
当然如果查询出了不需要的数据,优先考虑通过过滤去掉数据,再考虑降低其权重
2.3,Dis max query 最佳匹配
通过dis_max以及结合queries进行使用,并且可以通过设置这个tie_breaker来确人是最佳匹配,还是所有的字段的值同等重要
java
POST /article_db/_search
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "java" }},
{ "match": { "comment": "java" }}
],
"tie_breaker": 0.5 //0代表使用最佳匹配;1代表所有语句同等重要。
}
}
}
但是在实际开发中,更加的推荐通过这个multi_match这个方式来实现这个最佳字段匹配,并且设置这个type类型为 best_fields
java
POST /article_db/_search
{
"query": {
"multi_match": {
"type": "best_fields",
"query": "java",
"fields": ["title","comment"],
"tie_breaker": 0.2 //0代表使用最佳匹配;1代表所有语句同等重要。
}
}
}
除了实现最佳匹配之外,multi_match还实现了最多字段匹配,就是将type的类型设置成 most_fields
java
GET /titles/_search
{
"query": {
"multi_match": {
"query": "java,
"type": "most_fields",
"fields": [
"title",
"comment"
]
}
}
}
2.4,Cross Field跨字段匹配
如在遇到某些场景,需要结合多个字段的值进行匹配,如省市区,在上面讲了一种copy_to 的方式解决这种跨字段匹配的方式,也可以使用这个 Cross Field 实现多字段匹配
如先创建一个address_db的地址索引,随后批量的插入一些数据
java
PUT /address_db
PUT /address_db/_bulk
{ "index": { "_id": "1"} }
{"province": "广东","city": "深圳","region":"南山"}
{ "index": { "_id": "2"} }
{"province": "广东","city": "深圳","region":"福田"}
{ "index": { "_id": "3"} }
{"province": "广东","city": "深圳","region":"宝安"}
{ "index": { "_id": "4"} }
{"province": "广东","city": "深圳","region":"龙岗"}
}
随后通过这个multi_match多字段查询,并且设置type类型为 cross_fields
java
GET /address_db/_search
{
"query": {
"multi_match": {
"query": "广东深圳宝安",
"type": "cross_fields",
"operator": "and",
"fields": ["province","city","region"]
}
}
}