Skip to content

高级操作(Query DSL)

数据模拟

http
# 创建index & mapping
PUT /product
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      },
      "time": {
        "type": "date",
        "format": "yyyyMMdd"
      },
      "name": {
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "category": {
        "type": "keyword"
      },
      "price": {
        "type": "double"
      }
    }
  }
}
# 批量插入数据
POST /product/_bulk
{"create": {"_id": "1001"}}
{"title": "buy apple Phone", "time": 20220910, "name":"苹果手机", "category": "Phone", "price": 4999.00}
{"create": {"_id": "1002"}}
{"title": "buy huawei Phone","time": 20221120, "name":"华为手机", "category": "Phone", "price": 6999.00}

在使用默认的standard分词器时,buy apple Phone 会被分词为buyapplephone,注意Phone -> phone。同样buy huawei Phone也被分词为了buyhuaweiphone

基础查询

搜索条件包含:query(包含过滤、查询条件)、_source(自定义返回字段)、from&size(分页查询)、sort(排序,默认按照score排序)四大模块。

http
# 查询全部数据
POST /product/_search
{
  "query":{
    "match_all": {} // 匹配全部
  },
  "_source": "title", // 只返回source
  "sort": [
    {
      "_id": {      // _id倒序
        "order": "desc"
      }
    }
  ]
}
# 查询全部数据进阶版
POST /{index}/_search
{
  "query":{
    "match_all": {} // 匹配全部
  },
  "_source": {"includes": ["title"], "excludes": ["_i*"]}, // 支持*,只返回includes中的排除了excludes中的字段。
  "sort": [     // 多个排序条件
    {"_id": "desc"},
    "_score" // 默认asc顺序
  ]
}

匹配查询

match
http
# 单个单词查询
POST /product/_search 
{
    "query": {
        "match": {
            "title": "buy" // 通过字段匹配
        }
    }
}
# 多个单词查询 匹配2条(buy apple Phone 和 buy huawei Phone)
POST /{index}/_search
{
	"query": {
		"match": {
			"title": {
				"query": "apple huawei",
				"operator": "or" // 多单词查询需要文档全满足或一个满足
			}
		}
	}
}
# 高亮显示
POST /product/_search
{
  "query": {
    "match": {
      "title": "Phone"
    }
  },
  "highlight": {
    "fields": {
      "title": {
        "pre_tags": "<em>",
        "post_tags": "</em>"
      }
    }
  }
}
  1. 在数据模拟中我们解释道,标准分词器会将大写转换为小写,所以使用phone去匹配时,2条数据都符合。
  2. 如果match中查询多个单词,默认(or)是有一个符合要求的文档就会被查询出来。
match_all
http
# 完全匹配
POST /product/_search
{
	"query": {
		"match_all": {} // 完全匹配
	}
}
multi_match
http
POST /product/_search
{
  "query": {
    "multi_match": {
      "query": "apple",
      "fields": ["title","name"]
    }
  }
}

等同于多个字段的match,只要有一个字段match就算匹配成功。

match_phrase
http
# 默认slop
POST /product/_search
{
  "query": {
    "match_phrase": {
      "title": "apple phone", // buy apple Phone 和 buy huawei Phone √
      "slop": 0  // 默认为0
    }
  }
}
# 指定slop
POST /product/_search
{
  "query": {
    "match_phrase": {
      "title": {
        "query": "buy phone", // buy apple Phone 和 buy huawei Phone √
        "slop": 1
      }
    }
  }
}
  1. match_phrase被称为短语搜索,要求所有的分词都必须同时出现在文档里面,且位置都必须相邻。
  2. slop表示短语分词后,各个词之间的距离整合后进行匹配查询,slop=1时,buy phone等于buy * phone
match_phrase_prefix
http
POST /product/_search
{
  "query": {
    "match_phrase_prefix": {
      "title": {
        "query": "buy apple",
        "max_expansions": 10
      }
    }
  }
}

match_phrase_prefix在match_phrase基础上进行查询,如果查询词为buy apple,那么根据buy apple * 进行匹配,默认最多返回50个匹配的结果,可以通过设置max_expansions限制结果返回。常用于搜索时的auto complete

精确查询

