赞
踩
目前golang操作elasticsearch的第三方包中最流行的是:
https://github.com/olivere/elastic
本教程也是基于elastic开发包进行讲解。
golang的elastic开发包和elasticsearch版本有一些对应关系,在开发前需要注意下,必须选择正确的版本,下面是golang elastic开发包和elasticsearch版本关系表:
例如:ES版本是7.0以后的版本,就使用github.com/olivere/elastic/v7这个包地址。
本教程ES使用的是7.0以后的版本,因此安装GO的依赖包如下
go get github.com/olivere/elastic/v7
提示:如果使用goland作为ide,直接导入 import "github.com/olivere/elastic/v7" 包,goland会自动安装依赖包。
在操作ES之前需要创建一个client,用于操作ES,在创建client的时候需要提供ES连接参数。
- package main
-
- import "fmt"
- import "github.com/olivere/elastic/v7"
-
- func main() {
- // 创建ES client用于后续操作ES
- client, err := elastic.NewClient(
- // 设置ES服务地址,支持多个地址
- elastic.SetURL("http://127.0.0.1:9200", "http://127.0.0.1:9201"),
- // 设置基于http base auth验证的账号和密码
- elastic.SetBasicAuth("user", "secret"))
- if err != nil {
- // Handle error
- fmt.Printf("连接失败: %v\n", err)
- } else {
- fmt.Println("连接成功")
- }
- }
- package main
-
- import (
- "context"
- "fmt"
- "github.com/olivere/elastic/v7"
- )
-
- // 索引mapping定义,这里仿微博消息结构定义
- const mapping = `
- {
- "mappings": {
- "properties": {
- "user": {
- "type": "keyword"
- },
- "message": {
- "type": "text"
- },
- "image": {
- "type": "keyword"
- },
- "created": {
- "type": "date"
- },
- "tags": {
- "type": "keyword"
- },
- "location": {
- "type": "geo_point"
- },
- "suggest_field": {
- "type": "completion"
- }
- }
- }
- }`
- //来源:公众号【码农编程进阶笔记】
- func main() {
- // 创建client
- client, err := elastic.NewClient(
- elastic.SetURL("http://127.0.0.1:9200", "http://127.0.0.1:9201"),
- elastic.SetBasicAuth("user", "secret"))
- if err != nil {
- // Handle error
- fmt.Printf("连接失败: %v\n", err)
- } else {
- fmt.Println("连接成功")
- }
-
- // 执行ES请求需要提供一个上下文对象
- ctx := context.Background()
-
- // 首先检测下weibo索引是否存在
- exists, err := client.IndexExists("weibo").Do(ctx)
- if err != nil {
- // Handle error
- panic(err)
- }
- if !exists {
- // weibo索引不存在,则创建一个
- _, err := client.CreateIndex("weibo").BodyString(mapping).Do(ctx)
- if err != nil {
- // Handle error
- panic(err)
- }
- }
- }
提示:后续代码不再提重复提供完整的代码,直接引用client对象,则假定你已经完成包的加载和初始化client对象。
先定义微博的struct, 跟前面创建的weibo索引结构一一对应。
- type Weibo struct {
- User string `json:"user"` // 用户
- Message string `json:"message"` // 微博内容
- Retweets int `json:"retweets"` // 转发数
- Image string `json:"image,omitempty"` // 图片
- Created time.Time `json:"created,omitempty"` // 创建时间
- Tags []string `json:"tags,omitempty"` // 标签
- Location string `json:"location,omitempty"` //位置
- Suggest *elastic.SuggestField `json:"suggest_field,omitempty"`
- }
上面struct定义的时候,都定义了json结构,因为ES请求使用的是json格式,在发送ES请求的时候,会自动转换成json格式。京东面试题:ElasticSearch深度分页解决方案
使用struct结构插入一条ES文档数据,
- // 创建创建一条微博
- msg1 := Weibo{User: "olivere", Message: "打酱油的一天", Retweets: 0}
-
- // 使用client创建一个新的文档
- put1, err := client.Index().
- Index("weibo"). // 设置索引名称
- Id("1"). // 设置文档id
- BodyJson(msg1). // 指定前面声明的微博内容
- Do(ctx) // 执行请求,需要传入一个上下文对象
- if err != nil {
- // Handle error
- panic(err)
- }
-
- fmt.Printf("文档Id %s, 索引名 %s\n", put1.Id, put1.Index)
- // 根据id查询文档
- get1, err := client.Get().
- Index("weibo"). // 指定索引名
- Id("1"). // 设置文档id
- Do(ctx) // 执行请求
- if err != nil {
- // Handle error
- panic(err)
- }
- if get1.Found {
- fmt.Printf("文档id=%s 版本号=%d 索引名=%s\n", get1.Id, get1.Version, get1.Index)
- }
- msg2 := Weibo{}
- // 提取文档内容,原始类型是json数据
- data, _ := get1.Source.MarshalJSON()
- // 将json转成struct结果
- json.Unmarshal(data, &msg2)
- // 打印结果
- fmt.Println(msg2.Message)
- //根据文档id更新内容
- _, err := client.Update().
- Index("weibo"). // 设置索引名
- Id("1"). // 文档id
- Doc(map[string]interface{}{"retweets": 0}). // 更新retweets=0,支持传入键值结构
- Do(ctx) // 执行ES查询
- if err != nil {
- // Handle error
- panic(err)
- }
- // 根据id删除一条数据
- _, err := client.Delete().
- Index("weibo").
- Id("1").
- Do(ctx)
- if err != nil {
- // Handle error
- panic(err)
- }
本节介绍golang elastic client参数详解,主要包括:Elasticsearch基本知识
• elasticsearch连接地址
• elasticsearch账号/密码
• 监控检查
• 失败重试次数
• gzip设置
- client, err := elastic.NewClient(
- // elasticsearch 服务地址,多个服务地址使用逗号分隔
- elastic.SetURL("http://10.0.1.1:9200", "http://10.0.1.2:9200"),
- // 基于http base auth验证机制的账号和密码
- elastic.SetBasicAuth("user", "secret"),
- // 启用gzip压缩
- elastic.SetGzip(true),
- // 设置监控检查时间间隔
- elastic.SetHealthcheckInterval(10*time.Second),
- // 设置请求失败最大重试次数
- elastic.SetMaxRetries(5),
- // 设置错误日志输出
- elastic.SetErrorLog(log.New(os.Stderr, "ELASTIC ", log.LstdFlags)),
- // 设置info日志输出
- elastic.SetInfoLog(log.New(os.Stdout, "", log.LstdFlags)))
- if err != nil {
- // Handle error
- panic(err)
- }
- _ = client
本节主要介绍go语言对Elasticsearch文档的基础操作:创建、查询、更新、删除。php 使用 ElasticSearch 高级查询、过滤、排序
为了方便演示文档的CRUD操作,我们先定义索引的struct结构
- // 定义一个文章索引结构,用来存储文章内容
- type Article struct {
- Title string // 文章标题
- Content string // 文章内容
- Author string // 作者
- Created time.Time // 发布时间
- }
- package main
-
- import (
- "context"
- "encoding/json"
- "fmt"
- "github.com/olivere/elastic/v7"
- "log"
- "os"
- "time"
- )
-
- type Article struct {
- Title string // 文章标题
- Content string // 文章内容
- Author string // 作者
- Created time.Time // 发布时间
- }
-
-
- func main() {
- // 创建client连接ES
- client, err := elastic.NewClient(
- // elasticsearch 服务地址,多个服务地址使用逗号分隔
- elastic.SetURL("http://127.0.0.1:9200", "http://127.0.0.1:9201"),
- // 基于http base auth验证机制的账号和密码
- elastic.SetBasicAuth("user", "secret"),
- // 启用gzip压缩
- elastic.SetGzip(true),
- // 设置监控检查时间间隔
- elastic.SetHealthcheckInterval(10*time.Second),
- // 设置请求失败最大重试次数
- elastic.SetMaxRetries(5),
- // 设置错误日志输出
- elastic.SetErrorLog(log.New(os.Stderr, "ELASTIC ", log.LstdFlags)),
- // 设置info日志输出
- elastic.SetInfoLog(log.New(os.Stdout, "", log.LstdFlags)))
-
- if err != nil {
- // Handle error
- fmt.Printf("连接失败: %v\n", err)
- } else {
- fmt.Println("连接成功")
- }
-
- // 执行ES请求需要提供一个上下文对象
- ctx := context.Background()
-
- // 定义一篇博客
- blog := Article{Title:"golang es教程", Content:"go如何操作ES", Author:"tizi", Created:time.Now()}
-
- // 使用client创建一个新的文档
- put1, err := client.Index().
- Index("blogs"). // 设置索引名称
- Id("1"). // 设置文档id
- BodyJson(blog). // 指定前面声明struct对象
- Do(ctx) // 执行请求,需要传入一个上下文对象
- if err != nil {
- // Handle error
- panic(err)
- }
-
- fmt.Printf("文档Id %s, 索引名 %s\n", put1.Id, put1.Index)
- }
提示:后续的章节不再重复给出完整的代码,仅给出关键代码片段
- 根据文档ID,查询文档
-
- // 根据id查询文档
- get1, err := client.Get().
- Index("blogs"). // 指定索引名
- Id("1"). // 设置文档id
- Do(ctx) // 执行请求
- if err != nil {
- // Handle error
- panic(err)
- }
- if get1.Found {
- fmt.Printf("文档id=%s 版本号=%d 索引名=%s\n", get1.Id, get1.Version, get1.Index)
- }
-
-
- # 手动将文档内容转换成go struct对象
- msg2 := Article{}
- // 提取文档内容,原始类型是json数据
- data, _ := get1.Source.MarshalJSON()
- // 将json转成struct结果
- json.Unmarshal(data, &msg2)
- // 打印结果
- fmt.Println(msg2.Title)
- 通过多个Id批量查询文档,对应ES的multi get
-
- // 查询id等于1,2,3的博客内容
- result, err := client.MultiGet().
- Add(elastic.NewMultiGetItem(). // 通过NewMultiGetItem配置查询条件
- Index("blogs"). // 设置索引名
- Id("1")). // 设置文档id
- Add(elastic.NewMultiGetItem().Index("blogs").Id("2")).
- Add(elastic.NewMultiGetItem().Index("blogs").Id("3")).
- Do(ctx) // 执行请求
-
- if err != nil {
- panic(err)
- }
-
- // 遍历文档
- for _, doc := range result.Docs {
- // 转换成struct对象
- var content Article
- tmp, _ := doc.Source.MarshalJSON()
- err := json.Unmarshal(tmp, &content)
- if err != nil {
- panic(err)
- }
-
- fmt.Println(content.Title)
- }
- //根据id更新文档
-
- _, err := client.Update().
- Index("blogs"). // 设置索引名
- Id("1"). // 文档id
- Doc(map[string]interface{}{"Title": "新的文章标题"}). // 更新Title="新的文章标题",支持传入键值结构
- Do(ctx) // 执行ES查询
- if err != nil {
- // Handle error
- panic(err)
- }
- //支持批量更新文档内容
- _, err = client.UpdateByQuery("blogs").
- // 设置查询条件,这里设置Author=tizi
- Query(elastic.NewTermQuery("Author", "tizi")).
- // 通过脚本更新内容,将Title字段改为1111111
- Script(elastic.NewScript( "ctx._source['Title']='1111111'")).
- // 如果文档版本冲突继续执行
- ProceedOnVersionConflict().
- Do(ctx)
提示: 复杂查询条件,请参考go es查询用法
- // 根据id删除一条数据
- _, err := client.Delete().
- Index("blogs").
- Id("1"). // 文档id
- Do(ctx)
- if err != nil {
- // Handle error
- panic(err)
- }
- _, _ = client.DeleteByQuery("blogs"). // 设置索引名
- // 设置查询条件为: Author = tizi
- Query(elastic.NewTermQuery("Author", "tizi")).
- // 文档冲突也继续删除
- ProceedOnVersionConflict().
- Do(ctx)
提示: 复杂查询条件,请参考go es查询用法:https://www.tizi365.com/archives/858.html
elasticsearch的查询语法比较丰富,下面分别介绍golang 的各种查询用法。
如果对ES的查询语法和概念不了解,请阅读:Elasticsearch基本知识
elasticsearch的term查询,下面给出完整的代码
- package main
-
- import (
- "context"
- "fmt"
- "github.com/olivere/elastic/v7"
- "log"
- "os"
- "reflect"
- "time"
- )
-
- type Article struct {
- Title string // 文章标题
- Content string // 文章内容
- Author string // 作者
- Created time.Time // 发布时间
- }
-
-
- func main() {
- // 创建Client, 连接ES
- client, err := elastic.NewClient(
- // elasticsearch 服务地址,多个服务地址使用逗号分隔
- elastic.SetURL("http://127.0.0.1:9200", "http://127.0.0.1:9201"),
- // 基于http base auth验证机制的账号和密码
- elastic.SetBasicAuth("user", "secret"),
- // 启用gzip压缩
- elastic.SetGzip(true),
- // 设置监控检查时间间隔
- elastic.SetHealthcheckInterval(10*time.Second),
- // 设置请求失败最大重试次数
- elastic.SetMaxRetries(5),
- // 设置错误日志输出
- elastic.SetErrorLog(log.New(os.Stderr, "ELASTIC ", log.LstdFlags)),
- // 设置info日志输出
- elastic.SetInfoLog(log.New(os.Stdout, "", log.LstdFlags)))
-
- if err != nil {
- // Handle error
- fmt.Printf("连接失败: %v\n", err)
- } else {
- fmt.Println("连接成功")
- }
-
- // 执行ES请求需要提供一个上下文对象
- ctx := context.Background()
-
- // 创建term查询条件,用于精确查询
- termQuery := elastic.NewTermQuery("Author", "tizi")
-
- searchResult, err := client.Search().
- Index("blogs"). // 设置索引名
- Query(termQuery). // 设置查询条件
- Sort("Created", true). // 设置排序字段,根据Created字段升序排序,第二个参数false表示逆序
- From(0). // 设置分页参数 - 起始偏移量,从第0行记录开始
- Size(10). // 设置分页参数 - 每页大小
- Pretty(true). // 查询结果返回可读性较好的JSON格式
- Do(ctx) // 执行请求
-
- if err != nil {
- // Handle error
- panic(err)
- }
-
- fmt.Printf("查询消耗时间 %d ms, 结果总数: %d\n", searchResult.TookInMillis, searchResult.TotalHits())
-
-
- if searchResult.TotalHits() > 0 {
- // 查询结果不为空,则遍历结果
- var b1 Article
- // 通过Each方法,将es结果的json结构转换成struct对象
- for _, item := range searchResult.Each(reflect.TypeOf(b1)) {
- // 转换成Article对象
- if t, ok := item.(Article); ok {
- fmt.Println(t.Title)
- }
- }
- }
- }
提示:后续章节,仅给出关键代码片段,其他代码结构参考本节即可
通过terms查询语法实现,多值查询效果用ElasticSearch实现基于标签的兴趣推荐
例子:
- // 创建terms查询条件
- termsQuery := elastic.NewTermsQuery("Author", "tizi", "tizi365")
-
- searchResult, err := client.Search().
- Index("blogs"). // 设置索引名
- Query(termsQuery). // 设置查询条件
- Sort("Created", true). // 设置排序字段,根据Created字段升序排序,第二个参数false表示逆序
- From(0). // 设置分页参数 - 起始偏移量,从第0行记录开始
- Size(10). // 设置分页参数 - 每页大小
- Do(ctx) // 执行请求
某个字段使用全文搜索,也就是ES的match语法
例子:
- // 创建match查询条件
- matchQuery := elastic.NewMatchQuery("Title", "golang es教程")
-
- searchResult, err := client.Search().
- Index("blogs"). // 设置索引名
- Query(matchQuery). // 设置查询条件
- Sort("Created", true). // 设置排序字段,根据Created字段升序排序,第二个参数false表示逆序
- From(0). // 设置分页参数 - 起始偏移量,从第0行记录开始
- Size(10). // 设置分页参数 - 每页大小
- Do(ctx)
实现类似Created > '2020-07-20' and Created < '2020-07-22'的范围查询条件
创建查询表达式例子:
- // 例1 等价表达式:Created > "2020-07-20" and Created < "2020-07-29"
- rangeQuery := elastic.NewRangeQuery("Created").
- Gt("2020-07-20").
- Lt("2020-07-29")
-
-
- // 例2 等价表达式:id >= 1 and id < 10
- rangeQuery := elastic.NewRangeQuery("id").
- Gte(1).
- Lte(10)
bool组合查询,实际上就是组合了前面的查询条件,然后通过类似SQL语句的and和or将查询条件组合起来,不熟悉ES查询语法,请参考ES教程
类似SQL的and,代表必须匹配的条件。
- // 创建bool查询
- boolQuery := elastic.NewBoolQuery().Must()
-
- // 创建term查询
- termQuery := elastic.NewTermQuery("Author", "tizi")
- matchQuery := elastic.NewMatchQuery("Title", "golang es教程")
-
- // 设置bool查询的must条件, 组合了两个子查询
- // 表示搜索匹配Author=tizi且Title匹配"golang es教程"的文档
- boolQuery.Must(termQuery, matchQuery)
-
- searchResult, err := client.Search().
- Index("blogs"). // 设置索引名
- Query(boolQuery). // 设置查询条件
- Sort("Created", true). // 设置排序字段,根据Created字段升序排序,第二个参数false表示逆序
- From(0). // 设置分页参数 - 起始偏移量,从第0行记录开始
- Size(10). // 设置分页参数 - 每页大小
- Do(ctx) // 执行请求
跟must的作用相反,用法和must类似
- // 创建bool查询
- boolQuery := elastic.NewBoolQuery().Must()
-
- // 创建term查询
- termQuery := elastic.NewTermQuery("Author", "tizi")
-
- // 设置bool查询的must not条件
- boolQuery.MustNot(termQuery)
类似SQL中的 or, 只要匹配其中一个条件即可
- // 创建bool查询
- boolQuery := elastic.NewBoolQuery().Must()
-
- // 创建term查询
- termQuery := elastic.NewTermQuery("Author", "tizi")
- matchQuery := elastic.NewMatchQuery("Title", "golang es教程")
-
- // 设置bool查询的should条件, 组合了两个子查询
- // 表示搜索Author=tizi或者Title匹配"golang es教程"的文档
- boolQuery.Should(termQuery, matchQuery)
- 提示:go的elastic库,组合bool语句的用法,跟ES bool语法类似,可以互相嵌套查询语句。
elasticsearch聚合分析的概念和语法可以参考:
这里主要介绍golang elasticsearch聚合分析的用法。
我们都知道ES聚合分析主要包括:
• 指标聚合
• 桶聚合
这两种聚合可以嵌套混合使用,桶聚合通常用于对数据分组,然后分组内的数据可以使用指标聚合汇总数据。
下面看一个综合的聚合分析的例子:
- // 创建ES client
- client, err := elastic.NewClient()
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 创建一个terms聚合,根据user字段分组,同时设置桶排序条件为按计数倒序排序,并且返回前10条桶数据
- timeline := elastic.NewTermsAggregation().Field("user").Size(10).OrderByCountDesc()
- // 创建Date histogram聚合,根据created时间字段分组,按年分组
- histogram := elastic.NewDateHistogramAggregation().Field("created").CalendarInterval("year")
-
- // 设置timeline的嵌套聚合条件,整体意思就是:首先按user字段分组,然后分组数据内,再次根据created时间字段按年分组,进行了两次分组。
- timeline = timeline.SubAggregation("history", histogram)
-
- // 执行ES查询
- searchResult, err := client.Search().
- Index("twitter"). // 设置索引名
- Query(elastic.NewMatchAllQuery()). // 设置查询条件
- Aggregation("timeline", timeline). // 设置聚合条件,并为聚合条件设置一个名字
- Pretty(true). // 返回可读的json格式
- Do(context.Background()) // 执行
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 遍历ES查询结果,因为我们首先使用的是terms聚合条件,
- // 所以查询结果先使用Terms函数和聚合条件的名字读取结果。
- agg, found := searchResult.Aggregations.Terms("timeline")
- if !found {
- // 没有查询到terms聚合结果
- log.Fatalf("we should have a terms aggregation called %q", "timeline")
- }
-
- // 遍历桶数据
- for _, userBucket := range agg.Buckets {
- // 每一个桶都有一个key值,其实就是分组的值,可以理解为SQL的group by值
- user := userBucket.Key
-
- // 查询嵌套聚合查询的数据
- // 因为我们使用的是Date histogram聚合,所以需要使用DateHistogram函数和聚合名字获取结果
- histogram, found := userBucket.DateHistogram("history")
- if found {
- // 如果找到Date histogram聚合结果,则遍历桶数据
- for _, year := range histogram.Buckets {
- var key string
- if s := year.KeyAsString; s != nil {
- // 因为返回的是指针类型,这里做一下取值运算
- key = *s
- }
- // 打印结果
- fmt.Printf("user %q has %d tweets in %q\n", user, year.DocCount, key)
- }
- }
- }
后面的章节再分别介绍指标聚合和桶聚合的详细写法。
ES指标聚合,就是类似SQL的统计函数,指标聚合可以单独使用,也可以跟桶聚合一起使用,下面介绍golang如何使用ES的指标聚合。
不了解ES指标聚合相关知识,先看一下Elasticsearch 指标聚合教程
值聚合,主要用于统计文档总数,类似SQL的count函数。
- package main
-
- import (
- "context"
- "fmt"
- "github.com/olivere/elastic/v7"
- "time"
- )
-
- func main() {
- // 创建ES client
- client, err := elastic.NewClient()
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 执行ES请求需要提供一个上下文对象
- ctx := context.Background()
-
- // 创建Value Count指标聚合
- aggs := elastic.NewValueCountAggregation().
- Field("order_id") // 设置统计字段
-
- searchResult, err := client.Search().
- Index("kibana_sample_data_flights"). // 设置索引名
- Query(elastic.NewMatchAllQuery()). // 设置查询条件
- Aggregation("total", aggs). // 设置聚合条件,并为聚合条件设置一个名字, 支持添加多个聚合条件,命名不一样即可。
- Size(0). // 设置分页参数 - 每页大小,设置为0代表不返回搜索结果,仅返回聚合分析结果
- Do(ctx) // 执行请求
-
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 使用ValueCount函数和前面定义的聚合条件名称,查询结果
- agg, found := searchResult.Aggregations.ValueCount("total")
- if found {
- // 打印结果,注意:这里使用的是取值运算符
- fmt.Println(*agg.Value)
- }
- }
提示:go elastic库,所有聚合分析结果都是通过对应的函数获取结果,例如前面的例子,Value Count聚合结果,通过ValueCount函数获取结果,后面继续介绍其他指标聚合的用法。
基数聚合,也是用于统计文档的总数,跟Value Count的区别是,基数聚合会去重,不会统计重复的值,类似SQL的count(DISTINCT 字段)用法。
提示:基数聚合是一种近似算法,统计的结果会有一定误差,不过性能很好。
- // 创建Cardinality指标聚合
- aggs := elastic.NewCardinalityAggregation().
- Field("order_id") // 设置统计字段
-
- searchResult, err := client.Search().
- Index("kibana_sample_data_flights"). // 设置索引名
- Query(elastic.NewMatchAllQuery()). // 设置查询条件
- Aggregation("total", aggs). // 设置聚合条件,并为聚合条件设置一个名字
- Size(0). // 设置分页参数 - 每页大小,设置为0代表不返回搜索结果,仅返回聚合分析结果
- Do(ctx) // 执行请求
-
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 使用Cardinality函数和前面定义的聚合条件名称,查询结果
- agg, found := searchResult.Aggregations.Cardinality("total")
- if found {
- // 打印结果,注意:这里使用的是取值运算符
- fmt.Println(*agg.Value)
- }
求平均值
- // 创建Avg指标聚合
- aggs := elastic.NewAvgAggregation().
- Field("price") // 设置统计字段
-
- searchResult, err := client.Search().
- Index("kibana_sample_data_flights"). // 设置索引名
- Query(elastic.NewMatchAllQuery()). // 设置查询条件
- Aggregation("avg_price", aggs). // 设置聚合条件,并为聚合条件设置一个名字
- Size(0). // 设置分页参数 - 每页大小,设置为0代表不返回搜索结果,仅返回聚合分析结果
- Do(ctx) // 执行请求
-
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 使用Avg函数和前面定义的聚合条件名称,查询结果
- agg, found := searchResult.Aggregations.Avg("avg_price")
- if found {
- // 打印结果,注意:这里使用的是取值运算符
- fmt.Println(*agg.Value)
- }
求和计算
- // 创建Sum指标聚合
- aggs := elastic.NewSumAggregation().
- Field("price") // 设置统计字段
-
- searchResult, err := client.Search().
- Index("kibana_sample_data_flights"). // 设置索引名
- Query(elastic.NewMatchAllQuery()). // 设置查询条件
- Aggregation("total_price", aggs). // 设置聚合条件,并为聚合条件设置一个名字
- Size(0). // 设置分页参数 - 每页大小,设置为0代表不返回搜索结果,仅返回聚合分析结果
- Do(ctx) // 执行请求
-
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 使用Sum函数和前面定义的聚合条件名称,查询结果
- agg, found := searchResult.Aggregations.Sum("total_price")
- if found {
- // 打印结果,注意:这里使用的是取值运算符
- fmt.Println(*agg.Value)
- }
求最大值
- // 创建Sum指标聚合
- aggs := elastic.NewMaxAggregation().
- Field("price") // 设置统计字段
-
- searchResult, err := client.Search().
- Index("kibana_sample_data_flights"). // 设置索引名
- Query(elastic.NewMatchAllQuery()). // 设置查询条件
- Aggregation("max_price", aggs). // 设置聚合条件,并为聚合条件设置一个名字
- Size(0). // 设置分页参数 - 每页大小,设置为0代表不返回搜索结果,仅返回聚合分析结果
- Do(ctx) // 执行请求
-
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 使用Max函数和前面定义的聚合条件名称,查询结果
- agg, found := searchResult.Aggregations.Max("max_price")
- if found {
- // 打印结果,注意:这里使用的是取值运算符
- fmt.Println(*agg.Value)
- }
求最小值
- // 创建Min指标聚合
- aggs := elastic.NewMinAggregation().
- Field("price") // 设置统计字段
-
- searchResult, err := client.Search().
- Index("kibana_sample_data_flights"). // 设置索引名
- Query(elastic.NewMatchAllQuery()). // 设置查询条件
- Aggregation("min_price", aggs). // 设置聚合条件,并为聚合条件设置一个名字
- Size(0). // 设置分页参数 - 每页大小,设置为0代表不返回搜索结果,仅返回聚合分析结果
- Do(ctx) // 执行请求
-
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 使用Min函数和前面定义的聚合条件名称,查询结果
- agg, found := searchResult.Aggregations.Min("min_price")
- if found {
- // 打印结果,注意:这里使用的是取值运算符
- fmt.Println(*agg.Value)
- }
Elasticsearch桶聚合,目的就是数据分组,先将数据按指定的条件分成多个组,然后对每一个组进行统计。
不了解Elasticsearch桶聚合概念,可以先学习下Elasticsearch桶聚合教程:https://www.tizi365.com/archives/646.html
下面分别介绍golang elasticsearch桶聚合的写法
- package main
-
- import (
- "context"
- "fmt"
- "github.com/olivere/elastic/v7"
- "log"
- )
-
- func main() {
- // 创建ES client
- client, err := elastic.NewClient()
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 执行ES请求需要提供一个上下文对象
- ctx := context.Background()
-
- // 创建Terms桶聚合
- aggs := elastic.NewTermsAggregation().
- Field("shop_id") // 根据shop_id字段值,对数据进行分组
-
- searchResult, err := client.Search().
- Index("shops"). // 设置索引名
- Query(elastic.NewMatchAllQuery()). // 设置查询条件
- Aggregation("shop", aggs). // 设置聚合条件,并为聚合条件设置一个名字
- Size(0). // 设置分页参数 - 每页大小,设置为0代表不返回搜索结果,仅返回聚合分析结果
- Do(ctx) // 执行请求
-
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 使用Terms函数和前面定义的聚合条件名称,查询结果
- agg, found := searchResult.Aggregations.Terms("shop")
- if !found {
- log.Fatal("没有找到聚合数据")
- }
-
- // 遍历桶数据
- for _, bucket := range agg.Buckets {
- // 每一个桶都有一个key值,其实就是分组的值,可以理解为SQL的group by值
- bucketValue := bucket.Key
-
- // 打印结果, 默认桶聚合查询,都是统计文档总数
- fmt.Printf("bucket = %q 文档总数 = %d\n", bucketValue, bucket.DocCount)
- }
- }
- // 创建Histogram桶聚合
- aggs := elastic.NewHistogramAggregation().
- Field("price"). // 根据price字段值,对数据进行分组
- Interval(50) // 分桶的间隔为50,意思就是price字段值按50间隔分组
-
- searchResult, err := client.Search().
- Index("order"). // 设置索引名
- Query(elastic.NewMatchAllQuery()). // 设置查询条件
- Aggregation("prices", aggs). // 设置聚合条件,并为聚合条件设置一个名字
- Size(0). // 设置分页参数 - 每页大小,设置为0代表不返回搜索结果,仅返回聚合分析结果
- Do(ctx) // 执行请求
-
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 使用Histogram函数和前面定义的聚合条件名称,查询结果
- agg, found := searchResult.Aggregations.Histogram("prices")
- if !found {
- log.Fatal("没有找到聚合数据")
- }
-
- // 遍历桶数据
- for _, bucket := range agg.Buckets {
- // 每一个桶都有一个key值,其实就是分组的值,可以理解为SQL的group by值
- bucketValue := bucket.Key
-
- // 打印结果, 默认桶聚合查询,都是统计文档总数
- fmt.Printf("bucket = %q 文档总数 = %d\n", bucketValue, bucket.DocCount)
- }
- // 创DateHistogram桶聚合
- aggs := elastic.NewDateHistogramAggregation().
- Field("date"). // 根据date字段值,对数据进行分组
- // 分组间隔:month代表每月、支持minute(每分钟)、hour(每小时)、day(每天)、week(每周)、year(每年)
- CalendarInterval("month").
- // 设置返回结果中桶key的时间格式
- Format("yyyy-MM-dd")
-
- searchResult, err := client.Search().
- Index("order"). // 设置索引名
- Query(elastic.NewMatchAllQuery()). // 设置查询条件
- Aggregation("sales_over_time", aggs). // 设置聚合条件,并为聚合条件设置一个名字
- Size(0). // 设置分页参数 - 每页大小,设置为0代表不返回搜索结果,仅返回聚合分析结果
- Do(ctx) // 执行请求
-
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 使用DateHistogram函数和前面定义的聚合条件名称,查询结果
- agg, found := searchResult.Aggregations.DateHistogram("sales_over_time")
- if !found {
- log.Fatal("没有找到聚合数据")
- }
-
- // 遍历桶数据
- for _, bucket := range agg.Buckets {
- // 每一个桶都有一个key值,其实就是分组的值,可以理解为SQL的group by值
- bucketValue := bucket.Key
-
- // 打印结果, 默认桶聚合查询,都是统计文档总数
- fmt.Printf("bucket = %q 文档总数 = %d\n", bucketValue, bucket.DocCount)
- }
- // 创Range桶聚合
- aggs := elastic.NewRangeAggregation().
- Field("price"). // 根据price字段分桶
- AddUnboundedFrom(100). // 范围配置, 0 - 100
- AddRange(100.0, 200.0). // 范围配置, 100 - 200
- AddUnboundedTo(200.0) // 范围配置,> 200的值
-
- searchResult, err := client.Search().
- Index("order"). // 设置索引名
- Query(elastic.NewMatchAllQuery()). // 设置查询条件
- Aggregation("price_ranges", aggs). // 设置聚合条件,并为聚合条件设置一个名字
- Size(0). // 设置分页参数 - 每页大小,设置为0代表不返回搜索结果,仅返回聚合分析结果
- Do(ctx) // 执行请求
-
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 使用Range函数和前面定义的聚合条件名称,查询结果
- agg, found := searchResult.Aggregations.Range("price_ranges")
- if !found {
- log.Fatal("没有找到聚合数据")
- }
-
- // 遍历桶数据
- for _, bucket := range agg.Buckets {
- // 每一个桶都有一个key值,其实就是分组的值,可以理解为SQL的group by值
- bucketValue := bucket.Key
-
- // 打印结果, 默认桶聚合查询,都是统计文档总数
- fmt.Printf("bucket = %q 文档总数 = %d\n", bucketValue, bucket.DocCount)
- }
任意聚合类型都支持嵌套,桶聚合可以嵌套桶聚合,也可以嵌套指标聚合。
例子:
- // 创terms桶聚合
- aggs := elastic.NewTermsAggregation().Field("shop_id")
- // 创建Sum指标聚合
- sumAggs := elastic.NewSumAggregation().Field("price")
- // terms聚合嵌套指标聚合
- aggs.SubAggregation("total_price", sumAggs)
提示:golang elasticsearch的用法,本质上还是对elasticsearch接口的封装,所以用法跟elasticsearch的语法完全一致。
golang elasticsearch 索引操作API 创建索引
- // 创建ES client
- client, err := elastic.NewClient()
- if err != nil {
- // Handle error
- panic(err)
- }
-
- // 执行ES请求需要提供一个上下文对象
- ctx := context.Background()
-
- // 索引mapping定义,这里仿微博消息结构定义
- const mapping = `
- {
- "mappings": {
- "properties": {
- "user": {
- "type": "keyword"
- },
- "message": {
- "type": "text"
- },
- "image": {
- "type": "keyword"
- },
- "created": {
- "type": "date"
- },
- "tags": {
- "type": "keyword"
- },
- "location": {
- "type": "geo_point"
- },
- "suggest_field": {
- "type": "completion"
- }
- }
- }
- }`
// 创建索引
- _, err = client.CreateIndex("weibo").BodyString(mapping).Do(ctx)
- if err != nil {
- // Handle error
- panic(err)
- }
- //删除blog索引
- client.DeleteIndex("blog").Do(ctx)
- // 检测下weibo索引是否存在
- exists, err := client.IndexExists("weibo").Do(ctx)
- if err != nil {
- // Handle error
- panic(err)
- }
参考:
• ES教程: https://www.tizi365.com/archives/590.html
• ES聚合分析: https://www.tizi365.com/archives/644.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。