es第五篇:Query DSL

一、Full text queries

The high-level full text queries are usually used for running full text queries on full text fields like the body of an email. They understand how the field being queried is analyzed and will apply each field’s analyzer (or search_analyzer) to the query string before executing.

包括match query、match_phrase query、match_phrase_prefix query、multi_match query、common terms query、query_string query、simple_query_string query。

在学习各种query时,可以设置query同级的profile属性值为true来了解es具体是如何解析各query命令的。即请求体如下:

{
    "profile": true,
    "query": {

    }
}

match query

get /website/_search
{
    "query":{
        "match": {
            "name": "kobe bean bryant"
        }
    }
}

match query接受text类型、numeric类型(包括byte、short、integer、long、float、double等)、date类型字段的查询。如果查询的字段是text类型的话,就是常说的分词查询。查询关键字会被分词,某文档字段值分词后的词条集只要和查询关键字分词后的词条集有交集,就能被查出来。如果查询的字段是numeric类型、date类型,这里查询关键字就不会分词了,查询关键字和文档字段值完全匹配才能查得出来。

multi_match query

get /website/_search
{
    "query":{
        "multi_match":{
            "query":"kobe bean",
            "fields":["name", "nick_name"]
        }
    }
}

multi_match query,顾名思义,多match查询,这里的多指的是一次可以查询多个字段,而不是指定多个查询关键字。如上面的,name字段值match查询关键字kobe bean的文档可以被查出来,nick_name字段值match查询关键字kobe bean的文档也可以被查出来。

在指定查询字段时,还可以用通配符,如

get /website/_search
{
    "query":{
        "multi_match":{
            "query":"kobe bean",
            "fields":["name", "*_name"]
        }
    }
}

这样,nick_name、first_name、last_name等所有以_name为后缀的字段都会被查询,假如某一个字段的值match查询关键字kobe bean,这个文档就会被查出来。

match_phrase query

get /website/_search
{
    "query": {
        "match_phrase": {
            "name": "kobe bean bryant"
        }
    }
}

在match query的基础上,要求查询关键字在分词后各词条的相对位置(position)要和文档字段值分词后相应词条的相对位置(position)相同,满足这个条件的文档才能被查出来。

假如查询字符串是kobe bean bryant,则分词后会是kobe position 0、bean position 1、bryant position 2,只有同时包含kobe、bean、bryant这三个词条且这三个词条依次紧挨着(中间没有其他词条)的文档才能被查出来,在文档字段值中kobe bean bryant前后有没有其他单词无所谓。例如,通过kobe bean bryant进行match_phrase query可以查询出字段值是john kobe bean bryant peter的文档,也可以查询出@kobe bean bryant@的文档,因为字符串在被standard分析器处理时,@字符会被直接忽略掉。

从另一个角度理解,不考虑分析器处理造成的大小写转换、特殊符号省略的话,只有文档字段值contains查询字符串的文档才会被查出来。

match_phrase_prefix query

get /website/_search
{
    "query": {
        "match_phrase_prefix": {
            "name": "kobe bean b"
        }
    }
}

和match_phrase query差不多,区别是查询关键字最后一个单词可以只写前面一个或几个字符,如用kobe bean b进行match_phrase_prefix query可以查出字段值是kobe bean bryant的文档,也可以查出字段值是kobe bean brother的文档,后者用kobe bean bryant进行match_phrase query查不出来。

query_string query

get /website/_search
{
    "query":{
        "query_string": {
            "default_field": "name",
            "query": "Kobe Bean OR Steven Jobs"
        }
    }
}

query_string query在查询text类型字段和keyword类型字段时,解析是不一样的。

如果查询字段是text类型,query_string query会把查询关键字依据分割符(默认是大写的OR)分割,然后用分割后的子字符串在指定字段上查询。如上,Kobe Bean OR Steven Jobs会被分割成Kobe Bean和Steven Jobs,然后在name字段上查询。Kobe Bean会被standard分析器处理成kobe和bean,Steven Jobs会被standard分析器处理成steven和jobs,name字段值被分析器处理后只要有这4个词条中的一个,该文档就会被查出来。

如果查询字段是keyword类型,则又要根据查询关键字有没有分割符分为两种情况:如果没有分隔符,比如查询关键字是Kobe Bean,这个时候查询关键字不会被任何分析器处理,故此时相当于term query,只有字段值是Kobe Bean的文档才会被查询出来。如果有分割符,比如查询关键字是Kobe Bean OR Steven Jobs,则查询关键字还是先被OR分割成Kobe Bean和Steven Jobs,然后Kobe Bean会被whitespace分析器处理成Kobe和Bean,Steven Jobs会被whitespace分析器处理成Steven和Jobs。之后转化成4个term query,彼此之间是or的关系,只要满足一个,即字段值只要是Kobe、Bean、Steven、Jobs中的任意一个,文档就能被查出来。

以上分析过程是通过设置profile等于true分析出来的。

从上面分析过程可以看出,如果查询字段是text类型,并且查询关键字没有分隔符的话,query_string query和match query效果是一样的。

