ES DSL查询

1、Query String Search

参考前面的文章

 

2、全文检索

运行数据构建,代码如下:

PUT /logs/_doc/1
{
  "Name":"apple phone",
  "Desc":"phone made in china 2022-08-01",
  "Date":"2022-08-01",
  "Price":111,
  "Tags":["Breakfast","Carbon","Cheap"]
}

PUT /logs/_doc/2
{
  "Name":"apple earphone",
  "Desc":"earphone made in china 2022-08-03",
  "Date":"2022-08-01",
  "Price":222,
  "Tags":["Breakfast","Cheap"]
}

PUT /logs/_doc/3
{
  "Name":"apple watch",
  "Desc":"watch made in china 2022-08-02",
  "Date":"2022-08-02",
  "Price":333,
  "Tags":["Breakfast","Zhangsan","Cheap"]
}

PUT /logs/_doc/4
{
  "Name":"huawei phone honor",
  "Desc":"honor made in china domestic",
  "Date":"2022-07-01",
  "Price":444,
  "Tags":["Breakfast","Lisi","Cheap"]
}

PUT /logs/_doc/5
{
  "Name":"huawei watch",
  "Desc":"huawei watch made in china 2022-06-01",
  "Date":"2022-06-01",
  "Price":555,
  "Tags":["Breakfast","Wangwu","Cheap"]
}

 

ES中全文检索的方式分为以下几种:

(1)、match语法

GET /logs/_search
{
  "query": {
    "match": {
      "Name": "apple watch"
    }
  }
}

当向ES插入数据时,如果采用默认设置,且设置了倒排索引,那么对应的字符串会被分词并建立倒排表.且到使用match进行匹配时,如上代码,匹配的是logs索引的Name字段,其值也会被分词,然后去倒排表检索,返回结果集.那么logs索引中Name字段包含apple和watch分词的document记录都会被检索出来,且如果有document记录的Name字段被分词后同时包含apple和watch的记录其评分会比包含一个的要高.具体的评分算法后续文章会介绍,频分关系到记录的排序.

 

(2)、match_all语法

Get /logs/_search
{
  "query":{
    "match_all": {}
  }
}

查询logs索引下所有的记录.

 

(3)、multi_match语法

Get /logs/_search
{
  "query":{
    "multi_match": {
      "query": "apple domestic",
      "fields": ["Name","Desc"]
    }
  }
}

查找logs索引下,字段Name、Desc包含apple或者domestic分词的document记录.支持多字段查找.

 

(4)、match_phrase语法

GET /logs/_search
{
  "query": {
    "match_phrase": {
      "Name": "apple earphone"
    }
  }
}

于上面的语法不同的是,apple watch只能关联apple watch的文档,无法查询watch apple,因为match_phrase将apple watch看作一个整体(短语)进行搜索.而其余语法会进行先分词然后在进行搜索.

 

(5)、match与match_phrase的异同点

相同点:两者都会对查询内容进行分词

不同点:match只需要包含一个词项就能检索到,而match_phrase需要包含所有的词项,且顺序内容要完全一致才能被检索到.

 

3、精准匹配

(1)、term语法

GET /logs/_search
{
  "query": {
    "term": {
      "Name": "apple"
    }
  }
}

需要注意以下问题,代码如下:

GET /logs/_search
{
  "query": {
    "term": {
      "Name": "apple watch"
    }
  }
}

这里会去倒排表检索term包含apple watch词项的document,但是一般插入内容会被分词,极少会出现这种情况,所以一般情况下只能检索到包含apple或者watch词项的document,所以以上demo一般清苦下无查询结果集.

 

(2)、term与match系列的区别

term语法查询与match系列不同的是term不会对查询内容进行分词.而match会进行分词.

 

(3)、keyword

es会为keyword的类型创建正排索引,并不会为text类型创建正排索引.

(1)中说明了term的缺点,通过keyword关键字就能解决其问题,产生问题的原因是因为使用动态映射时,ES会对Text类型进行分词,同时会有一个keyword类型,其构造大致如下:

"Desc": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256  //字符串超过256进行字符串截取
            }
          }
        }

ES在对Desc字段进行分词的同时,并不会对Keyword进行分词,但是超过当前字段内容如果大于256则会进行内容截取.那么就可以通过keyword和term进行内容的精准匹配了代码如下:

GET /logs/_search
{
  "query": {
    "term": {
      "Name.keyword": "huawei watch"
    }
  }
}

 

(4)、terms 语法

GET /logs/_search
{
  "query": {
    "terms": {
      "Tags.keyword": [
        "Breakfast","Zhangsan","Cheap"
      ]
    }
  }
}

terms语法就是在term上的in操作.

 

(5) range语法

GET /logs/_search
{
  "query": {
    "range": {
      "Price": {
        "gte": 100,
        "lte": 112
      }
    }
  }
}

查询Price字段值在100和112范围之间的document记录.

注:gte代表大于等于.  gt-大表大于  lte代表小于等于   lt代表小于

时间操作同样如此,代码如下:

