万字详解!在 Go 语言中操作 ElasticSearch

简介: 本文档通过示例代码详细介绍了如何在Go应用中使用`olivere/elastic`库,涵盖了从连接到Elasticsearch、管理索引到执行复杂查询的整个流程。

在大数据和搜索引擎技术不断进步的今天,ElasticSearch 已成为业界内非常流行的搜索引擎解决方案,被广泛应用于日志分析、全文搜索、数据分析等领域。针对 Go 语言开发者来说,olivere/elastic 是一个非常强大而且易于使用的 ElasticSearch 客户端库,允许开发者在 Go 应用中轻松地操作 ElasticSearch。

在本文中,我们将通过简单的代码演示,来介绍如何在 Go 应用中使用 olivere/elastic 包来操作 ElasticSearch。

下载 olivere/elastic

首先,我们需要下载这个客户端库。在终端中运行以下命令即可安装:

# 这里使用的是 v7 版本
go get github.com/olivere/elastic/v7

使用

开始之前

在我们深入代码之前,确保你已经有了运行中的 ElasticSearch 服务。本文假设你已经掌握了 ElasticSearch 的基本知识,比如索引的概念、文档以及基本的操作等。

下面是一个简单的 Go 应用示例,展示了如何使用 olivere/elastic 包连接 ElasticSearch、执行基本的操作。

这里直接以代码 demo 的形式呈现,具体含义,请见注释。若有错误,还望指正,感谢!


/**
GitHub: https://github.com/olivere/elastic
官方文档示例: https://olivere.github.io/elastic/
下载:go get github.com/olivere/elastic/v7
*/
package main

import (
    "context"
    "encoding/json"
    "errors"
    "fmt"
    "sync"
    "time"

    "github.com/olivere/elastic/v7"
)

var (
    ESClient *elastic.Client
    once     sync.Once
)

type RcpGoodsImgChecksES struct {
   
    AppName     int    `json:"app_name"`
    GoodsId     string `json:"goods_id"`
    SiteId      int    `json:"site_id"`
    CheckStatus int    `json:"check_status"`
    CreatedAt   int    `json:"created_at"`
    UpdatedAt   int    `json:"updated_at"`
}

const RcpGoodsImgChecksESIndex = "rcp_goods_img_checks"

const RcpGoodsImgChecksESMapping = `
{
    "mappings":{
        "properties":{
            "app_name":{
                "type": "integer"
            },
            "goods_id":{
                "type": "keyword"
            },
            "site_id":{
                "type": "keyword"
            },
            "check_status":{
                "type": "integer"
            },
            "created_at":{
                "type": "date"
            },
            "updated_at":{
                "type": "date"
            }
        }
    }
}`

const esUrl = "http://127.0.0.1:9200"

func main() {
   
    var err error

    // 创建 es 连接
    ConnectES(
        // 如果 es 是通过 docker 安装,如果不设置 `elastic.SetSniff(false)` 那么则会报错
        elastic.SetSniff(false),            // 允许指定弹性是否应该定期检查集群,默认为 true, 会把请求 http://ip:port/_nodes/http,并将其返回的 publish_address 作为请求路径
        elastic.SetURL([]string{
   esUrl}...), // 服务地址
        elastic.SetBasicAuth("", ""),       // 设置认证账号和密码
        // elastic.SetHealthcheckInterval(time.Second*5), // 心跳检查,间隔时间
        // elastic.SetGzip(true),                         // 启用 gzip 压缩
    )

    // Ping the Elasticsearch server to get e.g. the version number
    info, code, err := Ping(esUrl)
    if err != nil {
   
        // Handle error
        panic(err)
    }
    // Elasticsearch returned with code 200 and version 7.9.3
    fmt.Printf("Elasticsearch returned with code %d and version %s\n", code, info.Version.Number)

    // 直接打印出 es 版本号
    esVersion, err := ESClient.ElasticsearchVersion(esUrl)
    if err != nil {
   
        panic(err)
    }
    // Elasticsearch version 7.9.3
    fmt.Printf("Elasticsearch version %s\n", esVersion)

    // 删除索引
    // testDeleteIndex()

    // 判断索引是否存在,如果不存在时,则创建
    err = CreateIndexIfNotExists(RcpGoodsImgChecksESIndex, RcpGoodsImgChecksESMapping)
    if err != nil {
   
        panic(err)
    }

}

在 Go 程序中,我们首先需要初始化一个 Elasticsearch 客户端实例。这里我们使用了一个全局变量 ESClient 和一个同步原语 once 来确保客户端只被初始化一次。

