简介
在 ES 中,提供了十分丰富的 DSL 查询。
DSL 查询使用 JSON 格式的请求体与 ES 交互,可以实现各种各样的查询需求。
常见的查询有:
- 查询所有:查询出所有文档,测试时才会用。如 match_all。
- 全文检索查询:利用分词器对输入的内容分词,然后去匹配倒排索引。如 match、multi_match。
- 精确查询:根据精确词条查询 keyword、数值、日期等。如 term、range、terms。
- 地理信息查询:根据经纬度 geo 查询。
- 复合查询:将上述简单查询组合起来查询。如 bool、function_score。
基本语法
使用 DSL 查询,需用到 query 参数。
基本的语法格式:
GET /索引库/_search
{
"query": {
"查询类型": {
"查询条件": "值"
}
}
}
空查询
没有查询条件的查询,就是空查询。它会匹配所有的文档。
GET /user/_search
全文检索查询
全文查询语句用于全文本字段 text 的分词查询。
match
示例:查询 full_name 字段中包含 John 或 Smith 的文档。
GET /user/_search
{
"query" : {
"match" : {
"full_name" : "John Smith"
}
}
}
说明: ES 会先使用分词器拆分 "John Smith" 为两个独立的词条 "John" 和 "Smith",然后再去匹配倒排索引。
这里的 full_name 字段可以替换为任何你想查询的字段甚至是 all 字段。
match 属于 boolean 的类型,也就是说,分析器会对提供的查询文本进行分析并构建 boolean 查询语句。
由 match 构建的 boolean 查询语句默认是逻辑或(or),当然,我们可以通过 operator 参数来改变这个默认行为。
示例:查询 full_name 字段中包含 John 和 Smith 的文档。
GET /user/_search
{
"query": {
"match" : {
"full_name" : {
"query" : "John Smith",
"operator" : "and"
}
}
}
}
说明: full_name 是字段名称,query 参数的值是要查询的文本,operator 参数用于设置 match 的逻辑(or 还是 and)。
match_phrase
match_phrase,即短语(词组)匹配,它会分析提供的查询文本并构建一个 phrase 查询。
match_phrase 用于精确的 phase 匹配。
示例:查询 full_name 字段中包含 "John Smith" 短语的文档。
GET /_search
{
"query" : {
"match_phrase" : {
"full_name" : "John Smith"
}
}
}
可以给提供的查询文本指定 analyzer(分词器),如果没有指定,默认使用字段的显式 mapping 中的定义,或者默认的 search analyzer。
GET /user/_search
{
"query": {
"match_phrase" : {
"full_name" : {
"query" : "John Smith",
"analyzer" : "standard"
}
}
}
}
match_phrase_prefix
match_phrase_prefix 和 match_phrase 类似,但 match_phrase_prefix 不是精准匹配,而是前缀匹配。
示例:查询 full_name 字段中以 "John Smi" 短语开头的文档。
GET /user/_search
{
"query" : {
"match_phrase_prefix" : {
"full_name" : "John Smi"
}
}
}
multi_match
multi_match 用于多字段匹配查询。
性能较低,建议使用 copyto 合并到 all 字段,然后使用 match。
GET /user/_search
{
"query" : {
"multi_match": {
"query": "John Smith",
"fields": ["first_name", "full_name"]
}
}
}
Term-level 查询
Term-level 查询是直接查询倒排索引中的精确的值。
Term-level 查询用于精确类型的用户输入查询,如数值、日期、枚举值或关键字,而不是文本(text)。
它不会对用户的输入分词。
term
term 用于查询指定字段的倒排索引包含某个确切值的记录。
POST /user/_search
{
"query": {
"term" : {
"first_name" : "john"
}
}
}
terms
terms 和 term 类似,只不过提供的确切的值是数组。类似于 MySQL 的 in 条件。
POST /user/_search
{
"query": {
"terms" : { "city" : ["bj", "sz"] }
}
}
range
range 用于范围查询。
GET /user/_search
{
"query": {
"range" : {
"age" : {
"gte" : 28,
"lt" : 60
}
}
}
}
prefix
prefix 返回字段的 term 以确切的前缀(前缀不会被分词)开头的记录。
GET /user/_search
{
"query": {
"prefix" : { "full_name" : "joh" }
}
}
wildcard
wildcard 指的是通配符查询。
支持的通配符主要有:
- *:匹配 0 个或多个任意字符
- ?:匹配 1 个任意字符
GET /user/_search
{
"query": {
"wildcard" : { "full_name" : "john*" }
}
}
regexp
regexp 指的是正则查询。
GET /user/_search
{
"query": {
"regexp" : { "full_name" : "jo.*" }
}
}
ids
ids 查询指的是根据文档的 id 来查询。
GET /user/_search
{
"query": {
"ids" : { "values" : ["1", "2", "60"] }
}
}
复合查询
复合查询是将多个简单查询组合起来,实现更复杂的查询逻辑。
function_score
算分函数查询,可以人为控制文档的相关性评分,控制文档排名。如百度竞价排名。
- query 简单查询语句,匹配的文档都有一个相关性评分 score
- functions 算分函数,可对满足过滤条件的文档,额外计算一个分数
- boost_mode 加权模式,指定两种分数的计算逻辑。如 sum, multiply
bool
bool 复合查询是将多个简单查询(子查询)进行组合。
组合的方式有以下几种:
- must 必须匹配内部的每个子查询。类似 and。
- should 匹配内部的任意一个子查询。类似 or。
- must_not 必须不匹配。不进行相关性评分,类似取反。
- filter 必须匹配。不进行相关性评分。
说明: must_not 和 filter 只进行条件过滤,不会参与评分,执行效率更高。对于评分没有要求的场景,尽量使用 must_not 和 filter。
通常,全文搜索或需要用到相关性评分的场景采用 must 或 should,其他的全部用 filter。
POST /user/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"last_name": "smith"
}
}
],
"should": [
{
"term": {
"full_name": "john"
}
},
{
"term": {
"full_name": "smith"
}
}
],
"filter": [
{
"term": {
"": "musics"
}
}
],
"must_not": [
{
"range": {
"": {
"gte": 10,
"lte": 25
}
}
}
],
"minimum_should_match": 1
}
}
}
查询与过滤
DSL 查询根据使用目的的不同分为两种类型:
- 上下文查询(Query context),简称查询
- 上下文过滤(Filter context),简称过滤
查询(Query)
在查询语境中,查询语句会判断文档是否匹配并计算相关性评分(_score)。
过滤(Filter)
过滤主要用于精确的字段查询。
例如:age 字段的值是否在 20-35 范围内?
一般来说,过滤语句比查询语句的执行效率更高,因为它不用计算文档的相关性评分(score)。
过滤语句的结果会被 ES 自动缓存,以提高性能。
过滤的目的就是粗暴地快速缩小匹配的结果集。
在进行搜索时,常常会在查询语句中,结合查询和过滤来达到我们的查询目的。
搜索结果处理
分页
ES 默认只会返回 top10 的文档。
可通过 from 和 size 参数实现分页查询。
sort 参数可实现自定义排序。
深度分页问题
ES 是分布式的,数据是分片存储的,在 ES 集群中,会面临深度分页问题。
如果搜索页数过深或者结果集(from + size)过大,对内存和 CPU 的影响就越大。
比如:每页 10 条,第 51 页就是,from=500, size=10。在 ES 集群中的实现是,先从每个分片上查询前 510 条,然后将所有节点的结果聚合,重新排序选出 510 条,最后从这 510 条数据中,选取最后的 10 条返回。
因此,ES 默认设定单机查询结果集的上限是 10000 条。也就是说 from + size <= 10000。
使用场景: 百度、京东的随机翻页搜索。
除了在业务侧进行强行限制外,针对深度分页问题,ES 也提供了一种解决方案。
search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。因此,它不支持随机翻页,只能向后翻页。
使用场景: 手机端的向下滚动翻页。
高亮
高亮:就是在搜索结果中把搜索关键字突出显示。
实现原理:
- 由 ES 将搜索结果中的关键字用标签(默认是
<em></em>
)包裹起来。可通过 highlight 参数实现。 - 在前端页面中,给该标签添加 css 样式。