Elasticsearch的查询

查询返回字段含义

新增一条索引及文档,用_search进行索引查询

返回结果:

java 复制代码
{
  #查询耗时,单位毫秒。这个查询花了 1ms 执行(仅 ES 服务端执行时间,不包含网络传输耗时)
  "took": 1,
  # 查询是否超时。false没有超时;true代表部分分片查询超时,返回的结果是不完整的。
  "timed_out": false,
  # 分片统计
  "_shards": {
    # 本次查询一共需要访问 1 个分片
    "total": 1,
    # 成功执行返回的分片数量 1
    "successful": 1,
    # 跳过的分片(分片预过滤,不需要查询)
    "skipped": 0,
    # 查询失败的分片,0 代表无分片异常
    "failed": 0
  },
  # 命中文档结果集(最核心)
  "hits": {
    # 匹配条件的文档总数
    "total": {
      # 符合查询条件总共有 1 条文档
      "value": 1,
      # eq:精确值,总数就是value;gte:大于等于(ES 默认,超过 10000 条时,不会精确计数,只会告诉你至少多少条,节省性能)
      "relation": "eq"
    },
    # 本次结果集中,最高的相关性打分 _score。这里最高分是 1
    "max_score": 1,
    # 存放每一条命中文档
    "hits": [
      {
        # 文档所属索引名 product
        "_index": "product",
        # 文档唯一 id
        "_id": "1",
        # 查询上下文的相关性分数。filter 上下文查询这里永远是1.0,不做相关性打分。
        "_score": 1,
        # 原始文档数据,就是你存入 ES 的 json 原始内容,默认返回。可以通过_source:false关闭返回,节省带宽。
        "_source": {
          "name": "xiaomi phone",
          "desc": "shouji zhong de zhandouji",
          "date": "2021-06-01",
          "price": 3999,
          "tags": [
            "xingjiabi",
            "fashao",
            "buka"
          ]
        }
      }
    ]
  }
}

_source过滤

_source是返回文档源数据,支持添加过滤,减少要输出的字段,节约带宽,es底层仍然是读取所有数据,不会提升查询速度。

1、请求体里的 _source

  1. 关闭返回 _source
java 复制代码
# 关闭_source
GET /product/_search
{
  "query": {
    "match_all": {}
  },
  "_source": false
}
  1. 数组,指定要返回哪些字段
java 复制代码
# 数组指定返回字段:name、price
GET /product/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["name","price"]
}
  1. 对象形式 includes + excludes,支持通配符 *
java 复制代码
GET /product/_search
{
  "query": {
    "match_all": {}
  },
  "_source": {
    "includes": ["name","desc","company.*"],
    "excludes": ["desc"]
  }
}
  • includes:包含的字段(白名单)
  • excludes:从白名单里再剔除字段(黑名单)
    支持嵌套对象写法:user.address.city,通配符 obj.*

2、URL 查询参数

用法同写在请求体中一样

  • _source_includes=name,price
  • _source_excludes=desc,date
java 复制代码
# 查询prouct索引的所有文档记录,_source中只返回包含d开头的字段,排除desc
GET /product/_search?_source_includes=d*&_source_excludes=desc

3、Mapping 层面永久过滤

在索引 mapping 中定义_source.includes / _source.excludes,索引存储时就移除字段,无法恢复,一般不推荐业务使用

java 复制代码
PUT product
{
  "mappings": {
    "_source": {
      "includes": ["name"],
      "excludes": ["desc"]
    }
  }
}

GET查询

通过get index/_search可以直接查询数据

java 复制代码
# 查询索引中全部记录
GET /product/_search

# 等价于
GET /product/_search
{
  "query": {
    "match_all": {}
  }
}

带查询条件

java 复制代码
# 查询prduct索引中,name包含xiaomi的文档
GET /product/_search?q=name:xiaomi

# 等价于
GET /product/_search
{
  "query": {
    "match": {
      "name": "xiaomi"
    }
  }
}

带分页

java 复制代码
# 查询prduct索引中,name包含xiaomi的文档,跳过0条,取2条
GET /product/_search?q=name:xiaomi&from=0&size=2

# 等价于
GET /product/_search
{
  "query": {
    "match": {
      "name": "xiaomi"
    }
  },
  "from": 0,
  "size": 2
}

带排序

java 复制代码
# 查询prduct索引中,name包含xiaomi的文档,跳过0条,取2条,按price降序
GET /product/_search?q=name:xiaomi&from=0&size=2&sort=price:desc

