Query DSL是一种通过request body提交搜索参数的请求方式。就是将请求头参数(?xxx=xxx)转换为请求体参数。
语法格式:
GET [/index_name/type_name]/_search
{
"query_name" : {
"argument" : "value" [, ....]
},
"query_name" : {
"field_name" : {
"argument" : "value" [, ....]
}
}
}
4.1测试数据
PUT /test_index
{
"mappings": {
"test_type" : {
"properties": {
"dept_name" : {
"type" : "keyword"
},
"emps.name" : {
"type" : "text",
"analyzer": "standard"
},
"emps.age" : {
"type": "long"
},
"num_of_emps" : {
"type": "long"
},
"tags" : {
"type" : "keyword"
}
}
}
}
}
PUT /test_index/test_type/1
{
"dept_name" : "Sales Department",
"emps" : [
{ "name" : "zhangsan", "age" : 20, "join_date" : "2018-07-10" },
{ "name" : "wangwu", "age" : 22, "join_date" : "2018-07-10" }
],
"num_of_emps" : 2,
"tags" : [ "Sales tag1", "Sales tag2", "Sales tag3" ]
}
PUT /test_index/test_type/2
{
"dept_name" : "Development Department",
"emps" : [
{ "name" : "Rod Johnson", "age" : 40, "join_date" : "2016-07-10" },
{ "name" : "James Gosling", "age" : 41, "join_date" : "2017-07-10" },
{ "name" : "Patrick Lencioni", "age" : 42, "join_date" : "2018-01-10" }
],
"num_of_emps" : 3,
"tags" : [ "Development tags1", "Development tags2", "Development tags3" ]
}
4.2搜索案例
全查询
GET /test_index/test_type/_search
{
"query" : {
"match_all" : {}
}
}
单字段匹配
GET /test_index/test_type/_search
{
"query" : {
"match" : { "emps.name" : "zhangsan" }
}
}
组合条件:
用java来描述 : ( a > 3 && ( b < 10 || c == 6 ) )
bool - 用于组合多条件,相当于java中的布尔表达式。相当于最外层的括号,也就是完整的布尔表达式。
must - 必须符合要求, 相当于java中的逻辑运算符 ==或&&。在must范围内的条件都必须满足。
must_not - 必须不符合要求, 相当于java中的逻辑运算符 !
should - 有任意条件符合要求即可,相当于java中的逻辑运算符 ||,在should范围内的条件,只要有任意一个满足即可。
range - 数学比较条件。计算数学范围的。
常见的搜索方式在后续案例中演示。
GET /test_index/test_type/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"dept_name": "Sales Department"
}
}
],
"must_not": [
{
"range": {
"num_of_emps": {
"gt": 10
}
}
}
],
"should": [
{
"match": {
"emps.name": "wangwu"
}
},
{
"range": {
"emps.age": {
"lte": 10
}
}
}
]
}
}
}
4.2.1match
根据某条件搜索document。match会根据field对应的analyzer对搜索条件做分词。并在倒排索引中搜索数据。
GET /_search
{
"query" : {
"match" : { "dept_name" : "Sales Department" }
}
}
4.2.2multi match
使用搜索条件,在多个字段中进行全文检索,搜索document数据。也会将搜索条件使用字段的analyzer做分词。
GET /_search
{
"query" : {
"multi_match": {
"query": "rod",
"fields": ["dept_name", "emps.name"]
}
}
}
4.2.3range
数学比较搜索
GET /_search
{
"query" : {
"range" : {
"emps.age" : {
"gt" : 21, "lte" : 45
}
}
}
}
在ES中range也可以实现日期的比较,如:查询40天以内入职的员工。其语法为:
GET /test_index/_search
{
"query": {
"range": {
"emps.join_date": {
"gte": "2018-08-10||-40d"
}
}
}
}
4.2.4term
词组比较,词组搜索。
忽略搜索条件分词,使用搜索条件进行精确匹配。进行匹配搜索的字段mapping建议手工创建,且type值为keyword。ES在处理keyword类型的字符串时,不进行分词操作。term搜索可执行。ES中对数字,日期,boolean类型数据也不会做分词操作。
在ES5.x版本后,为text类型的field默认会创建一个子字段,子字段命名为field_name.keyword,这个字段是不做分词的,默认字段长度最大256个字符。
GET /_search
{
"query" : {
"term" : {
"dept_name" : "Sales Department"
}
}
}
4.2.5terms
与term含义相同,就是可以比对多个搜索条件数据
GET /_search
{
"query" : {
"terms" : {
"tags" : [ "Sales tag1", "Development tags3" ]
}
}
}
4.2.6复合搜索
在一个请求体中,有多个搜索条件,就是复合搜索。如:搜索数据,条件为部门名称是Sales Department,部门员工数量少于10人,部门员工入职时间晚于2017-01-01,部门员工中一定有人叫zhangsan。上述条件中,部门名称可选,部门员工数量必须满足要求,部门员工入职时间和人名有任意一个条件满足即可。
在执行复合条件搜索的时候,命令也是有内外顺序的。总体来说就是:"bool" : { "must" : { "action" : {} }, "should" : { "action" : {} }, "must_not" : { "action" : {} }, bool: {}}。 bool命令是可以嵌套的。而"action"代表的就是具体的比对方式,如:match,range等。
GET /test_index/_search
{
"query": {
"bool": {
"must": [
{
"range": {
"num_of_emps": {
"lt": 10
}
}
}
],
"should": [
{
"match": {
"dept_name": "Sales Department"
}
},
{
"match": {
"emps.name": "zhangsan"
}
},
{
"range": {
"emps.join_date": {
"gte": "2017-01-01"
}
}
}
]
}
}
}
4.2.7排序
在ES的搜索中,默认是使用相关度分数实现排序的。可以通过搜索语法实现定制化排序。
GET test_index/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"num_of_emps": {
"order": "asc"
}
},
{
"dept_name": {
"order": "desc"
}
}
],
"_source": ["dept_name", "num_of_emps"]
}
注意:在ES中,如果使用字符串类型的字段作为排序依据,可能会有问题,尤其是字段类型为text时,ES需要对字段数据做分词,建立倒排索引。如果使用text类型字段做排序,ES给出的排序结果未必友好,毕竟分词后,先使用哪一个单词做排序是不固定的。如:
PUT /test_sort/sort_type/1
{
"content" : "first content",
"order" : 1
}
PUT /test_sort/sort_type/2
{
"content" : "second content",
"order" : 2
}
GET /test_sort/sort_type/_search
{
"query" : {
"match" : {
"content" : "content"
}
},
"sort" : [
{
"content" : { "order" : "asc" }
}
]
}
此时需要对字段content进行特殊处理,处理如下:为字符串字段增加子字段sf(sort field),这个字段类型为keyword类型(不进行分词),排序时使用这个子字段content.sf作为排序字段即可。在ES5.x版本之后,ES默认为每一个text类型的字段建立一个子字段。子字段类型为keyword,可以实现排序机制。
DELETE /test_sort
PUT /test_sort
{
"mappings": {
"sort_type": {
"properties": {
"content" : {
"type": "text",
"analyzer": "standard",
"fields": {
"sf" : {
"type": "keyword"
}
}
},
"order" : {
"type": "long"
}
}
}
}
}
PUT /test_sort/sort_type/1
{
"content" : "first content",
"order" : 1
}
PUT /test_sort/sort_type/2
{
"content" : "second content",
"order" : 2
}
GET /test_sort/sort_type/_search
{
"query" : {
"match_all" : {}
},
"sort" : [
{
"content.sf" : { "order" : "desc" }
}
]
}
4.2.8scroll搜索
如果需要一次性搜索出10万数据,那么执行效率一定不会很高,这个时候可以使用scroll滚动搜索的方式实现搜索。scroll滚动搜索类似分页,是在搜索的时候,先查询一部分,之后再查询下一部分,分批查询总体数据。可以实现一个高效的响应。
scroll搜索会在第一次搜索的时候保存一个快照,这个快照保存的时长由请求指定,后续的查询会依据这个快照执行再次查询。如果这个过程中,ES中的document发生了变化,是不会影响到原搜索结果的。
scroll搜索时,需要指定一个排序,可以使用_doc实现排序,这种排序性能更高一些。
scroll搜索的2次以后的请求,必须在指定的快照保存时长内发起,且每次请求都需要指定这个快照保存时长。
scroll搜索的返回结果一定会包含一个特殊的结果数据_scroll_id。这个数据就是scroll搜索的快照ID。scroll搜索的2次以后的请求,必须携带上这个ID,否则无法依据ES保存的快照执行后续的搜索。
测试:
GET /t_index/_search?scroll=1m
{
"query": {
"match_all": {}
},
"sort": [ "_doc" ],
"size" : 1
}
GET /_search/scroll
{
"scroll" : "1m",
"scroll_id" : "根据具体返回结果替换"
}
scroll不是分页,不是用来替换分页搜索的技术。一般来说,分页是为用户提供数据的,scroll是为系统内部处理分批提供数据的。如:reindex 重建索引。