Elasticsearch DSL查询常用语法,es构建复杂查询条件

6,146 阅读7分钟

1. 数据准备

  • 1.1 先新建索引index,索引名称:shop
  • 1.2 添加以下字段
{
    "properties": {
        "id": {
            "type": "long"
        },
        "age": {
            "type": "integer"
        },
        "username": {
            "type": "keyword"
        },
        "nickname": {
            "type": "text",
            "analyzer": "ik_max_word"
        },
        "money": {
            "type": "float"
        },
        "desc": {
            "type": "text",
            "analyzer": "ik_max_word"
        },
        "sex": {
            "type": "byte"
        },
        "birthday": {
            "type": "date"
        },
        "face": {
            "type": "text",
            "index": false
        }
    }
}

HTTP POST请求创建以上字段

POST /shop/_mapping HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 654

{
    "properties": {
        "id": {
            "type": "long"
        },
        "age": {
            "type": "integer"
        },
        "username": {
            "type": "keyword"
        },
        "nickname": {
            "type": "text",
            "analyzer": "ik_max_word"
        },
        "money": {
            "type": "float"
        },
        "desc": {
            "type": "text",
            "analyzer": "ik_max_word"
        },
        "sex": {
            "type": "byte"
        },
        "birthday": {
            "type": "date"
        },
        "face": {
            "type": "text",
            "index": false
        }
    }
}
  • 1.3 添加测试数据

HTTP POST请求

POST /shop/_doc/1001 HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 335

{
    "id": 1001,
    "age": 18,
    "username": "zhangsan",
    "nickname": "张三",
    "money": 199.8,
    "desc": "我是张三,我现在在学习Elasticsearch",
    "sex": 0,
    "birthday": "2003-09-01",
    "face": "http://www.imooc.com/static/img/index/logo.png"
}

完整的post请求链接为:http://192.168.10.235:9200/shop/_doc/1001

链接末尾的1001与json中的id:1001最好对应上。

按此格式,添加多条用于测试的数据即可。

2. 简单数据查询

  • 2.1 GET 请求查询数据(无条件,查询全部)
http://192.168.10.235:9200/shop/_search
  • 2.2 GET 请求查询数据(添加查询条件)
一个查询条件
http://192.168.10.235:9200/shop/_search?q=nickname:张三
多个查询条件
http://192.168.10.235:9200/shop/_search?q=nickname:张三&q=desc:学习
  • 2.3 POST 请求查询数据
POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 79

{
    "query": {
        "match": {
            "nickname": "张三"
        }
    }
}

post请求链接固定:http://192.168.10.235:9200/shop/_search

搜索条件写在json中,match为es中的关键字,如果你写错,执行请求就会报错。

3. DSL搜索-全部查询、分页查询

  • 3.1 GET 请求
http://192.168.10.235:9200/shop/_search
  • 3.2 POST 请求,使用es关键字 match_all
POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 48

{
    "query": {
        "match_all": {}
    }
}

查询全部建议使用post方式,因为后期如果需要添加分页、过滤等功能,get请求链接会越来越复杂。

  • 3.3 只返回指定字段,使用 _source
POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 120

{
    "query": {
        "match_all": {}
    },
    "_source": [
        "id",
        "nickname",
        "age"
    ]
}

这样查询结果就只会返回 id、nickname、age 三个指定的字段。

  • 3.4 分页查询,使用 from、size
POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 151

{
    "query": {
        "match_all": {}
    },
    "_source": [
        "id",
        "nickname",
        "age"
    ],
    "from": 0,
    "size": 10
}

from:从第几条数据开始

size:一页返回多少条数据

4. DSL搜索-不进行分词搜索 term

match在查询时,是进行了分词的操作

而term是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 77

{
    "query": {
        "term": {
            "desc": "学习"
        }
    }
}

搜索在desc中包含 “学习” 的数据

如果需要匹配多个词,则可以使用 terms

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 77

{
    "query": {
        "terms": {
            "desc": ["在","学习"]
        }
    }
}

5. DSL搜索-临近搜索词 match_phrase

现在有两条数据,desc 分别为:

第一条:“你大学已经毕业了吗?”

第二条:“我在大学毕业后,考研究生去了”

现在使用 match_phrase 来进行查询

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 129

{
    "query": {
        "match_phrase": {
            "desc": {
                "query": "大学 毕业"
            }
        }
    }
}

此时只能查询出来第二条数据,第一条数据是匹配不到的,

match_phrase 要求 大学 的后面,必须是 毕业 两个字,只有满足“大学毕业”的才算符合条件;

那怎样才能同时查询出第一条和第二条数据呢

可以使用关键字 slop,即中间跳过一定的字符

比如第一条 大学毕业 中间隔了两个字符,使用 "slop": 2

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 156

{
    "query": {
        "match_phrase": {
            "desc": {
                "query": "大学 毕业",
                "slop": 2
            }
        }
    }
}

只要 slop 大于等于2,两条数据都能查询出来。

关于 match_phrase 具体解释和用法,也可以看官方文档 短语匹配

5. DSL搜索-操作符 operator

先看请求

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 154

{
    "query": {
        "match": {
            "desc": {
                "query": "在学习",
                "operator": "or"
            }
        }
    }
}

我们知道 match 会进行分词操作,在查询时,会将 “在学习” 分成 “在”“学习” 两个词进行检索匹配

使用or,则会查询出在desc中包含 “在” 或包含 “学习” 的数据

如果使用 and ("operator": "and"),则只会查询出在desc中包含 “在学习” 的数据

如果 query 条件是一句话,比如:“让开发者成为一个令人尊敬的职业”,此时则会分词成很多个词,匹配到的数据也有很多,使得查询精度不够高,

这时就可以是用 minimum_should_match 来提高匹配精度。

我们先看看es会将 “让开发者成为一个令人尊敬的职业” 这一句话分成多少个词?

POST /_analyze HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 64

{
    "analyzer": "ik_max_word",
    "text": "让开发者成为一个令人尊敬的职业"
}

查询结果显示是分成了 13 个词,所以如果是直接去匹配的话,则会查询出来很多数据

minimum_should_match 代表最小被匹配到的

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 167

{
    "query": {
        "match": {
            "desc": {
                "query": "让开发者成为一个令人尊敬的职业",
                "minimum_should_match": "60%"
            }
        }
    }
}

这里 60% 则表示,搜索结果要满足 13 个词的60%(含)以上,即 13*0.6=7.8,向下取整为7

需要包含7个(包括7)以上的词,而不是只包含其中的一个词。

这里不仅可以写百分比,也可以直接写数字,比如:"minimum_should_match": "3"

则表示是包含 3 个(包括3)词以上的

6. DSL搜索-根据id查询数据 ids

这个比较简单,使用关键字 ids 即可,可以查询多个

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 164

{
    "query": {
        "ids": {
            "type": "_doc",
            "values": [
                "1001",
                "1006"
            ]
        }
    }
}

values数组中填写要查询的id就行。

7. DSL搜索-多重查询 multi_match 和提高指定字段权重

多重查询的意思就是使用多个字段去做查询

现在有这两条数据:

我要查询 nicknamedesc 都包含 沙僧 的数据

使用关键字 multi_match

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 175

{
    "query": {
        "multi_match": {
            "query": "沙僧",
            "fields": [
                "nickname",
                "desc"
            ]
        }
    }
}

提高指定字段的权重,使用 ^

比如我要提高nickname在查询中的权重,则只需要在 nickname 后加上 ^数字

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 175

{
    "query": {
        "multi_match": {
            "query": "沙僧",
            "fields": [
                "nickname^10",
                "desc"
            ]
        }
    }
}

这样查询出来的结果排序和默认的则不一样了。

而且返回的数据中 max_score_score 的值也有变化

7. DSL搜索-组合查询 bool

将多个条件组合在一起

  • 7.1 must:是指要同时满足下面的两个条件
POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 481