term
http
POST /product/_search
{
  "query": {
    "term": { // term完全匹配
      "category": "Phone"
    }
  }
}
  1. term与match不同点在于:term是精确匹配,term不会对搜索词进行分词。
  2. 例如category是text类型,对应的文档值是huaiwei phone,那么经过默认分词器会出现huaweiphone两个token(单词),此时用term去匹配,如果搜索词是huawei,那么可以匹配到,如果是huawei phone,那么无法匹配。
  3. categorykeyword类型(添加文档字段不会被分词),那么经过默认分词器会出现一个huawei phone的token,此时用term去匹配,如果是huawei,那么无法匹配到,如果是huawei phone,则是可以匹配到。
terms
http
POST /product/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "terms": { // 效果等同于title in ("buy", "huawei")
            "title": [
              "buy",
              "huawei"
            ]
          }
        }
      ]
    }
  }
}
  1. term适用于一个词(一个体现在不分词,而不是一个单词),terms用于匹配多个词,效果等同于SQL中的in (?,?)查询。

条件查询

bool
http
# 多条件bool查询
POST /{index}/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "phone"
          }
        }
      ],
      "must_not": [
        {
          "range": {
            "time": {
              "gt": "now-4M/M"
            }
          }
        }
      ],
      "should": [
        {
          "term": {
            "name": "手机"
          }
        }
      ]
    }
  }
}
  1. must、must_not、should分别对应Sql中的and、not和or。filter可用于范围过滤,但是并不作用于score计算。
  2. 每个关键字里面都可以包含多个条件,多个关键字组合使用时,必须保证三者都满足的文档才会被返回。
  3. (title="phone") && (time < now-4M/M) && (name="手机") = true的文档才会被返回。

过滤器

filter
http
POST /product/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "title": "phone"
          }
        }
      ],
      "filter": [
        {
          "range": {
            "time": {
              "gte": 20221010,
              "lte": 20230101
            }
          }
        }
      ]
    }
  }
}
  1. query查询注重匹配度,filter注重是否匹配,filter适合大范围筛选数据,query适合精确匹配数据,两者可以配合使用。
  2. filter可以进行缓存匹配,命中就返回文档,而query需要进行额外一步(计算score)并且不支持缓存。
  3. ES通过bitmap(0/1)集合来表示文档是否匹配过滤器,那么在过滤器应用于其他请求时,就不需要再次计算匹配。
  4. filter不光可以作用在bool查询中,也支持在aggs聚合中使用。

其他查询

query_string
http
POST /product/_search
{
  "query": {
    "query_string": {   // 更推荐simple_query_string
      "default_field": "nick_name", 
      "fields": ["nick_name", "title"], // 与default_field不能共存,只能二选一出现。
      "query": "apple Phone1" // 默认会对搜索词进行分词再匹配,与match相同。
    }
  }
}
  1. query_string在不显式指定default_field属性时,默认会对所有文档的字段都进行匹配。会影响性能,不推荐使用。
  2. 相比query_string,更推荐simple_query_string,使用时必须要指定查询哪些字段。
range
http
# 范围查询
POST /product/_search
{
  "query": {
    "range": {
      "time": {
        "lte": "20221101"
      }
    }
  }
}
# 范围查询,指定日期格式
POST /product/_search
{
  "query": {
    "range": {
      "time": {
        "lte": "2022-11-01",
        "format": "yyyy-MM-dd||yyyyMMdd" // ES会将输入的yyyy-MM-dd的参数转换为yyyyMMdd
      }
    }
  }
}
  1. 基于时间进行查询时,需要注意输入的格式需要与mapping中定义的格式相同。
  2. date类型支持表达式(y表示年,M表示月,d表示天),比如20221010||/M表示为2022100120221010||/y表示为20220101now-1y/d表示为当前时间减1年后包含日的时间,若now为20221010,那么结果为20211010。同理now-4M/M,结果为20220601
prefix
http
POST /product/_search
{
  "query": {
    "prefix": {
      "title": {
        "value": "苹果"
      }
    }
  }
}
  1. prefix不会对搜索词进行分析,大小写敏感,score永远为1,不能缓存的filter。
wildcard
http
POST /product/_search
{
  "query": {
    "wildcard": {
      "title": {
        "value": "b*"
      }
    }
  }
}
  1. wildcard使用通配符进行查询,*匹配多个字符或汉字,?匹配一个字符或汉字。
  2. 大小写敏感,如果是buy apple phone,那么通配符是bu*这种才可以,buy app*是不能完全匹配的。
  3. 尽量避免*b这样的操作,和mysql一样,避免左通配让索引失效。
exist
http
POST /product/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "exists": {
            "field": "name"
          }
        }
      ]
    }
  }
}

用于判断字段是否存在,存在会返回文档信息。