简单封装的一些常见方法

创建 es 连接

// ConnectES 创建 es 连接
func ConnectES(options ...elastic.ClientOptionFunc) {
   
    once.Do(func() {
   
        // client, err := elastic.NewClient(elastic.SetSniff(false), elastic.SetURL("http://127.0.0.1:9200"))
        var err error
        ESClient, err = elastic.NewClient(options...)
        if err != nil {
   
            panic(err)
        }
    })
}

ping

func Ping(url string) (*elastic.PingResult, int, error) {
   
    return ESClient.Ping(url).Do(context.Background())
}

索引不存在时,创建索引

// CreateIndexIfNotExists 索引不存在时,创建索引
// index 索引名称
// mapping 数据类型
func CreateIndexIfNotExists(index, mapping string) error {
   
    ctx := context.Background()
    exists, err := ESClient.IndexExists(index).Do(ctx)
    if err != nil {
   
        return err
    }
    if exists {
   
        return nil
    }

    info, err := ESClient.CreateIndex(index).BodyString(mapping).Do(ctx)
    // info, err := ESClient.CreateIndex(index).Do(ctx)  // 如果只是想创建索引时,那么就不需要 BodyString() 方法
    if err != nil {
   
        return err
    }
    if !info.Acknowledged {
   
        return errors.New(fmt.Sprintf("ES 创建索引 [%s] 失败", index))
    }
    return nil
}

删除索引

// DeleteIndex 删除索引
// index 索引名称
func DeleteIndex(index string) (*elastic.IndicesDeleteResponse, error) {
   
    info, err := ESClient.DeleteIndex(index).Do(context.Background())
    if err != nil {
   
        return nil, err
    }
    if !info.Acknowledged {
   
        return nil, errors.New(fmt.Sprintf("ES 删除索引 [%s] 失败", index))
    }
    return info, err
}

单条添加

// CreateDoc 单条添加
// index 索引
// id 文档 id(可以直接为空字符串,当实参为空字符串时,es 会主动随机生成)
// body 需要添加的内容
func CreateDoc(index, id string, body interface{
   }) (*elastic.IndexResponse, error) {
   
    client := ESClient.Index().Index(index)
    if "" != id {
   
        client = client.Id(id)
    }
    return client.BodyJson(body).Do(context.Background())
}

单条更新

// UpdateDoc 单条更新
// index 索引
// id 记录 id
// body 需要更新的内容 (建议只使用 map[string]interface{} 进行更新指定字段且需要注意 map 中的 key 需要和 es 中的 key 完全匹配,否则 es 会认为新增字段,不要使用 struct 否则会将某些值初始化零值)
func UpdateDoc(index, id string, body interface{
   }) (*elastic.UpdateResponse, error) {
   
    return ESClient.Update().Index(index).Id(id).Doc(body).Do(context.Background())
}

删除文档

// DeleteDoc 删除文档
// index 索引
// id 需要删除的文档记录 id
func DeleteDoc(index, id string) (*elastic.DeleteResponse, error) {
   
    return ESClient.Delete().Index(index).Id(id).Do(context.Background())
}

批量添加

// CreateBulkDoc 批量添加
// index 索引
// ids 需要新建的 id 数组(可以为空的字符串切片)
// body 需要添加的内容
// 需要注意:ids 和 body 的顺序要一一对应
func CreateBulkDoc(index string, ids []string, body []interface{
   }) (*elastic.BulkResponse, error) {
   
    bulkRequest := ESClient.Bulk()
    for k, v := range body {
   
        tmp := v
        doc := elastic.NewBulkIndexRequest().Index(index).Doc(tmp)
        if len(ids) > 0 {
   
            doc = doc.Id(ids[k])
        }
        bulkRequest = bulkRequest.Add(doc)
    }
    return bulkRequest.Do(context.Background())
}

批量更新

// UpdateBulkDoc 批量更新
// index 索引
// ids 需要更新的 id 数组
// body 需要更新的 id 对应的数据 (建议只使用 []map[string]interface{} 进行更新指定字段且需要注意 map 中的 key 需要和 es 中的 key 完全匹配,否则 es 会认为新增字段,不要使用 struct 否则会将某些值初始化零值)
// 需要注意:ids 和 body 的顺序要一一对应
func UpdateBulkDoc(index string, ids []string, body []interface{
   }) (*elastic.BulkResponse, error) {
   
    bulkRequest := ESClient.Bulk()
    for k, v := range body {
   
        tmp := v
        doc := elastic.NewBulkUpdateRequest().Index(index).Id(ids[k]).Doc(tmp).DocAsUpsert(true)
        bulkRequest = bulkRequest.Add(doc)
    }
    return bulkRequest.Do(context.Background())
}