{
    "query": {
        "bool": {
            "must": [
                {
                    "multi_match": {
                        "query": "沙僧",
                        "fields": [
                            "nickname",
                            "desc"
                        ]
                    }
                },
                {
                    "term": {
                        "sex": 0
                    }
                }
            ]
        }
    }
}
  • 7.2 should:下面的两个条件,满足一个就行
{
    "query": {
        "bool": {
            "should": [
                {
                    "multi_match": {
                        "query": "沙僧",
                        "fields": [
                            "nickname",
                            "desc"
                        ]
                    }
                },
                {
                    "term": {
                        "sex": 0
                    }
                }
            ]
        }
    }
}
  • 7.3 must_not:下面的两个条件,都不能满足,和 must 相反
{
    "query": {
        "bool": {
            "must_not": [
                {
                    "multi_match": {
                        "query": "沙僧",
                        "fields": [
                            "nickname",
                            "desc"
                        ]
                    }
                },
                {
                    "term": {
                        "sex": 0
                    }
                }
            ]
        }
    }
}
  • 7.4 mustmust_notshould 可以同时使用
POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 771

{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "desc": "大学"
                    }
                }
            ],
            "must_not": [
                {
                    "term": {
                        "sex": "1"
                    }
                }
            ],
            "should": [
                {
                    "range": {
                        "age": {
                            "gt": "18",
                            "lt": "22"
                        }
                    }
                },
                {
                    "match": {
                        "desc": "毕业"
                    }
                }
            ]
        }
    }
}

8. DSL搜索-过滤器 post_filter

将金额money小于100并且大于1000的数据过滤掉

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 226

{
    "query": {
        "match": {
            "desc": "大学"
        }
    },
    "post_filter": {
        "range": {
            "money": {
                "gt": 100,
                "lt": 1000
            }
        }
    }
}

post_filterquery 是同级的

9. DSL搜索-排序 sort

es默认是按 _score 排序

正序asc

倒序desc

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 144

{
    "query": {
        "match": {
            "desc": "大学"
        }
    },
    "sort": [
        {
            "age": "asc"
        }
    ]
}

多个排序条件,组合排序

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 193

{
    "query": {
        "match": {
            "desc": "大学"
        }
    },
    "sort": [
        {
            "age": "asc"
        },
        {
            "money": "desc"
        }
    ]
}

注意:text类型无法排序,keyword类型可以

还记得在1.2步骤中创建的字段类型吗

"username": {
	"type": "keyword"
},
"nickname": {
	"type": "text",
	"analyzer": "ik_max_word"
}

username 是可以排序的,但是 nickname 不行。使用 nickname 排序会报错!

那怎样才能即使用 text 类型,又能进行排序呢?

我们按下面json再添加一个测试字段

POST /shop/_mapping HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 256

{
    "properties": {
        "nickname_test": {
            "type": "text",
            "analyzer": "ik_max_word",
            "fields": {
                "keyword": {
                    "type": "keyword"
                }
            }
        }
    }
}

这个 fields 就是 nickname_test 字段的附属属性

这个时候如果想使用 nickname_test* 进行排序,就不能按照这种方式写了

{
	"nickname_test": "asc"
}

而应该使用 nickname_test 的附属属性 keyword 进行排序

{
	"nickname_test.keyword": "asc"
}

10. DSL搜索-关键字高亮显示 highlight

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 157

{
    "query": {
        "match": {
            "desc": "沙僧"
        }
    },
    "highlight": {
        "fields": {
            "desc": {}
        }
    }
}

返回的数据中会对搜索关键字增加一个 em 标签

"highlight": {
	"desc": [
		"唐三藏是<em>沙僧</em>的师傅"
	]
}

我们也可以自定义这个标签,使用 pre_tagspost_tags

POST /shop/_search HTTP/1.1
Host: 192.168.10.235:9200
Content-Type: application/json
Content-Length: 267

{
    "query": {
        "match": {
            "desc": "沙僧"
        }
    },
    "highlight": {
        "pre_tags": [
            "<span>"
        ],
        "post_tags": [
            "</span>"
        ],
        "fields": {
            "desc": {}
        }
    }
}

这样返回的数据中,em标签 则改成了 span

"highlight": {
	"desc": [
		"唐三藏是<span>沙僧</span>的师傅"
	]
}