57.Go操作ES(官方提供github.com/elastic/go-elasticsearch库)

文章目录

  • 一、简介
    • 1、安装依赖
    • 2、导入依赖
    • 3、连接 ES
  • 二、操作索引
  • 三、model定义
  • 四、操作文档
    • 1、创建文档
    • 2、根据文档唯一ID获取指定索引下的文档
    • 3、检索 document
      • 1、 检索全部文档
      • 2、 模糊条件检索
      • 3、聚合检索
    • 4、更新文档
    • 5、删除文档
    • 6、文档操作完整代码

代码地址:https://gitee.com/lymgoforIT/golang-trick/tree/master/35-go-elasticsearch

一、简介

go-elasticsearchElasticsearch 官方提供的 Go 客户端。每个 Elasticsearch 版本会有一个对应的 go-elasticsearch 版本。官方会维护最近的两个主要版本。

go-elasticsearch 提供了 Low-levelFully-typed 两套API。本文以 Fully-typed API 为例介绍 go-elasticsearch 的常用方法。

本文接下来将以电商平台 “用户评价” 数据为例,演示 Go 语言 Elasticsearch 客户端的相关操作。原文参考地址:https://mp.weixin.qq.com/s/Em-xPi2ZqBALiFX9ebb2fw

关于如何使用 docker 在本地搭建 Elasticsearch 环境请查看我之前的博客: 56.windows docker
安装ES、Go操作ES(github.com/olivere/elastic/v7库)

1、安装依赖

执行以下命令安装v8版本的 go 客户端。

go get github.com/elastic/go-elasticsearch/v8@latest

2、导入依赖

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

可以根据实际需求导入不同的客户端版本,也支持在一个项目中导入不同的客户端版本。

import (
  elasticsearch7 "github.com/elastic/go-elasticsearch/v7"
  elasticsearch8 "github.com/elastic/go-elasticsearch/v8"
)

// ...
es7, _ := elasticsearch7.NewDefaultClient()
es8, _ := elasticsearch8.NewDefaultClient()

3、连接 ES

指定要连接 ES 的相关配置,并创建客户端连接。

这里按实际工作中的习惯创建目录。如在项目下创建dao目录,其下面可能继续有MySQL、Redis、MQ、ES等目录,本文只涉及到ES,所以就是先建 es目录,接着下面建立base.go用于创建连接和初始化等工作。之后的索引和文档等操作可以分别新建对应的.go文件,当然,操作es的函数很多时,也可以根据业务拆分建立.go文件。
在这里插入图片描述

base.go

package es

import (
	"fmt"
	elasticsearch "github.com/elastic/go-elasticsearch/v8"
)

var EsClinet *elasticsearch.TypedClient

func InitEsConn() {
	// ES 配置
	cfg := elasticsearch.Config{
		Addresses: []string{
			"http://localhost:9200",
		},
	}

	// 创建客户端连接
	client, err := elasticsearch.NewTypedClient(cfg)
	if err != nil {
		fmt.Printf("elasticsearch.NewTypedClient failed, err:%v\n", err)
		panic(err)
	}
	EsClinet = client
}

main.go

package main

import "golang-trick/35-go-elasticsearch/dao/es"

func main() {
	// 初始化ES客户端全局连接
	es.InitEsConn()
}

二、操作索引

索引主要包含创建和删除操作

索引相关操作我们都放到index.go文件中

package es

import (
	"context"
	"fmt"
)

// CreateIndex 创建索引
func CreateIndex(indexName string) error {
	resp, err := EsClinet.Indices.
		Create(indexName).
		Do(context.Background())
	if err != nil {
		fmt.Printf("create index failed, err:%v\n", err)
		return err
	}
	fmt.Printf("index:%#v\n", resp.Index)
	return nil
}

// DeleteIndex 删除索引
func DeleteIndex(indexName string) error{
	_, err := EsClinet.Indices. // 表明是对索引的操作,而Index则表示是要操作具体索引下的文档
		Delete(indexName).
		Do(context.Background())
	if err != nil {
		fmt.Printf("delete index failed,err:%v\n", err)
		return err
	}
	fmt.Printf("delete index successed,indexName:%s", indexName)
	return nil
}

