go操作ES

Elasticsearch (简称 ES) 是一个基于 Lucene 的开源分布式搜索和分析引擎。它不仅能提供强大的全文检索功能,还能处理结构化数据的聚合分析。

整理了这份涵盖核心概念环境搭建常用 APIGo 语言实战 的入门教程。


📚 核心概念:ES 术语 vs 关系型数据库

理解这些概念是学习 ES 的第一步,我们可以将其与 MySQL 做类比:

SQL 术语 Elasticsearch 术语 说明
Database (数据库) Index (索引) 存储相似文档的容器。
Table (表) Type (类型) ES 7.x 已废弃,8.x 已移除。现在统一使用 _doc
Row (行) Document (文档) 具体的 JSON 数据,是 ES 的基本信息单元。
Column (列) Field (字段) 文档中的属性。
Schema (模式) Mapping (映射) 定义字段类型(如 text, keyword, integer)的元数据。

🛠️ 环境搭建 (Docker 推荐)

使用 Docker 是最快的方式。以下命令启动一个单节点的 ES 实例(以 8.x 版本为例):

# 1. 拉取镜像
docker pull docker.elastic.co/elasticsearch/elasticsearch:8.9.0

# 2. 启动容器
docker run -d --name es-node01 \
-p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e "ES_JAVA_OPTS=-Xms1g -Xmx1g" \
-e "xpack.security.enabled=false" \
elasticsearch:8.9.0

注:xpack.security.enabled=false 用于关闭鉴权,方便本地开发调试。

启动后,访问 http://localhost:9200 看到 JSON 响应即表示成功。


⚙️ 常用 REST API 操作

ES 提供基于 HTTP 的 RESTful API。你可以使用 curl 或 Kibana 的 Dev Tools 进行测试。

1. 索引管理 (Index)

创建索引并定义 Mapping
在 ES 中,字段类型非常重要。text 用于全文检索(会被分词),keyword 用于精确匹配(不分词)。

PUT /books
{
  "mappings": {
    "properties": {
      "title": { "type": "text" },
      "author": { "type": "keyword" },
      "price": { "type": "double" },
      "publish_date": { "type": "date" }
    }
  }
}

删除索引

DELETE /books

2. 文档 CRUD 操作

新增/修改文档
如果 ID 存在则更新,不存在则新增。

POST /books/_doc/1
{
  "title": "Elasticsearch 权威指南",
  "author": "Clinton Gormley",
  "price": 89.90,
  "publish_date": "2015-01-01"
}

查询单个文档

GET /books/_doc/1

删除文档

DELETE /books/_doc/1

这是 ES 最强大的部分,所有搜索逻辑都写在 query 对象中。

全文检索 (Match Query)
会对输入进行分词。例如搜索 "Elasticsearch 指南",会匹配包含 "Elasticsearch" 或 "指南" 的文档。

GET /books/_search
{
  "query": {
    "match": {
      "title": "Elasticsearch 指南"
    }
  }
}

精确匹配 (Term Query)
不分词,直接匹配完整值。适用于 keyword、数字、布尔类型。

GET /books/_search
{
  "query": {
    "term": {
      "author": "Clinton Gormley"
    }
  }
}

范围查询 (Range Query)

GET /books/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 50,
        "lte": 100
      }
    }
  }
}

组合查询 (Bool Query)
最常用的复杂查询,支持 must (必须满足), filter (过滤,不计算相关性得分), should (可选)。

GET /books/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "Elasticsearch" } }
      ],
      "filter": [
        { "range": { "price": { "gte": 80 } } }
      ]
    }
  }
}

💻 Go 语言实战

在 Go 中使用 ES,官方推荐的客户端是 github.com/elastic/go-elasticsearch

1. 初始化客户端

package main

import (
    "log"
    "strings"

    "github.com/elastic/go-elasticsearch/v8"
)

func main() {
    cfg := elasticsearch.Config{
        Addresses: []string{
            "http://localhost:9200", // ES 地址
        },
        // 如果有用户名密码,在这里配置
        // Username: "elastic",
        // Password: "password",
    }

    es, err := elasticsearch.NewClient(cfg)
    if err != nil {
        log.Fatalf("Error creating the client: %s", err)
    }

    // 简单的连通性测试
    res, err := es.Info()
    if err != nil {
        log.Fatalf("Error getting response: %s", err)
    }
    defer res.Body.Close()
    
    log.Println("✅ 连接成功:", res.String())
}

2. 写入文档 (Index)

import (
    "bytes"
    "encoding/json"
    // ... 其他引用
)

// 定义数据结构
type Book struct {
    Title  string  `json:"title"`
    Author string  `json:"author"`
    Price  float64 `json:"price"`
}

func indexDoc(es *elasticsearch.Client) {
    book := Book{
        Title:  "Go 语言实战",
        Author: "张三",
        Price:  59.00,
    }

    // 将结构体转为 JSON
    var buf bytes.Buffer
    if err := json.NewEncoder(&buf).Encode(book); err != nil {
        log.Fatalf("Error encoding book: %s", err)
    }

    // 执行写入,ID 设为 "1"
    res, err := es.Index(
        "books",
        &buf,
        es.Index.WithDocumentID("1"),
    )
    if err != nil {
        log.Fatalf("Error indexing document: %s", err)
    }
    defer res.Body.Close()

    log.Printf("写入结果: %s", res.String())
}

Go 客户端使用 esapi.SearchRequest 来构建查询,通常直接传入 JSON 字符串或使用 strings.NewReader

func searchDoc(es *elasticsearch.Client) {
    // 构建查询 DSL (JSON 格式)
    queryStr := `{
        "query": {
            "bool": {
                "must": [
                    { "match": { "title": "Go 语言" } }
                ],
                "filter": [
                    { "range": { "price": { "gte": 50 } } }
                ]
            }
        }
    }`

    res, err := es.Search(
        es.Search.WithIndex("books"),
        es.Search.WithBody(strings.NewReader(queryStr)),
        es.Search.WithPretty(), // 格式化输出
    )
    if err != nil {
        log.Fatalf("Error search request: %s", err)
    }
    defer res.Body.Close()

    // 打印结果
    var r map[string]interface{}
    if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
        log.Fatalf("Error parsing response body: %s", err)
    }

    log.Printf("Found %d hits:", int(r["hits"].(map[string]interface{})["total"].(map[string]interface{})["value"].(float64)))
    for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
        hitMap := hit.(map[string]interface{})
        log.Printf(" > ID: %s, Source: %v", hitMap["_id"], hitMap["_source"])
    }
}

📌 总结

  1. ES 是文档型数据库,数据以 JSON 格式存储。
  2. Mapping 很重要,区分 text (全文搜) 和 keyword (精确搜)。
  3. 查询 DSL 是核心,掌握 match, term, bool, range 即可应对 80% 的场景。
  4. Go 开发 主要是将 JSON 查询语句通过 strings.NewReader 传递给官方客户端。
posted @ 2026-04-21 19:41  干炸小黄鱼  阅读(12)  评论(0)    收藏  举报