--- title: "Easysearch 搜索入门:_search API 与查询流程" date: 2026-01-16 lastmod: 2026-01-16 description: "讲解 Easysearch 搜索基础与 _search API 核心机制,详解 Query DSL 结构、match_all 查询、响应结果分析、分页与字段过滤、分布式查询流程的 Query-Then-Fetch 两阶段执行,帮助快速上手 Easysearch 搜索。" tags: ["搜索入门", "_search API", "查询流程"] summary: "对于刚开始接触 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),这是为了防止网络传输过载。" --- 对于刚开始接触 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)。在生产环境中,我们绝大多数情况都使用后者。 **基本语法结构:** ```http GET /<索引名>/_search { "query": { <查询条件> }, "from": <偏移量>, "size": <返回条数>, "sort": [ <排序规则> ], "_source": [ <指定返回字段> ] } ``` ## 2. Hello World:使用 match_all 查询 最简单的查询莫过于 `match_all`。顾名思义,它匹配索引中的**所有**文档。这通常用于检查索引是否包含数据,或者作为聚合分析的背景集。 **请求示例:** ```json GET /products/_search { "query": { "match_all": {} } } ``` _注意:虽然它匹配了所有文档,但 Easysearch 默认只会返回前 10 条结果(类似于 SQL 的 _`LIMIT 10`_),这是为了防止网络传输过载。_ ## 3. 读懂搜索响应(Response Analysis) 很多初学者看到 Easysearch 返回的层层嵌套的 JSON 会感到困惑。理解每个字段的含义是开发调试的基础。 假设上述 `match_all` 查询返回如下结果: ```json { "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 意味着部分数据可能不可用(例如某节点宕机)。 2. **核心结果部分 (**`hits`** 对象)**: - `hits.total.value`: **最重要的数据**。表示符合查询条件的文档总数(示例中为 1000 条)。 - `hits.max_score`: 本次搜索结果中最高的匹配评分。 - `hits.hits`: 具体的文档数组(默认返回前 10 个)。 3. **文档详情部分 (**`hits.hits`** 数组)**: - `_index`: 文档所属索引。 - `_id`: 文档的唯一主键。 - `_score`: **相关性评分**。Easysearch 默认按此字段降序排列。对于 `match_all`,所有文档评分都是 1.0。 - `_source`: **原始数据**。即你在写入时发送给 Easysearch 的完整 JSON 内容。 ## 4. 控制查询结果:分页与字段过滤 在实际业务中,我们很少直接使用裸的 `match_all`,通常需要配合分页和字段选择。 ### 4.1 分页 (Pagination) Easysearch 使用 `from`(跳过多少条)和 `size`(返回多少条)来实现分页。 ```json GET /products/_search { "query": { "match_all": {} }, "from": 20, // 跳过前 20 条,即从第 21 条开始 "size": 10 // 返回 10 条 } ``` ### 4.2 字段过滤 (Source Filtering) 如果文档很大,但前端列表页只需要显示标题和价格,通过 `_source` 过滤可以显著减少网络传输流量,提升性能。 ```json GET /products/_search { "query": { "match_all": {} }, "_source": ["name", "price"] // 结果中只返回这两个字段 } ``` ## 5. 深度:Easysearch 的分布式查询流程 理解查询结果后,了解底层流程有助于理解性能特性。Easysearch 是分布式的,数据分散在不同分片(Shard)上。一次搜索请求实际上经历了 **Query Then Fetch(先查询后取回)** 两个阶段: 1. **Query 阶段**: - 协调节点(接收请求的节点)将查询请求转发给索引的所有分片(主分片或副本分片)。 - 每个分片在本地执行查询,计算文档评分,并生成一个**优先队列**(仅包含文档 ID 和 Score,不包含具体数据)。 - 各分片将优先队列返回给协调节点。 2. **Fetch 阶段**: - 协调节点对收集到的所有结果进行全局排序,确定最终需要返回的文档 ID。 - 协调节点向持有这些文档的具体分片发送 `GET` 请求,抓取 `_source` 数据。 - 拼装最终 JSON,返回给客户端。 _这就是为什么深度分页(例如 _`from: 10000, size: 10`_)会很慢的原因——协调节点需要处理 10010 条数据的排序,却只扔掉前 10000 条。_ ## 总结 - `_search` API 是与 Easysearch 交互的核心入口。 - DSL 采用 JSON 结构,`match_all` 是最基础的查询类型。 - 学会解读 `hits.total` 和 `_source` 是开发的第一步。 - 利用 `from`、`size` 和 `_source` 过滤,可以让查询更加高效。 掌握了这些基础后,下一篇我们将深入探讨 Easysearch 强大的**全文检索(Full Text Search)与精确查询(Term Level Query)**的区别与应用。