创建一个名为 my-review-1(用户评价) 的 index

main.go

package main

import "golang-trick/35-go-elasticsearch/dao/es"

func main() {
	// 初始化ES客户端全局连接
	es.InitEsConn()

	es.CreateIndex("my-review-1")
}

三、model定义

本文主要是对用户评价做检索案例,所以需要建立model,我们一般习惯在项目路径下建立model包专门存放model,入下:

在这里插入图片描述

定义与 document 数据对应的 Review(评价数据) 和 Tag(评价标签) 结构体。

review.go

// Review 评价数据
type Review struct {
 ID          int64     `json:"id"`
 UserID      int64     `json:"userID"`
 Score       uint8     `json:"score"`
 Content     string    `json:"content"`
 Tags        []Tag     `json:"tags"`
 Status      int       `json:"status"`
 PublishTime time.Time `json:"publishDate"`
}

// Tag 评价标签
type Tag struct {
 Code  int    `json:"code"`
 Title string `json:"title"`
}

四、操作文档

1、创建文档

创建一条 document 并添加到 my-review-1index 中。

es包下创建review_doc.go文件,表示是对于review的文档相关操作。

review_doc.go

package es

import (
	"context"
	"fmt"
	"golang-trick/35-go-elasticsearch/model"
	"strconv"
)

// indexDocument 索引文档
func CreateDocument(review model.Review, indexName string) {

	// 添加文档
	resp, err := EsClinet.Index(indexName).
		Id(strconv.FormatInt(review.ID, 10)). // 指定文档唯一ID
		Document(review).
		Do(context.Background())
	if err != nil {
		fmt.Printf("indexing document failed, err:%v\n", err)
		return
	}
	fmt.Printf("result:%#v\n", resp.Result)
}

main.go

package main

import (
	"golang-trick/35-go-elasticsearch/dao/es"
	"golang-trick/35-go-elasticsearch/model"
	"time"
)

func main() {
	// 初始化ES客户端全局连接
	es.InitEsConn()

	// es.CreateIndex("my-review-1")

	// 定义 document 结构体对象
	d1 := model.Review{
		ID:      1,
		UserID:  147982601,
		Score:   5,
		Content: "这是一个好评!",
		Tags: []model.Tag{
			{1000, "好评"},
			{1100, "物超所值"},
			{9000, "有图"},
		},
		Status:      2,
		PublishTime: time.Now(),
	}
	es.CreateDocument(d1, "my-review-1")
}

2、根据文档唯一ID获取指定索引下的文档

// GetDocument 根据文档ID获取文档
func GetDocumentByDocId(id string, indexName string) {
	resp, err := EsClinet.Get(indexName, id).
		Do(context.Background())
	if err != nil {
		fmt.Printf("get document by id failed, err:%v\n", err)
		return
	}
	fmt.Printf("fileds:%s\n", resp.Source_)
}

3、检索 document

1、 检索全部文档

构建搜索查询可以使用结构化的查询条件。

// SearchAllDocument 搜索指定索引下所有文档
func SearchAllDocument(indexName string) {
	// 搜索文档
	resp, err := EsClinet.Search().
		Index(indexName).
		Request(&search.Request{
			Query: &types.Query{
				MatchAll: &types.MatchAllQuery{},
			},
		}).
		Do(context.Background())
	if err != nil {
		fmt.Printf("search document failed, err:%v\n", err)
		return
	}
	fmt.Printf("total: %d\n", resp.Hits.Total.Value)
	// 遍历所有结果
	for _, hit := range resp.Hits.Hits {
		fmt.Printf("%s\n", hit.Source_)
	}
}

2、 模糊条件检索

下面是在 my-review-1 中搜索 content 包含 “好评” 的文档。