批量删除

// DeleteBulkDoc 批量删除
// index 索引
// ids 需要删除的 id 数组
func DeleteBulkDoc(index string, ids []string) (*elastic.BulkResponse, error) {
   
    bulkRequest := ESClient.Bulk()
    for _, v := range ids {
   
        tmp := v
        req := elastic.NewBulkDeleteRequest().Index(index).Id(tmp)
        bulkRequest = bulkRequest.Add(req)
    }
    return bulkRequest.Do(context.Background())
}

通过文档 id 取出数据

// FirstDoc 通过 id 取出数据
// index 索引
// id 需要取的文档记录 id
func FirstDoc(index, id string) (*elastic.GetResult, error) {
   
    return ESClient.Get().Index(index).Id(id).Do(context.Background())
}

打印出查询条件

func PrintQuery(src interface{
   }) {
   
    fmt.Println("开始打印参数 ====>")
    data, err := json.MarshalIndent(src, "", "  ")
    if err != nil {
   
        panic(err)
    }
    fmt.Println(string(data))
    fmt.Println("打印参数结束 ====>")
}

查询出数据

func querySearch(query elastic.Query) {
   
    if querySrc, err := query.Source(); err == nil {
   
        PrintQuery(querySrc)
    }
    queryRet, err := ESClient.Search().Index(RcpGoodsImgChecksESIndex).Query(query).Do(context.Background())
    if err != nil {
   
        panic(err)
    }
    fmt.Printf("查询到的结果总数为 %v \n", queryRet.TotalHits())
    for _, v := range queryRet.Hits.Hits {
   
        var tmp RcpGoodsImgChecksES
        json.Unmarshal(v.Source, &tmp)
        fmt.Printf("已经命中查询的数据为 ==> %+v \n %+v \n\n", v.Id, tmp)
    }
}

测试方法

删除索引

func testDeleteIndex() {
   
    // 删除索引
    deleteIndexRet, err := DeleteIndex(RcpGoodsImgChecksESIndex)
    if err != nil {
   
        panic(err)
    }
    // deleteIndexRet  ==> &{Acknowledged:true}
    fmt.Printf("deleteIndexRet  ==> %+v \n\n", deleteIndexRet)
}

创建文档

func testCreateDoc() {
   
    // 创建文档
    now := time.Now().Unix()

    createDocRet, err := CreateDoc(RcpGoodsImgChecksESIndex, "2_18_alex111", RcpGoodsImgChecksES{
   
        AppName:     2,
        GoodsId:     "alex111",
        SiteId:      18,
        CheckStatus: 1,
        CreatedAt:   int(now),
        UpdatedAt:   int(now),
    })
    if err != nil {
   
        panic(err)
    }
    // CreateDoc ==> &{Index:rcp_goods_img_checks Type:_doc Id:2_18_alex111 Version:1 Result:created Shards:0xc00020c2c0 SeqNo:0 PrimaryTerm:1 Status:0 ForcedRefresh:false}
    fmt.Printf("CreateDoc ==> %+v \n\n", createDocRet)
}

通过文档 id 的形式更新文档

func testUpdateDoc() {
   
    // 通过文档 id 的形式更新文档
    updateDocRet, err := UpdateDoc(RcpGoodsImgChecksESIndex, "2_18_alex111", map[string]interface{
   }{
   
        "check_status": 2,
        "updated_at":   int(time.Now().Unix()),
    })
    if err != nil {
   
        panic(err)
    }
    // UpdateDoc ==> &{Index:rcp_goods_img_checks Type:_doc Id:2_18_alex111 Version:2 Result:updated Shards:0xc0002bc280 SeqNo:1 PrimaryTerm:1 Status:0 ForcedRefresh:false GetResult:<nil>}
    fmt.Printf("UpdateDoc ==> %+v \n\n", updateDocRet)
}

通过 Script 方式更新文档(单字段更新,借助文档 id 更新)