GET /logs/_search
{
  "query": {
    "range": {
      "Date": {
        "gt": "2022-06-01",
        "lte": "2022-07-01"
      }
    }
  }
}

查找Date日期字段大于2022-06-01小于等于2022-07-01范围之间的记录.

 

4、过滤器 filter

本质也是一种查询,与query类似.区别是,query查询的是当前查询语句与document的相关度是怎么样的,也就是query会计算相关度评分,query的查询结果会以相关度评分作为排序条件,进行排序。而filter不会计算相关度评分,且filter有相应的缓存机制,这也是filter的查询效率比query高的原因,调用代码如下:

GET /logs/_search
{
  "query": {
    "constant_score": {
      "filter": 
      {
        "term": {
          "Name": "huawei"
        }
      }
    }
  }
}

constant_score代表采用固定的得分,查询的评分不在计算和改变.结果集的评分都是1.0

 

5、组合查询 Bool Query

bool查询可以组合多个查询条件,一般分为两种类型

(1)、计算评分的查询 must,should

满足must和should子句的文档会合并起来计算分值

must查询如下:

GET /logs/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "Name.keyword": {
              "value": "huawei watch"
            }
          }
        },
        {
          "match": {
            "Desc": "huawei"
          }
        }
      ]
    }
  }
}

查询的是logs索引中Name的keyword值为huawei watch的且Desc字段分词后存在索引值为huawei的结果集,关于keyworld参考上面的介绍.

注:Must语法相当于and操作.所有的条件必须都满足.

 

should查询如下:

GET /logs/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match_phrase": {
            "Name": "apple phone"
          }
        },
        {
          "range": {
            "Price": {
              "gte": 555,
              "lte": 666
            }
          }
        }
      ]
    }
  }
}

在没有filter、must、must_not等子句是,should操作等同于关系型数据库的or操作

 

(2)、不计算评分的查询 filter和must_not

filter查询如下:

GET /logs/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "Name.keyword": {
              "value": "huawei watch"
            }
          }
        },
        {
          "match": {
            "Desc": "huawei"
          }
        }
      ]
    }
  }
}

filter的查询结果集和must相同,功能也相同相当于and操作,所有的条件必须都满足,区别是filter不计算评分.

must_not查询如下:

GET /logs/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "term": {
            "Name.keyword": {
              "value": "huawei watch"
            }
          }
        },
        {
          "match": {
            "Desc": "huawei"
          }
        }
      ]
    }
  }
}

must_not查询的结果集是must的!操作,所有的条件必须都不满足,才能计入结果集,其不计算评分.

 

(3)must和fliter的组合查询

GET /logs/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "Name": "earphone"
          }
        }
      ], 
      "filter": [
        {
          "range": {
            "Price": {
              "gte": 111,
              "lte": 333
            }
          }
        }
      ]
    }
  }
}

must和filter组合的查询的好处是,当数据量特别大时,可以用filter先确定数据集的大致范围,之后利用must进行计算评分.提高查询的效率.应为filter只会做查询结果的匹配不会计算评分且fliter会对热点查询进行缓存,进一步提高查询效率.

 

(4)、should查询的问题及如何使用

一般情况下should如果没有和must和fliter组合查询,其功能就是or查询,但是一但和二者进行了组合查询,如下:

GET /logs/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "Price": {
              "gte": 111,
              "lte": 333
            }
          }  
        }
      ],
      "should": [
        {
          "match_phrase": {
            "Name": "apple phone"
          }
        }
      ]
    }
  }
}

此时的操作,should操作不会生效,只会执行filter操作.解决方案如下:

GET /logs/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "Price": {
              "gte": 111,
              "lte": 333
            }
          }  
        }
      ],
      "should": [
        {
          "match_phrase": {
            "Name": "apple phone"
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}

通过minimum_should_match关键字解决,原因是因为当组合查询中should与filter和must共存是minimum_should_match默认就为0,含义就是当指定了must或者filter查询时,should内的条件都不满足,所以当显示指定minimum_should_match为1时,说明要同时满足must或者filter条件的同时,should内的条件至少满足1个.

 

6、批量查询

6.1 批量查询多个索引的数据 分别查询food和logs索引id为1的数据

GET /_mget
{
  "docs": [
    {
      "_index": "food",
      "_id": 1
    },
    {
      "_index": "logs",
      "_id": 1
    }
  ]
}

 

6.2 根据id集合批量查询索引数据

类似sql select * from tab where id in ()

GET logs/_mget
{
  "ids":[1,2,3]
}

如果需要指定查询字段内,类似 select field1,field2 from tab where id in (),有两种方式,第一种用query配合ids代码如下:

GET logs/_search
{
  "_source": {
    "includes": "Date"
  }, 
  "query": {
    "ids": {
      "values": [1,2,3]
    }
  }
}

第二种通过_mget

GET logs/_mget
{
  "docs": [
    {
      "_id": 1,
      "_source": [
        "Date"
      ]
    },
    {
      "_id": 2,
      "_source": [
        "Date"
      ]
    }
  ]
}

 

posted @ 2022-08-03 14:39  郑小超  阅读(276)  评论(0编辑  收藏  举报