// SearchDocument 指定条件搜索文档
func SearchDocument(indexName string) {
	// 搜索content中包含好评的文档
	resp, err := EsClinet.Search().
		Index(indexName).
		Request(&search.Request{
			Query: &types.Query{
				MatchPhrase: map[string]types.MatchPhraseQuery{
					"content": {Query: "好评"},
				},
			},
		}).
		Do(context.Background())
	if err != nil {
		fmt.Printf("search document failed, err:%v\n", err)
		return
	}
	fmt.Printf("total: %d\n", resp.Hits.Total.Value)
	// 遍历所有结果
	for _, hit := range resp.Hits.Hits {
		fmt.Printf("%s\n", hit.Source_)
	}
}

3、聚合检索

my-review-1 上运行一个平均值聚合,得到所有文档 score 的平均值。

// AggregationDemo 聚合
func AggregationDemo(indexName string) {
	avgScoreAgg, err := EsClinet.Search().
		Index(indexName).
		Request(
			&search.Request{
				Size: some.Int(0),
				Aggregations: map[string]types.Aggregations{
					"avg_score": { // 将所有文档的 score 的平均值聚合为 avg_score
						Avg: &types.AverageAggregation{
							Field: some.String("score"),
						},
					},
				},
			},
		).Do(context.Background())
	if err != nil {
		fmt.Printf("aggregation failed, err:%v\n", err)
		return
	}
	fmt.Printf("avgScore:%#v\n", avgScoreAgg.Aggregations["avg_score"])
}

4、更新文档

使用新值更新文档。

// UpdateDocument 更新文档
func UpdateDocument(review model.Review, indexName string) {
	 修改后的结构体变量
	//d1 := Review{
	//	ID:      1,
	//	UserID:  147982601,
	//	Score:   5,
	//	Content: "这是一个修改后的好评!", // 有修改
	//	Tags: []Tag{ // 有修改
	//		{1000, "好评"},
	//		{9000, "有图"},
	//	},
	//	Status:      2,
	//	PublishTime: time.Now(),
	//}

	resp, err := EsClinet.Update(indexName, fmt.Sprintf("%d", review.ID)). // 通过唯一文档ID指定要更新的文档
										Doc(review). // 使用结构体变量更新
										Do(context.Background())
	if err != nil {
		fmt.Printf("update document failed, err:%v\n", err)
		return
	}
	fmt.Printf("result:%v\n", resp.Result)
}

更新可以使用结构体变量也可以使用原始JSON字符串数据。

// UpdateDocumentByJson 更新文档
func UpdateDocumentByJson(docId,str ,indexName string) {
	 修改后的JSON字符串
	//str := `{
    // "id":1,
    // "userID":147982601,
    // "score":5,
    // "content":"这是一个二次修改后的好评!",
    // "tags":[
    //  {
    //   "code":1000,
    //   "title":"好评"
    //  },
    //  {
    //   "code":9000,
    //   "title":"有图"
    //  }
    // ],
    // "status":2,
    // "publishDate":"2023-12-16T15:27:18.219385+08:00"
    //}`
	
	// 直接使用JSON字符串更新
	resp, err := EsClinet.Update(indexName, docId).
		Request(&update.Request{
			Doc: json.RawMessage(str),
		}).
		Do(context.Background())
	if err != nil {
		fmt.Printf("update document failed, err:%v\n", err)
		return
	}
	fmt.Printf("result:%v\n", resp.Result)
}

5、删除文档

根据文档 id 删除文档。

// DeleteDocument 删除 document
func DeleteDocument(docId,indexName string) {
	resp, err := EsClinet.Delete(indexName, docId).
		Do(context.Background())
	if err != nil {
		fmt.Printf("delete document failed, err:%v\n", err)
		return
	}
	fmt.Printf("result:%v\n", resp.Result)
}

6、文档操作完整代码

在这里插入图片描述

package es

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/elastic/go-elasticsearch/v8/typedapi/core/search"
	"github.com/elastic/go-elasticsearch/v8/typedapi/core/update"
	"github.com/elastic/go-elasticsearch/v8/typedapi/some"
	"github.com/elastic/go-elasticsearch/v8/typedapi/types"
	"golang-trick/35-go-elasticsearch/model"
	"strconv"
)