func testUpdateDocScript() {
   
    // 通过 Script 方式更新文档(单字段更新,借助文档 id 更新)
    updateDocScript, err := ESClient.Update().
        Index(RcpGoodsImgChecksESIndex).
        Id("2_18_alex111").
        Script(elastic.NewScript("ctx._source.site_id=11")).
        Do(context.Background())
    if err != nil {
   
        panic(err)
    }
    // updateDocScript  ==> &{Index:rcp_goods_img_checks Type:_doc Id:2_18_alex111 Version:3 Result:updated Shards:0xc000098280 SeqNo:2 PrimaryTerm:1 Status:0 ForcedRefresh:false GetResult:<nil>}
    fmt.Printf("updateDocScript  ==> %+v \n\n", updateDocScript)
}

通过条件 Script 方式更新文档(单字段更新,根据查询条件批量更新字段)

func testUpdateDocScriptQuery() {
   
    // 通过条件 Script 方式更新文档(单字段更新,根据查询条件批量更新字段)
    updateDocScriptQuery, err := ESClient.UpdateByQuery(RcpGoodsImgChecksESIndex).
        Query(elastic.NewTermQuery("goods_id", "alex111")).
        Script(elastic.NewScript("ctx._source.check_status=23")).
        ProceedOnVersionConflict().Do(context.Background())
    if err != nil {
   
        panic(err)
    }
    // updateDocScriptQuery  ==> &{Header:map[] Took:47 SliceId:<nil> TimedOut:false Total:2 Updated:2 Created:0 Deleted:0 Batches:1 VersionConflicts:0 Noops:0 Retries:{Bulk:0 Search:0} Throttled: ThrottledMillis:0 RequestsPerSecond:-1 Canceled: ThrottledUntil: ThrottledUntilMillis:0 Failures:[]}
    fmt.Printf("updateDocScriptQuery  ==> %+v \n\n", updateDocScriptQuery)
}

通过文档 id 查找文档

func testFirstDoc() {
   
    // 通过文档 id 查找文档
    firstDocRet, err := FirstDoc(RcpGoodsImgChecksESIndex, "2_18_alex111")
    if err != nil {
   
        panic(err)
    }
    if firstDocRet.Found {
    // 表示找到了数据
        // FirstDoc ==>  &{Index:rcp_goods_img_checks Type:_doc Id:2_18_alex111 Uid: Routing: Parent: Version:0xc000282a10 SeqNo:0xc000282a18 PrimaryTerm:0xc000282a20 Source:[123 34 97 112 112 95 110 97 109 101 34 58 50 44 34 117 112 100 97 116 101 100 95 97 116 34 58 49 54 54 48 53 55 57 52 56 54 44 34 115 105 116 101 95 105 100 34 58 49 56 44 34 103 111 111 100 115 95 105 100 34 58 34 97 108 101 120 49 49 49 34 44 34 99 114 101 97 116 101 100 95 97 116 34 58 49 54 54 48 53 55 57 52 56 54 44 34 99 104 101 99 107 95 115 116 97 116 117 115 34 58 50 51 125] Found:true Fields:map[] Error:<nil>} Source: {"app_name":2,"updated_at":1660579486,"site_id":18,"goods_id":"alex111","created_at":1660579486,"check_status":23}
        fmt.Printf("FirstDoc ==>  %+v Source: %+v \n\n", firstDocRet, string(firstDocRet.Source))
    }
}

通过文档 id 删除文档

func testDeleteDoc() {
   
    // 通过文档 id 删除文档
    deleteDocRet, err := DeleteDoc(RcpGoodsImgChecksESIndex, "2_18_alex111")
    if err != nil {
   
        panic(err)
    }
    // DeleteDoc  ==> &{Index:rcp_goods_img_checks Type:_doc Id:2_18_alex111 Version:6 Result:deleted Shards:0xc00007e2c0 SeqNo:7 PrimaryTerm:1 Status:0 ForcedRefresh:false}
    fmt.Printf("DeleteDoc  ==> %+v \n\n", deleteDocRet)
}

批量创建