# 等价于
GET /product/_search
{
  "query": {
    "match": {
      "name": "xiaomi"
    }
  },
  "from": 0,
  "size": 2,
  "sort": [
    {
      "price": {
        "order": "desc"
      }
    }
  ]
}

在所有支持索引查询的字段查询

java 复制代码
# 查询product索引中,所有排序字段带xiaomi的文档
GET /product/_search?q=xiaomi

# 等价于
GET /product/_search
{
  "query": {
    "query_string": {
      "query": "xiaomi"
    }
  }
}

日期字段查询,日期字段不会分词。

java 复制代码
# 查询product索引中,所有排序字段带xiaomi的文档
GET /product/_search?q=date:2021-06-01

# 等价于
GET /product/_search
{
  "query": {
    "query_string": {
      "query": "date:2021-06-01"
    }
  }
}

match查询

match 查询会对查询条件分词,只要索引字段有能匹配的字段就能查到

java 复制代码
# 查询索引product,name字段包含小米的文档
GET /product/_search
{
  "query": {
    "match": {
      "name": "xiaomi"
    }
  }
}

# 查询索引product,name字段,包含 xiaomi 或 nfc 或 phone 的文档
GET /product/_search
{
  "query": {
    "match": {
      "name": "xiaomi nfc phone"
    }
  }
}

查询条件"xiaomi nfc phone"也会被分词,只要有一个词匹配就算匹配成功。默认按评分字段'_score'排序,匹配的越多,排序越靠前。

match_all查询

match_all 匹配全部文档 。它本身不支持写字段条件 ,它只有两个可选参数:boost、_name。

java 复制代码
{
  "match_all": {
    "boost": 1.2,   // 给这个查询的分数加权,默认1.0
    "_name": "all"  // 查询命名,用于profile/命中标记,调试用
  }
}

multi_match查询

在多个字段中全文搜索,查询条件会分词,只要有一个条件匹配就返回

java 复制代码
# 查询索引中,name或desc字段包含xiaomi 或 nfc 或 phone 的文档
GET /product/_search
{
  "query": {
    "multi_match": {
      "query": "xiaomi nfc phone",
      "fields": ["name","desc"]
    }
  }
}

match_phrase 查询

match_phrase 会对检索文本分词,要求分词后的词在文档中连续、并且顺序一致,还可以控制词之间允许的间隔 slop。

java 复制代码
# 查询索引中,name字段包含xiaomi phone短句的文档,xiaomi和phone都存在且连续、顺序一致。
GET /product/_search
{
  "query": {
    
    "match_phrase": {
      "name": "xiaomi phone"
    }
  }
}

trem-精确匹配

于match查询不同,trem查询不会对要查询的字段进行分词,要求文档中对应字段完全包含才会匹配。

java 复制代码
# 匹配 name为"xiaomi phone" 的文档
GET /product/_search
{
  "query": {
    "term": {
      "name": "xiaomi phone"
    }
  }
}

# 等价于
GET /product/_search
{
  "query": {
    "term": {
      "name": {
        "value": "xiaomi phone"
      }
    }
  }
}

注意:text类型的name为"xiaomi phone"的数据不能被term匹配,因为存储时会被分词,不能用于精确匹配。

match_phrase和term的区别

match_phrase会对搜索条件分词,只要分词后的词条在目标文档的倒排索引中都存在、连续且顺序一致就能被检索。

term不会对查询条件分词,需要目标文档字段字段的倒排索引存在完整的文本才能被检索。

terms查询

terms相当于同时精确匹配多个查询条件,相当于in。

java 复制代码
# 查询tags字段包含lowbee或zhong的文档
GET /product/_search
{
  "query": {
    "terms": {
      "tags": [
        "lowbee",
        "zhong"
      ]
    }
  }
}

range查询

range用户范围查询,用于数值、日期、ip类型的查询,text/keyword 一般不用于 range。

java 复制代码
# 查询price大于等于2999并且小于等于4999的文档
GET /product/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 2999,
        "lte": 4999
      }
    }
  }
}

# 查询日期 大于2021-06-01且小于2021-06-03的文档
GET /product/_search
{
  "query": {
    "range": {
      "date": {
        "gt": "2021-06-01",
        "lt": "2021-06-03"
      }
    }
  }
}

es中提供了对日期的计算方法:

锚点日期 + || + 运算,锚点可以是 now 或者写死日期字符串Elastic

支持:+(加)、-(减)、/(向下舍入 / 取整),单位支持:y 年、M 月、w 周、d 天、h 小时、m 分钟、s 秒