query_string query还可以像multi_match query一样,一次查询多个字段,只需用fields关键字代替default_field,并且指定一个字段数组即可。

get website/_search
{
    "query": {
        "query_string": {
            "fields" : ["name", "nick_name"],
            "query": "Kobe Bean OR Steven Jobs"
        }
    }
}

simple_query_string query

get website/_search
{
    "query": {
        "simple_query_string": {
            "fields" : ["name", "nick_name"],
            "query": "Kobe Bean OR Steven Jobs"
        }
    }
}

二、Term level queries 

本节讲的查询都是term级别的查询,要求查询字段是不被分析器处理的字段,字段类型有keyword类型、数字类型(byte、short、integer、long、float、double等)、date类型。

term query

get /_search
{
    "query":{
        "term":{
            "name":"as"
        }
    }
}

term query用于精确查询,要查询的关键字不会被分析器处理,要求字段值完全等于关键字。注意关键字的大小写,如果大小写不匹配的话,也不会查询出来的。

terms query

get /_search
{
    "query":{
        "terms": {
            "name": ["as",  "head"]
        }
    }
}

terms query和term query作用一样,除了要查询的关键字可以是多个,只要字段值等于任意一个关键字,就能查出来。

range query

get /website/_search
{
    "query":{
        "range":{
            "age":{
                "gt":30,
                "lt":40
            }
        }
    }
}

范围查询。查询的字段需是keyword类型、数字类型、date类型。底层构建的lucene query随查询字段类型不同而不同,如果查询字段是keyword类型,则构建一个TermRangeQuery,如果查询字段是数字类型或者date类型,则构建一个NumericRangeQuery。用gt、gte、lt、lte来表示大于、大于等于、小于、小于等于。

exists query

get /website/_search
{
    "query":{
        "exists":{
            "field":"birthday"
        }
    }
}

查询birthday字段值不为null的文档。注意,空字符串也算是不为null。如果查询的字段是数组类型,则要求数组不能是空数组,即至少有一个非null值,否则不会被查出来。

数组类型:

put /website/blog/5
{
    "id" : "5",
    "name" : "Heisenberg",
    "age" : 27,
    "desc" : "A normal soft engineer",
    "hobbies":["swimming","pingpong"]
}

put /website/blog/6
{
    "id" : "6",
    "name" : "James Gosling",
    "age" : 55,
    "desc" : "Father of Java",
    "hobbies":[]
}

如上,hobbies字段是数组类型,元素类型是字符串。

get /website/_search
{
    "query":{
        "exists": {"field": "hobbies"}
    }
}

只能查出来id=5的这条文档,id=6的这条文档因为hobbies值是空数组,所以查不出来。

missing query

不像其他的query,missing query没有missing关键字,而是在bool里面嵌套must_not,再在must_not里面嵌套exists

get /website/_search
{
    "query":{
        "bool":{
            "must_not":[
                {
                    "exists":{
                        "field":"hobbies"
                    }
                }
            ]
        }
    }
}

与exists query相反,missing query查询字段值为null的文档。如果查询字段类型是数组,那么也会把值为空数组的文档查出来。

prefix query

get /website/_search
{
    "query":{
        "prefix":{
            "name.keyword":{
                "value":"Kobe"
            }
        }
    }
}

查找name字段值以Kobe开头的文档。

前缀查询。prefix query在底层映射成lucene的PrefixQuery。

wildcard query

get /website/_search
{
    "query":{
        "wildcard": {
            "name.keyword": {
                "value": "Ko*e"
            }
        }
    }
}

查询name字段值以Ko开始,以e结尾的文档。

通配符查询。*代表任意个字符,?代表任意单个字符。wildcard query在底层映射成WildcardQuery。

regex query

get /website/_search
{
    "query":{
        "regexp":{
            "name.keyword":"Kobe.*"
        }
    }
}

正则查询。regexp是regular expression的简写,regular前三个字符,expression前三个字符。查询关键字中可以有正则表达式。

^表示以什么开始,$表示以什么结束。

ES的正则查询匹配模式相当于java中的matches,本身就是以第一个字符开始,以最后一个字符结尾,所以在ES的正则查询中不能用^、$,否则会去查询以"^"开头、以"^"结尾的字符串。

.可以代表任意一个字符,如s.d可以匹配sad、sbd、scd等

?表示前面的字符最多重复1次,如s?b可以匹配b、sb

+表示前面的字符至少要有1次,如sa+b可以匹配sab、saab、saaab等

*表示前面的字符可以有任意次,如s*b可以匹配b、sb、ssb、sssb等