func testCreateBulkDoc() {
   
    now := time.Now().Unix()
    // 批量创建
    createBulkDocRet, err := CreateBulkDoc(RcpGoodsImgChecksESIndex, []string{
   "h1", "h2", "h3"}, []interface{
   }{
   
        RcpGoodsImgChecksES{
   
            AppName:     2,
            GoodsId:     "h1_goods_id",
            SiteId:      17,
            CheckStatus: 1,
            CreatedAt:   int(now),
            UpdatedAt:   int(now),
        },
        RcpGoodsImgChecksES{
   
            AppName:     1,
            GoodsId:     "h2_goods_id",
            SiteId:      19,
            CheckStatus: 4,
            CreatedAt:   int(now),
            UpdatedAt:   int(now),
        },
        RcpGoodsImgChecksES{
   
            AppName:     3,
            GoodsId:     "h3_goods_id",
            SiteId:      19,
            CheckStatus: 2,
            CreatedAt:   int(now),
            UpdatedAt:   int(now),
        },
    })
    if err != nil {
   
        panic(err)
    }
    // CreateBulkDoc ==> &{Took:5 Errors:false Items:[map[index:0xc00019c200] map[index:0xc00019c280] map[index:0xc00019c300]]}
    fmt.Printf("CreateBulkDoc ==> %+v \n\n", createBulkDocRet)
}

批量更新

func testUpdateBulkDoc() {
   
    // 批量更新
    updateBulkDocRet, err := UpdateBulkDoc(RcpGoodsImgChecksESIndex, []string{
   "h1", "h3"}, []interface{
   }{
   
        map[string]interface{
   }{
   
            "check_status": 2,
            "updated_at":   int(time.Now().Unix()),
        },
        map[string]interface{
   }{
   
            "site_id":    20,
            "updated_at": int(time.Now().Unix()),
        },
    })
    if err != nil {
   
        panic(err)
    }
    // UpdateBulkDoc ==> &{Took:6 Errors:false Items:[map[update:0xc0001e2080] map[update:0xc0001e2100]]}
    fmt.Printf("UpdateBulkDoc ==> %+v \n\n", updateBulkDocRet)
}

通过文档 id 批量删除

func testDeleteBulkDoc() {
   
    // 通过文档 id 批量删除
    deleteBulkDocRet, err := DeleteBulkDoc(RcpGoodsImgChecksESIndex, []string{
   "h2", "h3_goods_id"})
    if err != nil {
   
        panic(err)
    }
    fmt.Printf("DeleteBulkDoc ==> %+v \n\n", deleteBulkDocRet)

    // DeleteBulkDoc ==> &{Took:36 Errors:false Items:[map[delete:0xc0000ea080] map[delete:0xc0000ea100]]}
}

按照条件删除

func testDeleteByQuery() {
   
    // 按照条件删除
    deleteDocByQuery, err := ESClient.DeleteByQuery(RcpGoodsImgChecksESIndex).
        Query(elastic.NewRangeQuery("updated_at").Gte(0).Lte(1660579923)).
        Do(context.Background())
    if err != nil {
   
        panic(err)
    }
    fmt.Printf("deleteDocByQuery ==> %+v \n\n", deleteDocByQuery)

    // deleteDocByQuery ==> &{Header:map[] Took:36 SliceId:<nil> TimedOut:false Total:3 Updated:0 Created:0 Deleted:3 Batches:1 VersionConflicts:0 Noops:0 Retries:{Bulk:0 Search:0} Throttled: ThrottledMillis:0 RequestsPerSecond:-1 Canceled: ThrottledUntil: ThrottledUntilMillis:0 Failures:[]}
}

term

func testTermQuery() {
   
    // term
    query1 := elastic.NewTermQuery("goods_id", "h2_goods_id")
    querySearch(query1)

    // 开始打印参数 ====>
    // {
   
    //  "term": {
   
    //    "goods_id": "h2_goods_id"
    //  }
    // }
    // 打印参数结束 ====>
    // 查询到的结果总数为 1
    // 已经命中查询的数据为 ==> h2
    // {AppName:1 GoodsId:h2_goods_id SiteId:19 CheckStatus:4 CreatedAt:1660579860 UpdatedAt:1660579860}
}

terms

func testTermsQuery() {
   
    // terms [where goods_id in ('h3_goods_id', 'h2_goods_id')]
    query2 := elastic.NewTermsQuery("goods_id", []interface{
   }{
   "h3_goods_id", "h2_goods_id"}...)
    querySearch(query2)

    // 开始打印参数 ====>
    // {
   
    //  "terms": {
   
    //    "goods_id": [
    //      "h3_goods_id",
    //      "h2_goods_id"
    //    ]
    //  }
    // }
    // 打印参数结束 ====>
    // 查询到的结果总数为 2
    // 已经命中查询的数据为 ==> h2
    // {AppName:1 GoodsId:h2_goods_id SiteId:19 CheckStatus:4 CreatedAt:1660579860 UpdatedAt:1660579860}
    //
    // 已经命中查询的数据为 ==> h3
    // {AppName:3 GoodsId:h3_goods_id SiteId:20 CheckStatus:2 CreatedAt:1660579860 UpdatedAt:1660579923}
}

