在 ES 中,提供了十分丰富的 DSL 查询。
DSL 查询使用 JSON 格式的请求体与 ES 交互,可以实现各种各样的查询需求。
常见的查询有:
使用 DSL 查询,需用到 query 参数。
基本的语法格式:
GET /索引库/_search
{
"query": {
"查询类型": {
"查询条件": "值"
}
}
}
没有查询条件的查询,就是空查询。它会匹配所有的文档。
GET /user/_search
全文查询语句用于全文本字段 text 的分词查询。
示例:查询 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,即短语(词组)匹配,它会分析提供的查询文本并构建一个 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 类似,但 match_phrase_prefix 不是精准匹配,而是前缀匹配。
示例:查询 full_name 字段中以 "John Smi" 短语开头的文档。
GET /user/_search
{
"query" : {
"match_phrase_prefix" : {
"full_name" : "John Smi"
}
}
}
multi_match 用于多字段匹配查询。
性能较低,建议使用 copyto 合并到 all 字段,然后使用 match。
GET /user/_search
{
"query" : {
"multi_match": {
"query": "John Smith",
"fields": ["first_name", "full_name"]
}
}
}
Term-level 查询是直接查询倒排索引中的精确的值。
Term-level 查询用于精确类型的用户输入查询,如数值、日期、枚举值或关键字,而不是文本(text)。
它不会对用户的输入分词。
term 用于查询指定字段的倒排索引包含某个确切值的记录。
POST /user/_search
{
"query": {
"term" : {
"first_name" : "john"
}
}
}
terms 和 term 类似,只不过提供的确切的值是数组。类似于 MySQL 的 in 条件。
POST /user/_search
{
"query": {
"terms" : { "city" : ["bj", "sz"] }
}
}
range 用于范围查询。
GET /user/_search
{
"query": {
"range" : {
"age" : {
"gte" : 28,
"lt" : 60
}
}
}
}
prefix 返回字段的 term 以确切的前缀(前缀不会被分词)开头的记录。
GET /user/_search
{
"query": {
"prefix" : { "full_name" : "joh" }
}
}
wildcard 指的是通配符查询。
支持的通配符主要有:
GET /user/_search
{
"query": {
"wildcard" : { "full_name" : "john*" }
}
}
regexp 指的是正则查询。
GET /user/_search
{
"query": {
"regexp" : { "full_name" : "jo.*" }
}
}
ids 查询指的是根据文档的 id 来查询。
GET /user/_search
{
"query": {
"ids" : { "values" : ["1", "2", "60"] }
}
}
复合查询是将多个简单查询组合起来,实现更复杂的查询逻辑。
算分函数查询,可以人为控制文档的相关性评分,控制文档排名。如百度竞价排名。
bool 复合查询是将多个简单查询(子查询)进行组合。
组合的方式有以下几种:
说明: 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": {
"info.interests": "musics"
}
}
],
"must_not": [
{
"range": {
"info.age": {
"gte": 10,
"lte": 25
}
}
}
],
"minimum_should_match": 1
}
}
}
DSL 查询根据使用目的的不同分为两种类型:
查询(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:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。因此,它不支持随机翻页,只能向后翻页。
使用场景: 手机端的向下滚动翻页。
高亮:就是在搜索结果中把搜索关键字突出显示。
实现原理:
<em></em>
)包裹起来。可通过 highlight 参数实现。