📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

对于刚开始接触 INFINI Easysearch 的开发者来说,最先打交道的往往是数据的写入,但最核心的价值则体现在搜索上。与传统关系型数据库使用 SQL 不同,Easysearch 使用基于 JSON 的 Query DSL(Domain Specific Language,领域特定语言)来定义查询。

本文将带你入门 Easysearch 的搜索机制,从最基础的 match_all 开始,深入理解 DSL 结构及查询结果的含义。

1. 认识 _search API #

在 Easysearch 中,搜索是通过发送 RESTful 请求到 _search 端点来实现的。它既支持 URL 参数查询(URI Search),也支持功能更强大的请求体查询(Request Body Search)。在生产环境中,我们绝大多数情况都使用后者。

基本语法结构:

GET /<索引名>/_search
{
  "query": { <查询条件> },
  "from": <偏移量>,
  "size": <返回条数>,
  "sort": [ <排序规则> ],
  "_source": [ <指定返回字段> ]
}

2. Hello World:使用 match_all 查询 #

最简单的查询莫过于 match_all。顾名思义,它匹配索引中的所有文档。这通常用于检查索引是否包含数据,或者作为聚合分析的背景集。

请求示例:

GET /products/_search
{
  "query": {
    "match_all": {}
  }
}

注意:虽然它匹配了所有文档,但 Easysearch 默认只会返回前 10 条结果(类似于 SQL 的 LIMIT 10),这是为了防止网络传输过载。

3. 读懂搜索响应(Response Analysis) #

很多初学者看到 Easysearch 返回的层层嵌套的 JSON 会感到困惑。理解每个字段的含义是开发调试的基础。

假设上述 match_all 查询返回如下结果:

{
  "took": 5,
  "timed_out": false,
  "_shards": {
    "total": 3,
    "successful": 3,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1000,
      "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
      {
        "_index": "products",
        "_id": "1001",
        "_score": 1.0,
        "_source": {
          "name": "Easysearch 权威指南",
          "price": 99.0,
          "category": "Book"
        }
      }
      // ... 更多文档
    ]
  }
}

字段深度解析:

  1. 元数据部分
  • took: 搜索请求耗时的毫秒数。这是性能调优的关键指标。
  • timed_out: 查询是否超时。
  • _shards: 描述了分片参与情况。total 是总分片数,failed 大于 0 意味着部分数据可能不可用(例如某节点宕机)。
  1. 核心结果部分 (hits** 对象)**:
  • hits.total.value: 最重要的数据。表示符合查询条件的文档总数(示例中为 1000 条)。
  • hits.max_score: 本次搜索结果中最高的匹配评分。
  • hits.hits: 具体的文档数组(默认返回前 10 个)。
  1. 文档详情部分 (hits.hits** 数组)**:
  • _index: 文档所属索引。
  • _id: 文档的唯一主键。
  • _score: 相关性评分。Easysearch 默认按此字段降序排列。对于 match_all,所有文档评分都是 1.0。
  • _source: 原始数据。即你在写入时发送给 Easysearch 的完整 JSON 内容。

4. 控制查询结果:分页与字段过滤 #

在实际业务中,我们很少直接使用裸的 match_all,通常需要配合分页和字段选择。

4.1 分页 (Pagination) #

Easysearch 使用 from(跳过多少条)和 size(返回多少条)来实现分页。

GET /products/_search
{
  "query": { "match_all": {} },
  "from": 20, // 跳过前 20 条,即从第 21 条开始
  "size": 10 // 返回 10 
}

4.2 字段过滤 (Source Filtering) #

如果文档很大,但前端列表页只需要显示标题和价格,通过 _source 过滤可以显著减少网络传输流量,提升性能。

GET /products/_search
{
  "query": { "match_all": {} },
  "_source": ["name", "price"] // 结果中只返回这两个字段
}

5. 深度:Easysearch 的分布式查询流程 #

理解查询结果后,了解底层流程有助于理解性能特性。Easysearch 是分布式的,数据分散在不同分片(Shard)上。一次搜索请求实际上经历了 Query Then Fetch(先查询后取回) 两个阶段:

  1. Query 阶段
  • 协调节点(接收请求的节点)将查询请求转发给索引的所有分片(主分片或副本分片)。
  • 每个分片在本地执行查询,计算文档评分,并生成一个优先队列(仅包含文档 ID 和 Score,不包含具体数据)。
  • 各分片将优先队列返回给协调节点。
  1. Fetch 阶段
  • 协调节点对收集到的所有结果进行全局排序,确定最终需要返回的文档 ID。
  • 协调节点向持有这些文档的具体分片发送 GET 请求,抓取 _source 数据。
  • 拼装最终 JSON,返回给客户端。

这就是为什么深度分页(例如 from: 10000, size: 10)会很慢的原因——协调节点需要处理 10010 条数据的排序,却只扔掉前 10000 条。

总结 #

  • _search API 是与 Easysearch 交互的核心入口。
  • DSL 采用 JSON 结构,match_all 是最基础的查询类型。
  • 学会解读 hits.total_source 是开发的第一步。
  • 利用 fromsize_source 过滤,可以让查询更加高效。

掌握了这些基础后,下一篇我们将深入探讨 Easysearch 强大的**全文检索(Full Text Search)与精确查询(Term Level Query)**的区别与应用。