range 范围查找

func testRangeQuery() {
   
    // 范围查找 [where updated_at >= 0 and updated_at <= 1659695758]
    // Gt(大于)、Lt(小于)、Gte(大于等于)、Lte(小于等于)
    query3 := elastic.NewRangeQuery("updated_at").Gte(0).Lte(1659695758)
    querySearch(query3)

    // 开始打印参数 ====>
    // {
   
    //  "range": {
   
    //    "updated_at": {
   
    //      "from": 0,
    //      "include_lower": true,
    //      "include_upper": true,
    //      "to": 1659695758
    //    }
    //  }
    // }
    // 打印参数结束 ====>
    // 查询到的结果总数为 0
}

match_all

func testMatchAllQuery() {
   
    // match_all
    query4 := elastic.NewMatchAllQuery()
    querySearch(query4)

    // 开始打印参数 ====>
    // {
   
    //  "match_all": {}
    // }
    // 打印参数结束 ====>
    // 查询到的结果总数为 4
    // 已经命中查询的数据为 ==> 2_19_alex111
    // {AppName:2 GoodsId:alex111 SiteId:18 CheckStatus:23 CreatedAt:1660579517 UpdatedAt:1660579517}
    //
    // 已经命中查询的数据为 ==> h2
    // {AppName:1 GoodsId:h2_goods_id SiteId:19 CheckStatus:4 CreatedAt:1660579860 UpdatedAt:1660579860}
    //
    // 已经命中查询的数据为 ==> h1
    // {AppName:2 GoodsId:h1_goods_id SiteId:17 CheckStatus:2 CreatedAt:1660579860 UpdatedAt:1660579923}
    //
    // 已经命中查询的数据为 ==> h3
    // {AppName:3 GoodsId:h3_goods_id SiteId:20 CheckStatus:2 CreatedAt:1660579860 UpdatedAt:1660579923}
}

match

func testMatchQuery() {
   
    // match
    query5 := elastic.NewMatchQuery("goods_id", "h2_goods_id")
    querySearch(query5)

    // 开始打印参数 ====>
    // {
   
    //  "match": {
   
    //    "goods_id": {
   
    //      "query": "h2_goods_id"
    //    }
    //  }
    // }
    // 打印参数结束 ====>
    // 查询到的结果总数为 1
    // 已经命中查询的数据为 ==> h2
    // {AppName:1 GoodsId:h2_goods_id SiteId:19 CheckStatus:4 CreatedAt:1660579860 UpdatedAt:1660579860}
}

match_phrase

func testMatchPhraseQuery() {
   
    // match_phrase
    query6 := elastic.NewMatchPhraseQuery("goods_id", "h2_goods_id")
    querySearch(query6)

    // 开始打印参数 ====>
    // {
   
    //  "match_phrase": {
   
    //    "goods_id": {
   
    //      "query": "h2_goods_id"
    //    }
    //  }
    // }
    // 打印参数结束 ====>
    // 查询到的结果总数为 1
    // 已经命中查询的数据为 ==> h2
    // {AppName:1 GoodsId:h2_goods_id SiteId:19 CheckStatus:4 CreatedAt:1660579860 UpdatedAt:1660579860}
}

match_phrase_prefix

func testMatchPhrasePrefixQuery() {
   
    // match_phrase_prefix
    // 这里因为类型不支持前缀匹配,可能会直接报错
    query7 := elastic.NewMatchPhrasePrefixQuery("goods_id", "h2_")
    querySearch(query7)
}

regexp

func testRegexpQuery() {
   
    // regexp
    // 搜索 goods_id 字段对应的值以 `h` 开头的所有文档
    query8 := elastic.NewRegexpQuery("goods_id", "h.*")
    querySearch(query8)

    // 开始打印参数 ====>
    // {
   
    //  "regexp": {
   
    //    "goods_id": {
   
    //      "value": "h.*"
    //    }
    //  }
    // }
    // 打印参数结束 ====>
    // 查询到的结果总数为 3
    // 已经命中查询的数据为 ==> h2
    // {AppName:1 GoodsId:h2_goods_id SiteId:19 CheckStatus:4 CreatedAt:1660579860 UpdatedAt:1660579860}
    //
    // 已经命中查询的数据为 ==> h1
    // {AppName:2 GoodsId:h1_goods_id SiteId:17 CheckStatus:2 CreatedAt:1660579860 UpdatedAt:1660579923}
    //
    // 已经命中查询的数据为 ==> h3
    // {AppName:3 GoodsId:h3_goods_id SiteId:20 CheckStatus:2 CreatedAt:1660579860 UpdatedAt:1660579923}
}