// indexDocument 索引文档
func CreateDocument(review model.Review, indexName string) {

	// 添加文档
	resp, err := EsClinet.Index(indexName). // 表示是要操作具体索引下的文档
						Id(strconv.FormatInt(review.ID, 10)). // 指定文档唯一ID
						Document(review).
						Do(context.Background())
	if err != nil {
		fmt.Printf("indexing document failed, err:%v\n", err)
		return
	}
	fmt.Printf("result:%#v\n", resp.Result)
}

// GetDocument 根据文档ID获取文档
func GetDocumentByDocId(id string, indexName string) {
	resp, err := EsClinet.Get(indexName, id).
		Do(context.Background())
	if err != nil {
		fmt.Printf("get document by id failed, err:%v\n", err)
		return
	}
	fmt.Printf("fileds:%s\n", resp.Source_)
}

// SearchAllDocument 搜索指定索引下所有文档
func SearchAllDocument(indexName string) {
	// 搜索文档
	resp, err := EsClinet.Search().
		Index(indexName).
		Request(&search.Request{
			Query: &types.Query{
				MatchAll: &types.MatchAllQuery{},
			},
		}).
		Do(context.Background())
	if err != nil {
		fmt.Printf("search document failed, err:%v\n", err)
		return
	}
	fmt.Printf("total: %d\n", resp.Hits.Total.Value)
	// 遍历所有结果
	for _, hit := range resp.Hits.Hits {
		fmt.Printf("%s\n", hit.Source_)
	}
}

// SearchDocument 指定条件搜索文档
func SearchDocument(indexName string) {
	// 搜索content中包含好评的文档
	resp, err := EsClinet.Search().
		Index(indexName).
		Request(&search.Request{
			Query: &types.Query{
				MatchPhrase: map[string]types.MatchPhraseQuery{
					"content": {Query: "好评"},
				},
			},
		}).
		Do(context.Background())
	if err != nil {
		fmt.Printf("search document failed, err:%v\n", err)
		return
	}
	fmt.Printf("total: %d\n", resp.Hits.Total.Value)
	// 遍历所有结果
	for _, hit := range resp.Hits.Hits {
		fmt.Printf("%s\n", hit.Source_)
	}
}

// AggregationDemo 聚合
func AggregationDemo(indexName string) {
	avgScoreAgg, err := EsClinet.Search().
		Index(indexName).
		Request(
			&search.Request{
				Size: some.Int(0),
				Aggregations: map[string]types.Aggregations{
					"avg_score": { // 将所有文档的 score 的平均值聚合为 avg_score
						Avg: &types.AverageAggregation{
							Field: some.String("score"),
						},
					},
				},
			},
		).Do(context.Background())
	if err != nil {
		fmt.Printf("aggregation failed, err:%v\n", err)
		return
	}
	fmt.Printf("avgScore:%#v\n", avgScoreAgg.Aggregations["avg_score"])
}

// UpdateDocument 更新文档
func UpdateDocument(review model.Review, indexName string) {
	 修改后的结构体变量
	//d1 := Review{
	//	ID:      1,
	//	UserID:  147982601,
	//	Score:   5,
	//	Content: "这是一个修改后的好评!", // 有修改
	//	Tags: []Tag{ // 有修改
	//		{1000, "好评"},
	//		{9000, "有图"},
	//	},
	//	Status:      2,
	//	PublishTime: time.Now(),
	//}

	resp, err := EsClinet.Update(indexName, fmt.Sprintf("%d", review.ID)). // 通过唯一文档ID指定要更新的文档
										Doc(review). // 使用结构体变量更新
										Do(context.Background())
	if err != nil {
		fmt.Printf("update document failed, err:%v\n", err)
		return
	}
	fmt.Printf("result:%v\n", resp.Result)
}