java 复制代码
"gte": "now-1d",       // 当前时间 -1天
"gte": "2021-06-01||+1M"    // 2021-06-01 加1个月
"gte": "2021-06-01||/d"     // 舍入到当天零点(向下取整天)
"gte": "now||/M"            // 舍入到当月第一天零点

bool查询

bool 是复合查询(Compound Query) ,本身不直接匹配文档,作用是组合多个子查询 ,通过 4 个子句来控制多个条件之间的逻辑关系(AND / OR / NOT)。

核心:把多个小查询(term/range/match 等)拼装在一起,并且区分查询上下文(算分) 和过滤上下文(不算分)。

子句 逻辑含义 上下文 是否算_score
must 必须满足(AND) 查询上下文 ✅ 参与打分
should 可以满足(OR) 查询上下文 ✅ 满足就加分
filter 必须满足(AND) 过滤上下文 ❌ 不打分,可缓存
must_not 必须不满足(NOT) 过滤上下文 ❌ 不打分,可缓存
  1. must:条件一定要成立,并且这个条件会参与相关性打分。
  2. filter:条件一定要成立 ,但是不参与打分 ,ES 会尝试缓存,性能更好;纯筛选条件优先放 filter。
  3. must_not:条件一定不能成立,过滤上下文,不打分。
  4. should:可选条件,满足的文档分数更高。
    • 如果 bool 里没有 must /filter:should 至少满足 1 条才算命中;
    • 如果 bool 里有 must /filter:should 只是加分项,不强制满足。

must + filter示例

name 必须匹配 xiaomi(全文检索打分),并且价格≥3000(过滤,不打分)

java 复制代码
# name有xiaomi,价格大于3000
GET product/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "xiaomi"
          }
        }
      ],
      "filter": [
        {
          "range": {
            "price": {
              "gte": 3000
            }
          }
        }
      ]
    }
  }
}

should

匹配 name 含 xiaomi 或者 tags 是 xingjiabi

java 复制代码
# 名字包含xiaomi 或者tags包含xingjiabi
GET /product/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "name": "xiaomi"
          }
        },
        {
          "term": {
            "tags": "xingjiabi"
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}

minimum_should_match:should 最少需要满足几条。当 bool 没有 must/filter 时,默认值是 1。有must/filter时默认等于0,满足只做加分。

must_not 排除

只排除数据不参与评分

java 复制代码
# 价格不能大于 5000
GET /product/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "range": {
            "price": {
              "gte": 5000
            }
          }
        }
      ]
    }
  }
}

bool嵌套

java 复制代码
# 从tags包含xingjiabi或价格小于等于4000的数据中筛选name包含xiaomi的文档
GET product/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "xiaomi"
          }
        }
      ],
      "filter": [
        {
          "bool": {
            "should": [
              {
                "term": {
                  "tags": "xingjiabi"
                }
              },
              {
                "range": {
                  "price": {
                    "lte": 4000
                  }
                }
              }
            ]
          }
        }
      ]
    }
  }
}

filter过滤数据,包含一个子查询:tags包含xingjiabi或者价格小于等于4000,再匹配name中包含xiaomi

相关推荐
Elasticsearch4 小时前
使用 Lucene 搜索你的 Bean —— Highlighting
elasticsearch
Elasticsearch5 小时前
使用 Lucene 搜索你的 Bean —— Elasticsearch
elasticsearch
Elasticsearch10 小时前
最好的 LLM 只有 59% 的时间能写出正确的 Elasticsearch ES|QL。以下是另外 41% 出错的原因
elasticsearch
垚垚学技术_聚焦云原生10 小时前
Ansible Role 生产环境标准目录结构与规范化实践
java·elasticsearch·ansible
小林ixn10 小时前
从 MySQL 的 LIKE 到 ES 倒排索引:一次把全文检索和混合检索讲透
sql·elasticsearch·全文检索·agent·关键词
MayBaymax10 小时前
ES 基础总结
大数据·elasticsearch·搜索引擎
Elastic 中国社区官方博客10 小时前
两个依赖和一个配置块:通过 Prometheus 远程写入将 Spring Boot 指标发送到 Elasticsearch
大数据·数据库·spring boot·elasticsearch·搜索引擎·全文检索·prometheus
SelectDB技术团队11 小时前
ELK 太占磁盘、ES 总报写入拒绝:从归因到可执行的优化清单
大数据·clickhouse·elk·elasticsearch·全文检索·复杂查询·实时更新
frjc21 小时前
数据库选型:如何从众多数据库中选出最理想的那一个
redis·mysql·clickhouse·elasticsearch