bool 组合查询

func testBoolQuery() {
   
    // 组合查询
    boolQuery := elastic.NewBoolQuery()
    // must
    boolQuery.Must(elastic.NewTermQuery("check_status", 2))
    // should
    boolQuery.Should(elastic.NewTermQuery("app_name", 20))
    // must_not
    boolQuery.MustNot(elastic.NewTermQuery("site_id", 18))
    // filter
    boolQuery.Filter(elastic.NewRangeQuery("updated_at").Gte(0).Lte(1660579923))
    querySearch(boolQuery)

    // 开始打印参数 ====>
    // {
   
    //  "bool": {
   
    //    "filter": {
   
    //      "range": {
   
    //        "updated_at": {
   
    //          "from": 0,
    //          "include_lower": true,
    //          "include_upper": true,
    //          "to": 1660579923
    //        }
    //      }
    //    },
    //    "must": {
   
    //      "term": {
   
    //        "check_status": 2
    //      }
    //    },
    //    "must_not": {
   
    //      "term": {
   
    //        "site_id": 18
    //      }
    //    },
    //    "should": {
   
    //      "term": {
   
    //        "app_name": 20
    //      }
    //    }
    //  }
    // }
    // 打印参数结束 ====>
    // 查询到的结果总数为 2
    // 已经命中查询的数据为 ==> h1
    // {AppName:2 GoodsId:h1_goods_id SiteId:17 CheckStatus:2 CreatedAt:1660579860 UpdatedAt:1660579923}
    //
    // 已经命中查询的数据为 ==> h3
    // {AppName:3 GoodsId:h3_goods_id SiteId:20 CheckStatus:2 CreatedAt:1660579860 UpdatedAt:1660579923}
}

分页查询,并排序

func testPageSort() {
   
    // 分页查询,并排序
    // from 为起始偏移量(offset)默认为 0,size 为每页显示数(limit)默认为 10
    // from 等于当前页码数减去一的商然后乘以每页显示数
    // Sort() 第二个参数,true 为升序、false 为降序
    pageRet, err := ESClient.Search().Index(RcpGoodsImgChecksESIndex).From(0).Size(20).Sort("updated_at", false).Do(context.Background())
    if err != nil {
   
        panic(err)
    }
    for _, v := range pageRet.Hits.Hits {
   
        var tmp RcpGoodsImgChecksES
        json.Unmarshal(v.Source, &tmp)
        fmt.Printf("分页查询,已经命中查询的数据为 ==> %+v \n %+v \n\n", v.Id, tmp)
    }

    // 分页查询,已经命中查询的数据为 ==> h1
    // {AppName:2 GoodsId:h1_goods_id SiteId:17 CheckStatus:2 CreatedAt:1660579860 UpdatedAt:1660579923}
    //
    // 分页查询,已经命中查询的数据为 ==> h3
    // {AppName:3 GoodsId:h3_goods_id SiteId:20 CheckStatus:2 CreatedAt:1660579860 UpdatedAt:1660579923}
    //
    // 分页查询,已经命中查询的数据为 ==> h2
    // {AppName:1 GoodsId:h2_goods_id SiteId:19 CheckStatus:4 CreatedAt:1660579860 UpdatedAt:1660579860}
    //
    // 分页查询,已经命中查询的数据为 ==> 2_19_alex111
    // {AppName:2 GoodsId:alex111 SiteId:18 CheckStatus:23 CreatedAt:1660579517 UpdatedAt:1660579517}
}

多字段排序