// UpdateDocumentByJson 更新文档
func UpdateDocumentByJson(docId, str, indexName string) {
	 修改后的JSON字符串
	//str := `{
	// "id":1,
	// "userID":147982601,
	// "score":5,
	// "content":"这是一个二次修改后的好评!",
	// "tags":[
	//  {
	//   "code":1000,
	//   "title":"好评"
	//  },
	//  {
	//   "code":9000,
	//   "title":"有图"
	//  }
	// ],
	// "status":2,
	// "publishDate":"2023-12-16T15:27:18.219385+08:00"
	//}`

	// 直接使用JSON字符串更新
	resp, err := EsClinet.Update(indexName, docId).
		Request(&update.Request{
			Doc: json.RawMessage(str),
		}).
		Do(context.Background())
	if err != nil {
		fmt.Printf("update document failed, err:%v\n", err)
		return
	}
	fmt.Printf("result:%v\n", resp.Result)
}

// DeleteDocument 删除 document
func DeleteDocument(docId, indexName string) {
	resp, err := EsClinet.Delete(indexName, docId).
		Do(context.Background())
	if err != nil {
		fmt.Printf("delete document failed, err:%v\n", err)
		return
	}
	fmt.Printf("result:%v\n", resp.Result)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/249897.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构与算法之美学习笔记:36 | AC自动机:如何用多模式串匹配实现敏感词过滤功能?

目录 前言基于单模式串和 Trie 树实现的敏感词过滤经典的多模式串匹配算法:AC 自动机解答开篇内容小结 前言 本节课程思维导图: 很多支持用户发表文本内容的网站,比如 BBS,大都会有敏感词过滤功能,用来过滤掉用户输入…

论文阅读:PointCLIP V2: Prompting CLIP and GPT for Powerful3D Open-world Learning

https://arxiv.org/abs/2211.11682 0 Abstract 大规模的预训练模型在视觉和语言任务的开放世界中都表现出了良好的表现。然而,它们在三维点云上的传输能力仍然有限,仅局限于分类任务。在本文中,我们首先协作CLIP和GPT成为一个统一的3D开放世…

使用 TensorFlow 创建生产级机器学习模型(基于数据流编程的符号数学系统)——学习笔记

资源出处:初学者的 TensorFlow 2.0 教程 | TensorFlow Core (google.cn) 前言 对于新框架的学习,阅读官方文档是一种非常有效的方法。官方文档通常提供了关于框架的详细信息、使用方法和示例代码,可以帮助你快速了解和掌握框架的使用。 如…

关于 Redis 与传统关系型数据库的选择

当需要为你的应用程序选择合适的数据库时,选择何种数据库通常取决于你项目的特定要求。Redis 是一种高性能的内存数据存储,而 MySQL 等传统关系型数据库也各自具有自己的优势和劣势。在本期文章中,我们将探讨在 Redis 和传统关系型数据库之间…

C++面向对象(OOP)编程-运算符重载

本文主要介绍C面向对象编程中的多态的手段之一运算符重载,讲清运算符重载的本质,以及通过代码实现一些常用的运算符重载。 目录 1 运算符重载的本质 2 运算符重载格式 3 运算符重载分类 3.1 重载为类的成员函数 3.1.1 双目运算符重载 3.1.2 单目运…

Faulhaber 2.5代运动控制系统 25mNm/13W

2.5代控制系统; PWM输出; 四象限控制带; RS232或CANopen通信接口; 2250_BX4_CxD 选件,电缆和连接信息: 适配部件:

Java-File类与IO流(1)

我是南城余!阿里云开发者平台专家博士证书获得者! 欢迎关注我的博客!一同成长! 一名从事运维开发的worker,记录分享学习。 专注于AI,运维开发,windows Linux 系统领域的分享! 本…

在4*4的平面上计算2a1+1+1

0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 在4*4的平面上有2个点,保持2a1的结构,然后向剩余的14个格子里随机扔2个石子。 共有14*13/291种可能 1 - - - 2 - - - 3 - - 1 4 - - - 1 1 - 1 1 - - - - - - - 1 - - …

Python个人代码随笔(观看无益,请跳过)

异常抛错:一般来说,在程序中,遇到异常时,会从这一层逐层往外抛错,一直抛到最外层,由最外层把错误显示在用户终端。 try:raise ValueError("A value error...") except ValueError:print("V…

IS-IS原理与配置3

IS-IS原理与配置 • IS-IS(Intermediate System to Intermediate System,中间系统到中间系统)是ISO (International Organization for Standardization,国际标准化组织)为它的CLNP (ConnectionL…

FGSM、PGD、BIM对抗攻击算法实现

本篇文章是博主在AI、无人机、强化学习等领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在AI学…

Unity中 URP Shader 的纹理与采样器的分离定义

文章目录 前言一、URP Shader 纹理采样的实现1、在属性面板定义一个2D变量用于接收纹理2、申明纹理3、申明采样器4、进行纹理采样 二、申明纹理 和 申明采样器内部干了什么1、申明纹理2、申明采样器 三、采样器设置采样器的传入格式1、纹理设置中,可以看见我们的采样…

vue脚手架安装及使用

准备工作 安装node安装cnpm cnpm是npm的“廉价平替” 提高安装速度 npm install -g cnpm --registryhttps://registry.npm.taobao.org 安装脚手架 安装Vue脚手架 cnpm install -g vue/cli 用vue脚手架创建vue项目 找好创建项目的位置 创建项目 vue create test (test为项…

鸿蒙系统(HarmonyOS)之方舟框架(ArkUI)介绍

鸿蒙开发官网:HarmonyOS应用开发官网 - 华为HarmonyOS打造全场景新服务 方舟开发框架(简称:ArkUI),是一套构建HarmonyOS应用界面的UI开发框架,它提供了极简的UI语法与包括UI组件、动画机制、事件交互等在内…

CSS文本样式(详解)

CSS文本样式 🍧 文本颜色🧁文本缩进🍨文本对齐🍥文本行高🥝文本装饰 🍧 文本颜色 属性:color 作用:设置文本颜色 属性值: 颜色表示方式表示含义属性值颜色名称预定义的…

【专题】最小生成树(prim算法、kruscal算法)

目录 一、最小生成树二、Prim算法1. 算法思想2. 例题3. 性能分析 三、Kruscal算法1. 算法思想2. 例题3. 性能分析 一、最小生成树 生成树中边的权值(代价)之和最小的树。 二、Prim算法 1. 算法思想 设N(V,{E})是连通网,TE是N上最小生成树…

目前最火的大模型训练框架 DeepSpeed 详解来了

目前,大模型的发展已经非常火热,关于大模型的训练、微调也是各个公司重点关注方向,但是大模型训练的痛点是模型参数过大,动辄上百亿,如果单靠单个GPU来完成训练基本不可能。所以需要多卡或者分布式训练来完成这项工作。…

虚拟机启动 I/O error in “xfs_read_agi+0x95“

1.在选择系统界面按e 进入维护模式 2.找到ro把ro改成 rw init/sysroot/bin/sh 然后按Ctrlx 3.找到坏掉的分区,以nvme0n1p3为例进行修复 xfs_repair -d /dev/nvme0n1p3 4.init 6 重新启动 以下情况 先umount 再修复 则修复成功

《使用ThinkPHP6开发项目》 - 登录接口一

《使用ThinkPHP6开发项目》 - 安装ThinkPHP框架-CSDN博客 《使用ThinkPHP6开发项目》 - 设置项目环境变量-CSDN博客 《使用ThinkPHP6开发项目》 - 项目使用多应用开发-CSDN博客 《使用ThinkPHP6开发项目》 - 创建应用-CSDN博客 《使用ThinkPHP6开发项目》 - 创建控制器-CSD…

Rabbitmq消息重复消费问题(幂等性保障)

消息百分百投递架构 在《消息可靠性保证》篇章中,我通过生产者确认机制保障了消息会发送到MQ中,但是在生产者与MQ建立过程的时候出现了网络抖动,连接建立失败,生产者就感知不到MQ返回的ack/nack,无法完全保障消息投递…