前言
对于索引映射的定义一般只需要定义一次,在日常工作中,我们更多的时候是在ElasticSearch(简称ES)中查询数据,那么我们可以如何在ES中处理多条件查询语句呢?ES中一共提供了哪些查询的形式给我们实现搜索呢?同时,检索出来的数据,又是通过什么方式来进行排序的呢?本文将围绕ElasticSearch查询相关的API,对上述问题进行一一讲解。希望能够给各位读者参考。
一、ElasticSearch 检索概述
实现对ES中存储的数据进行查询分析,使用的关键字是_search
,比如下面的API
GET /_search 在根目录下检索所有index
GET //_search 在指定索引下检索所有文档
GET /,/_search 检索指定多个索引下的所有文档
GET /my_*/_search 和上面一条一样,区别在于这一条命令使用了通配符
- 查询的两种形式
ES给使用者提供了URI Search 和 Request Body Search 两种方式来进行查询,下面将对两种查询方式的特点进行简单介绍:
URI Search:操作简便,方便通过命令行测试-仅包含部分查询语法
Request Body Search:ES为这种查询方式提供了完备查询语法Query DSL
(Domain Specific Language)
二、URI Search
(一)常用参数
参数 | 含义 |
---|---|
q | 指定查询的语句,语法为Query String Syntax |
df | q中不指定字段时默认查询的字段,如果不指定,es 会查询所有字段 |
sort | 排序 |
timeout | 指定超时时间,默认不超时 |
from,size | 用于分页 |
小例子:查询job字段中包含java的文档,结果根据age属性进行升序排序,返回第1个文档,如果超过1s没有结束,则以超时结束。
PUT /search_index/_doc/1
{
"name" : "java coder",
"hobby" : "coding",
"job" : "java enginger",
"age" : 4
}
PUT /search_index/_doc/2
{
"name" : "xiaoming",
"hobby" : "coding",
"job" : "java enginger",
"age" : 16
}
PUT /search_index/_doc/3
{
"name" : "xiaoming",
"hobby" : "coding",
"job" : "java enginger",
"age" : 16
}
PUT /search_index/_doc/4
{
"name" : "xiaoming",
"hobby" : "talking",
"job" : "teacher",
"age" : 20
}
PUT /search_index/_doc/5
{
"name" : "xiaoming",
"hobby" : "work",
"job" : "worker",
"age" : 11
}
GET /search_index/_search?q=java&df=job&sort=age:desc&from=0&size=1&timeout=1s
(二)Query URI查询详讲
1. term与phrase (词匹配和词语匹配)
# 检索数据和上面的保持一致
GET /search_index/_search?q=java xiaoming
多词匹配之间使用空格进行隔开,词语之间的关系为“或”
若想使用词语匹配,需要用双引号括起来
# 检索数据和上面的保持一致
GET /search_index/_search?q="java coder"
2. 泛查询和指定字段查询
泛查询在之前其实就有演示过,即像q = xiaoming
这种不指定检索字段范围,会在所有字段中进行检索的查询即称为泛查询。反之,如果指定了字段,就能大大缩小范围。常见的指定字段的方式为字段:检索条件字符
。
GET /search_index/_search?q=name:java coder
3. 布尔操作符
SearchURI中也可以使用和和Query DSL
类型的布尔操作符,不过具体的使用规则略有不同,具体的规则如下:
Search URI 参数 |
Query DSL 中的等价参数 |
---|---|
AND | 等价于&&,也就是且 |
OR | 等价于||,也就是或 |
NOT | 等价于!,也就是非 |
- | 等价于must_no,和OR的功能差不多 |
+ | 等价于must |
# 新加一个测试数据,便于区别测试
PUT /search_index/_doc/6
{
"name" : "xiaohong",
"hobby" : "coding",
"job" : "enginger",
"age" : 16
}
GET /search_index/_search?q=job:(java AND enginger)
GET /search_index/_search?q=job:(java OR enginger)
GET /search_index/_search?q=job:(NOT java enginger)
GET /search_index/_search?q=job:((NOT java enginger) || (worker -teacher))
需要注意的是,“+”
号在url中会被解析为空格,要使用encode后的结果才可以,我们需要用%2B来替代一下。
4. 范围查询
范围查询支持数据范围和日期范围的查询,具体的表达式写法有两种方式:
- 区间写法,闭区间用
[ ]
,开区间用{ }
(开区间这种写法可能有些版本的ES不支持了)
区间写法测试用例 - 数值写法,类似于我们sql的正常写法
GET /search_index/_search?q=age:(>=16 AND <=20)
5. 通配符查询
GET /search_index/_search?q=name:ja??
GET /search_index/_search?q=name:ja*
Query URI
支持我们使用通配符来进行模糊查询,?
表示一个字符,*
表示一个或者多个字符
注意:通配符匹配执行效率低,且占用较多内存,数据较多时不建议使用
如无特殊需求,不要将?/*
放在最前面(因为会加大匹配的范围,降低匹配效率)
6. 正则表达式匹配
Query URI
支持我们使用正则表达式来进行检索
GET /search_index/_search?q=name:/jav[abcd]/
7. 近似值匹配和模糊匹配
当我们对于检索条件字符不明确时,Query URI
支持我们使用近似值来作为模糊查询的条件,这个和之前的通配符不太一样,通配符很多时候是基于我们清楚某一段字符中的某个区间内容了(比如说最终的结果是xiaoming
,我们可以根据知道的字符进行匹配,比如xiao*
、*ming
)。而近似值匹配可以是众多模糊匹配的并集。
- 近似值匹配 fuzzy query
GET /search_index/_search?q=name:jave~1
- 模糊值匹配 proximity search
相比于近似值匹配来说,模糊值匹配的计量单位从字符变成了单词
PUT /search_index/_doc/7
{
"name" : "java coder master",
"hobby" : "coding",
"job" : "java enginger",
"age" : 4
}
GET /search_index/_search?q="java master"~1
三、Request Body Search
Request Body Search
字如其意,就是通过在body
中添加请求体来实现搜索。我们在第二节中学习了Query URI
的众多语法可能会觉得它已经很强大,但实际上第三节的将要介绍的Query DSL
会更加强大和灵活,不仅支持URI写法的所有语法,还支持更多高阶的语法查询。也是我们日常工作中更为常用的一种检索方式。
(一)Query DSL的简单使用
(二)字段类查询
字段类的查询可以分为全文匹配和单词匹配,两者的区别在于会不会对查询语句进行分词后处理,全文匹配会(match、match_phrase),单词匹配(term、terms、range)不会。
1. match 全文匹配
GET /search_index/_search
{
"query": {
"match": {
"name": {
"query": "java coder",
"minimum_should_match": 2
}
}
}
}
GET /search_index/_search
{
"query": {
"match": {
"name": {
"query": "java coder",
"operator": "and"
}
}
}
}
2. Match Phrase Query 字段匹配
该方法用于对字段作检索,对字段有顺序要求
PUT /search_index2/_doc/1
{
"name": "sun wukong",
"age": 100
}
PUT /search_index2/_doc/2
{
"name": "wukong sun",
"age": 100
}
PUT /search_index2/_doc/3
{
"name": "sun wukong test",
"age": 100
}
GET /search_index2/_search
{
"query": {
"match_phrase": {
"name": {
"query": "sun test",
"slop": 1
}
}
}
}
3. Query String Query
Query String Query
其实就相当于Query URI
的请求体写法
GET /search_index/_search
{
"query": {
"query_string": {
"fields": ["name"],
"query": "java AND coder OR (master loser)"
}
}
}
4. Simple Query String Query 简单匹配
Simple Query String Query
类似于Query String,但是会忽略错误的查询语法,并且仅支持部分查询语法。其常用的逻辑符号如下,不能使用AND、OR、NOT等关键词,而是要对应地使用+
|
-
。
GET /search_index/_search
{
"query": {
"simple_query_string": {
"query": "java NOT coder",
"fields": ["name"]
}
}
}
GET /search_index/_search
{
"query": {
"simple_query_string": {
"query": "java -coder",
"fields": ["name"]
}
}
}
5. Term Query 全词匹配
Term Query
将查询语句作为整个单词进行查询,即不对查询语句做分词处理
GET /search_index/_search
{
"query": {
"term": {
"name": {
"value": "java coder"
}
}
}
}
6. Terms Query 多全词匹配
terms query
和term query
类似,差别在于后者可以同时支持多个全词匹配
GET /search_index/_search
{
"query": {
"terms": {
"name": [
"java coder",
"xiaoming"
]
}
}
}
7. Range Query 范围匹配
Query DSL
支持我们使用Range Query
来使用范围匹配
GET /search_range_index/_search
{
"query": {
"range": {
"birth": {
"gte": "1995-01-01"
}
}
}
}
拓展:
(三)复合查询
我们可以发现,之前列举的字段类查询只能够满足单个字段查询条件的使用,当涉及到多个字段时,字段类查询的match
和term
等API就无能为力了。满足这种多字段检索需要的方式是使用复合查询。
·复合查询是指包含字段类查询或复合查询的类型,主要包括以下几类∶constant_score query
、bool query
、dis_max query
、function_score query
、boosting query
这里的话,主要讲一下布尔查询Bool Query
API | 含义 |
---|---|
filter | 只过滤符合条件的文档,不计算相关性得分 |
must | 文档必须符合must中的所有条件,会影响相关性得分 |
must_not | 文档必须不符合must_not中的所有条件 |
should | 文档可以符合should 中的条件,会影响相关性得分 |
- Bool Query Filter
GET /search_index/_search
{
"query": {
"bool": {
"filter": {
"term": {
"hobby": "coding"
}
}
}
}
}
- Bool Query Must和Must_no
GET /search_index/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "xiaoming"
}
},
{
"match": {
"hobby": "coding"
}
}
]
}
}
}
- Bool Query Should
Should使用分两种情况:
bool查询中只包含should,不包含must查询以及同时包含should和must查询。我们知道只要文档中数据满足任意一个should条件,那么就可以作为结果呈现出来,相关性算分作为呈现结果顺序的重要依据,满足查询的条件越多,相关性算分会越高
Bool Query Should的测试用例
(四)Count API
当我们不需要获取详细的文档信息,只需要获取命中的文档数量时,就可以使用count来获取我们想要的结果。
GET /search_index/_search
{
"query": {
"terms": {
"name": [
"java coder",
"xiaoming"
]
}
}
}
(五)Source Filter
当我们只需要获取命中文档的指定字段值时,我们可以通过source来指定我们需要es返回的值
GET /search_index/_search
{
"query": {
"terms": {
"name": [
"java coder",
"xiaoming"
]
}
},
"_source": ["name","job"]
}