func testMultiFieldSort() {
   
    // 多字段排序
    sortsBuilders := []elastic.Sorter{
   
        elastic.NewFieldSort("check_status").Asc(), // 升序
        elastic.NewFieldSort("created_at").Desc(),  // 降序
    }
    sortRet, err := ESClient.Search().Index(RcpGoodsImgChecksESIndex).SortBy(sortsBuilders...).Do(context.Background())
    if err != nil {
   
        panic(err)
    }
    for _, v := range sortRet.Hits.Hits {
   
        var tmp RcpGoodsImgChecksES
        json.Unmarshal(v.Source, &tmp)
        fmt.Printf("多字段排序,已经命中查询的数据为 ==> %+v \n %+v \n\n", v.Id, tmp)
    }

    // 多字段排序,已经命中查询的数据为 ==> h1
    // {AppName:2 GoodsId:h1_goods_id SiteId:17 CheckStatus:2 CreatedAt:1660579860 UpdatedAt:1660579923}
    //
    // 多字段排序,已经命中查询的数据为 ==> h3
    // {AppName:3 GoodsId:h3_goods_id SiteId:20 CheckStatus:2 CreatedAt:1660579860 UpdatedAt:1660579923}
    //
    // 多字段排序,已经命中查询的数据为 ==> h2
    // {AppName:1 GoodsId:h2_goods_id SiteId:19 CheckStatus:4 CreatedAt:1660579860 UpdatedAt:1660579860}
    //
    // 多字段排序,已经命中查询的数据为 ==> 2_19_alex111
    // {AppName:2 GoodsId:alex111 SiteId:18 CheckStatus:23 CreatedAt:1660579517 UpdatedAt:1660579517}
}

返回指定字段(只查询指定字段)

func testFetchSource() {
   
    // 返回指定字段
    includeFields := elastic.NewFetchSourceContext(true).Include([]string{
   "app_name", "goods_id"}...)
    includeRet, err := ESClient.Search().Index(RcpGoodsImgChecksESIndex).FetchSourceContext(includeFields).Do(context.Background())
    if err != nil {
   
        panic(err)
    }
    for _, v := range includeRet.Hits.Hits {
   
        var tmp RcpGoodsImgChecksES
        json.Unmarshal(v.Source, &tmp)
        fmt.Printf("返回指定字段,已经命中查询的数据为 ==> %+v \n %+v \n\n", v.Id, tmp)
    }

    // 返回指定字段,已经命中查询的数据为 ==> 2_19_alex111
    // {AppName:2 GoodsId:alex111 SiteId:0 CheckStatus:0 CreatedAt:0 UpdatedAt:0}
    //
    // 返回指定字段,已经命中查询的数据为 ==> h2
    // {AppName:1 GoodsId:h2_goods_id SiteId:0 CheckStatus:0 CreatedAt:0 UpdatedAt:0}
    //
    // 返回指定字段,已经命中查询的数据为 ==> h1
    // {AppName:2 GoodsId:h1_goods_id SiteId:0 CheckStatus:0 CreatedAt:0 UpdatedAt:0}
    //
    // 返回指定字段,已经命中查询的数据为 ==> h3
    // {AppName:3 GoodsId:h3_goods_id SiteId:0 CheckStatus:0 CreatedAt:0 UpdatedAt:0}
}

查询数据总数

func testTotal() {
   
    // 查询总命中计数
    total, err := ESClient.Count().Index(RcpGoodsImgChecksESIndex).Do(context.Background())
    if err != nil {
   
        panic(err)
    }

    // 查询总命中计数,已经命中查询的数据为 ==> 2
    fmt.Printf("查询总命中计数,已经命中查询的数据为 ==> %+v \n", total)
}

olivere/elastic 是 Go 语言操作 Elasticsearch 的一个非常有用的客户端库。它提供了简单直观的 API 来执行常见的 Elasticsearch 操作,如创建连接、Ping 服务、创建索引等。

通过本文的介绍,你应该能够开始使用 olivere/elastic 来集成 Elasticsearch 服务到你的 Go 应用中。

相关实践学习
以电商场景为例搭建AI语义搜索应用
本实验旨在通过阿里云Elasticsearch结合阿里云搜索开发工作台AI模型服务,构建一个高效、精准的语义搜索系统,模拟电商场景,深入理解AI搜索技术原理并掌握其实现过程。
ElasticSearch 最新快速入门教程
本课程由千锋教育提供。全文搜索的需求非常大。而开源的解决办法Elasricsearch(Elastic)就是一个非常好的工具。目前是全文搜索引擎的首选。本系列教程由浅入深讲解了在CentOS7系统下如何搭建ElasticSearch,如何使用Kibana实现各种方式的搜索并详细分析了搜索的原理,最后讲解了在Java应用中如何集成ElasticSearch并实现搜索。 &nbsp;
相关文章
|
6月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
325 2
|
编译器 Go
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
运维 监控 算法
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
|
8月前
|
Cloud Native 安全 Java
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
482 1
|
8月前
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
534 0
|
8月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
358 0
|
8月前
|
Cloud Native Java 中间件
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
403 0
|
8月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
441 0
|
8月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
8月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。