2021-07-08

前言

目前golang操作elasticsearch的第三方包中最流行的是:

https://github.com/olivere/elastic

版本说明

golang的elastic开发包和elasticsearch版本有一些对应关系,在开发前需要注意下,必须选择正确的版本,
下面是golang elastic开发包和elasticsearch版本关系表:
elasticsearch versiongo elastic version
7.x7.0
6.x6.0
5.x5.0

1.go-elastic快速入门

1.1 创建客户端

在操作ES之前需要创建一个client,用于操作ES,在创建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("连接成功")
	}
}

1.2 创建索引

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)
		}
	}
}

1.3 插入数据

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格式。使用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)

1.4 查询数据

// 根据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)
}

# 手动将文档内容转换成go struct对象
msg2 := Weibo{}
// 提取文档内容,原始类型是json数据
data, _ := get1.Source.MarshalJSON()
// 将json转成struct结果
json.Unmarshal(data, &msg2)
// 打印结果
fmt.Println(msg2.Message)

1.5 更新数据

//根据文档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)
}

1.6 删除数据

// 根据id删除一条数据
_, err := client.Delete().
		Index("weibo").
		Id("1").
		Do(ctx)
if err != nil {
	// Handle error
	panic(err)
}

2.golang 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)
}

3 .golang elasticsearch文档操作(CRUD)

介绍go语言对elasticsearch文档的基础操作:增、删、改、查等

3.1 添加文档

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)
}

3.2 查询文档

根据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等于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)
	}

3.3 更新文档

根据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)

3.4 删除文档

根据id删除文档:
// 根据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)

4. go-elasticsearch 查询语法

4.1 精确匹配单个字段

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)
			}
		}
	}
}

4.2 通过terms实现SQL的in查询

通过terms语法可以实现多值查询效果。如:
// 创建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)             // 执行请求
		

4.3 匹配单个字段

某个字段使用全文搜索,也就是elasticsearch中的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) 
		

4.4 范围查询

实现类似于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)

4.5 bool组合查询

must条件:类似于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_not条件:与must条件相反。
// 创建bool查询
boolQuery := elastic.NewBoolQuery().Must()

// 创建term查询
termQuery := elastic.NewTermQuery("Author", "tizi")

// 设置bool查询的must not条件
boolQuery.MustNot(termQuery)

should条件:类似于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)

5. go-elasticsearch 聚合分析

elasticsearch聚合分析主要包括:
       —指标聚合
       —桶聚合
这两种聚合可以嵌套使用,桶聚合通常用于对数据分组,然后分组内的数据可以使用指标聚合汇总数据。
如下面这个例子:
// 创建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)
        }
    }
}

5.1 指标聚合

     值聚合:主要用于统计文档总数,类似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)
	}
}
基数聚合:也是用于统计文档的总数,跟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)
}

5.2 桶聚合

	1.Terms聚合:
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)
	}
}
	2.Histogram聚合
// 创建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)
}
	3.Date histogram聚合
// 创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)
}
	4.Range聚合
// 创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)
}
	5.嵌套聚合
// 创terms桶聚合
aggs := elastic.NewTermsAggregation().Field("shop_id")
// 创建Sum指标聚合
sumAggs := elastic.NewSumAggregation().Field("price")
// terms聚合嵌套指标聚合
aggs.SubAggregation("total_price", sumAggs)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值