{2}表示{前面的字符要重复2次,如s{2}b表示ssb

{2,}表示{前面的字符至少重复2次,如s{2,}b可以匹配ssb、sssb、sssb等

{2,3}表示{前面的字符最少重复2次,最多重复3次,如s{2,3}b可以匹配ssb、sssb

上面的+*?{}表示的是前面1个字符的重复次数,如果想处理前面多个字符的重复呢,就应该用括号了。用括号把要处理重复的字符包起来,配合+*?{}就行了。

(xy)+表示xy至少要有1次,如s(xy)+b可以匹配sxyb、sxyxyb、sxyxyxyb等

(xy)*表示xy可以有任意次,如s(xy)*可以匹配s、sxy、sxyxy等

(xy){2}表示xy要有2次,如s(xy){2}表示sxyxy

| 表示或的意思,只要竖线左边或者右边有一个匹配,就算匹配。如sa+可以匹配saa,sa?不能匹配saa,但是sa+|sa?可以匹配saa。

[]表示内部的多个字符的某一个,如

[abc]可以匹配a、b、c

[a-g]可以匹配a、b、c、d、e、f、g,即可以匹配a到g的任意一个字符

在[]中,^表示非的意思,如

[^abc]可以匹配a、b、c之外的任意一个字符,如可以匹配d、e、f等

[^a-g]可以匹配a到g之外的任意一个字符,如可以匹配h、i、j等

案例:假设在indicator_item索引中有一个keyword类型的name字段,有些文档的name值有括号,如*红细胞(RBC),有些文档的值没括号,如尿酸,那么怎么把name值有括号的文档查出来呢?

get /indicator_item/_search
{
    "query":{
        "regexp":{
            "name":{
                "value":".*\(.*\).*"
            }
        }
    }
}

.*表示任意字符串,第二个是用来转义(的,因为(在正则中是保留字符,第一个是用来转义第二个的,在正则表达式用双引号包裹时,转义符都是成对存在的。这点要切记,因为在复制粘贴的时候,可能会自动加一个或者减一个,要注意这一点,否则问题很难定位出来。

Fuzzy query

get /website/_search
{
    "query":{
        "fuzzy":{
            "name.keyword":"James Goslin"
        }
    }
}

模糊查询。根据莱文斯坦编辑距离查询。莱文斯坦编辑距离是看两个字符串的相似度的,编辑距离越小,相似度越高。

三、Compound queries 

Compound是复合的意思,Compound queries可以认为是复合查询。

bool query

get /website/_search
{
    "query":{
        "bool":{
            "must":[
                {
                    "term":{
                        "name.keyword":"James Gosling"
                    }
                }
            ],
            "filter":[
                {
                    "term":{
                        "age":55
                    }
                }
            ],
            "must_not":[
                {
                    "range":{
                        "age":{
                            "gte":0,
                            "lt":18
                        }
                    }
                }
            ],
            "should":[
                {
                    "match":{
                        "desc":"java"
                    }
                },
                {
                    "match":{
                        "desc":"go"
                    }
                }
            ],
            "minimum_should_match":1
        }
    }
}

bool query是由must子句、filter子句、should子句、must_not子句复合起来的,这几个子句可以随意组合,如只有must,只有must、should等。

must是必须满足的条件,must_not是必须不能满足的条件,filter也是必须满足的条件,filter和must的不同之处在于,filter不会影响文档的score,加上filter前和加上filter后,查出来的文档的score是一样的。

should就相当于sql中多个条件的or拼接,如(age = 20 or name = 'James Gosling')可以表示为

get /website/_search
{
    "query":{
        "bool":{
            "should":[
                {
                    "term":{
                        "age":"20" 
                    }
                },
                {
                    "term":{
                        "name.keyword":"James Gosling"
                    }
                }
            ],
            "minimum_should_match": 2
        }
    }
}

should需要和minimum_should_match配合使用。minimum_should_match指定should条件数组中至少要满足几个。minimum_should_match值可以是整数,也可以是百分数。是整数时,最小值是1,即使我们指定小于1的值,es服务端也会认为值是1。

四、Joining queries

nested query

嵌套查询。用于在嵌套类型的字段上查询。

如给website添加一个字段position,类型是nested,里面有经度和维度两个属性

put /website/_mapping/blog
{
    "properties":{
        "position":{
            "type":"nested",
            "properties":{
                "longitude":{
                    "type":"keyword"
                },
                "latitude":{
                    "type":"keyword"
                }
            }
        }
    }
}

nested类型字段设置映射的格式如上,除了使用type属性指定nested类型外,还使用properties属性,在这个内层的properties中定义被嵌套字段的映射。

往website中添加两条数据:

put /website/blog/11
{
    "position":{
        "longitude":"121°38′E",
        "latitude":"25°2′N"
    }
}

put /website/blog/12
{
    "position":{
        "longitude":"120°38′E",
        "latitude":"35°2′N"
    }
}

根据position.longitude = '121°38′E' and position.latitude = '25°2′N'查询

get /website/_search
{
    "query":{
        "nested":{
            "path":"position",
            "query":{
                "bool":{
                    "must":[
                        {
                            "term":{
                                "position.longitude.keyword":"121°38′E"
                            }
                        },
                        {
                            "term":{
                                "position.latitude.keyword":"25°2′N"
                            }
                        }
                    ]
                }
            }
        }
    }

用path指定要查询的nested字段,必需。

如果要查的索引中没有path指定的字段,或者有,但不是nested类型的,则查询会报异常failed to find nested object under path。设置ignore_unmapped属性值为true,就只会查不出来,而不会报错。

nested query还支持多级嵌套。

原文地址:https://www.cnblogs.com/koushr/p/5873428.html