【ElasticSearch系列-03】ElasticSearch的高级句法查询Query DSL

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

前面两篇主要讲解了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"]
    }
  }
}
相关推荐
小筱在线9 分钟前
SpringCloud微服务实现服务熔断的实践指南
java·spring cloud·微服务
luoluoal14 分钟前
java项目之基于Spring Boot智能无人仓库管理源码(springboot+vue)
java·vue.js·spring boot
ChinaRainbowSea20 分钟前
十三,Spring Boot 中注入 Servlet,Filter,Listener
java·spring boot·spring·servlet·web
小游鱼KF23 分钟前
Spring学习前置知识
java·学习·spring
扎克begod26 分钟前
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
java·开发语言·python
青灯文案128 分钟前
SpringBoot 项目统一 API 响应结果封装示例
java·spring boot·后端
我就是程序猿38 分钟前
tomcat的配置
java·tomcat
阳光阿盖尔44 分钟前
EasyExcel的基本使用——Java导入Excel数据
java·开发语言·excel
二十雨辰1 小时前
[苍穹外卖]-12Apache POI入门与实战
java·spring boot·mybatis
程序员皮皮林1 小时前
开源PDF工具 Apache PDFBox 认识及使用(知识点+案例)
java·pdf·开源·apache