Elasticsearch 的 easyjson 示例

mailru/easyjson库的特点就是我们只需要提供结构体,然后用这些结构体生成编码解码的代码。

示例的项目名为elasticsearch/encoding/json

一、创建 models

在项目中新建 model 目录,目录中新建两个文件model.goresponse.go,在这两个文件头都添加一行代码生成注释:

//go:generate easyjson -all -snake_case $GOFILE
  • //后不能有空格

models.go:

//go:generate easyjson -all -snake_case $GOFILE

package model

import (
	"time"
)

type Article struct {
	ID        uint
	Title     string
	Body      string
	Published time.Time
	Author    *Author
}

type Author struct {
	FirstName string
	LastName  string
}

response.go:

//go:generate easyjson -all -snake_case $GOFILE

package model

type ErrorResponse struct {
	Info *ErrorInfo `json:"error,omitempty"`
}

type ErrorInfo struct {
	RootCause []*ErrorInfo
	Type      string
	Reason    string
	Phase     string
}

type IndexResponse struct {
	Index   string `json:"_index"`
	ID      string `json:"_id"`
	Version int    `json:"_version"`
	Result  string
}

type SearchResponse struct {
	Took int64
	Hits struct {
		Total struct {
			Value int64
		}
		Hits []*SearchHit
	}
}

type SearchHit struct {
	Score   float64 `json:"_score"`
	Index   string  `json:"_index"`
	Type    string  `json:"_type"`
	Version int64   `json:"_version,omitempty"`

	Source Article `json:"_source"`
}

二、完善主逻辑代码

main.go:

package main

import (
	"bytes"
	"elasticsearch/encoding/json/model"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/elastic/go-elasticsearch/v8"
	"github.com/elastic/go-elasticsearch/v8/esapi"
	"github.com/fatih/color"
	"github.com/mailru/easyjson"
)

var (
	out       = color.New(color.Reset)
	faint     = color.New(color.Faint)
	bold      = color.New(color.Bold)
	red       = color.New(color.FgRed)
	boldGreen = color.New(color.Bold, color.FgGreen)
	boldRed   = color.New(color.Bold, color.FgRed)

	articles []model.Article
	fnames   []string
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

func main() {
	es, err := elasticsearch.NewDefaultClient()
	if err != nil {
		fmt.Printf("Error creating the client: %s\n", err)
		os.Exit(2)
	}

	fnames = []string{"Alice", "John", "Mary"}

	for i, title := range []string{"One", "Two", "Three", "Four", "Five"} {
		articles = append(articles,
			model.Article{
				ID:        uint(i + 1),
				Title:     "Test" + title,
				Body:      "Lorem ipsum dolor sit amet, consectetur adipisicing elit",
				Published: time.Now().AddDate(i, 0, 0),
				Author: &model.Author{
					FirstName: fnames[rand.Intn(len(fnames))],
					LastName:  "Smith",
				},
			})
	}

	faint.Println("Indexing articles...")
	faint.Println(strings.Repeat("━", 80))

	var b bytes.Buffer
	for _, a := range articles {
		b.Reset()

        // 将 article 结构体序列化为 json,json 保存到 buffer 中
		if _, err := easyjson.MarshalToWriter(a, &b); err != nil {
			red.Println("Error decoding response", err)
			continue
		}

		res, err := es.Index(
			"articles",
			bytes.NewReader(b.Bytes()),
			es.Index.WithDocumentID(strconv.Itoa(int(a.ID))),
            // es.Index.WithVersion(-1), // <-- 取消此注释来触发异常
		)
		if err != nil {
			red.Printf("Error indexing article: %s\n", err)
			continue
		}
		defer res.Body.Close()

		if res.IsError() {
			printErrorResponse(res)
			os.Exit(2)
		}

		var ir model.IndexResponse
		if err := easyjson.UnmarshalFromReader(res.Body, &ir); err != nil {
			red.Println("Error decoding response", err)
			continue
		}

		boldGreen.Printf("[%s] ", res.Status())
		fmt.Println(
			faint.Sprint("result=")+out.Sprint(ir.Result),
			faint.Sprint("index=")+out.Sprint(ir.Index),
			faint.Sprint("ID=")+out.Sprint(ir.ID),
			faint.Sprint("version=")+out.Sprint(ir.Version),
		)

	}

	es.Indices.Refresh(es.Indices.Refresh.WithIndex("articles"))

	faint.Println("\nSearching articles...")
	faint.Println(strings.Repeat("━", 80))

	res, err := es.Search(
		es.Search.WithIndex("articles"),
		es.Search.WithQuery("one OR two"),
		// es.Search.WithQuery("{{{one OR two"), // <-- 取消此注释来触发异常
	)
	if err != nil {
		red.Printf("Error searching articles: %s\n", err)
		os.Exit(2)
	}
	defer res.Body.Close()

	if res.IsError() {
		printErrorResponse(res)
		os.Exit(2)
	}

	var sr model.SearchResponse
	if err := easyjson.UnmarshalFromReader(res.Body, &sr); err != nil {
		red.Println("Error decoding response", err)
		os.Exit(2)
	}

	faint.Printf("[%s] took=%d total=%d\n", res.Status(), sr.Took, sr.Hits.Total.Value)
	faint.Println(strings.Repeat("─", 80))

	for _, h := range sr.Hits.Hits {
		fmt.Println(
			out.Sprintf("%s,", strings.Join([]string{h.Source.Author.FirstName, h.Source.Author.LastName}, " ")),
			bold.Sprintf("%s", h.Source.Title),
			out.Sprintf("(%d)", h.Source.Published.Year()),
		)
		faint.Println(strings.Repeat("─", 80))
	}
}

// printErrorResponse 解码 es 响应,格式化后打印到终端
func printErrorResponse(res *esapi.Response) {
	bold.Printf("[%s] ", res.Status())

	var e model.ErrorResponse
	if err := easyjson.UnmarshalFromReader(res.Body, &e); err != nil {
		red.Println("Error decoding response:", err)
		return
	}
	boldRed.Print(e.Info.RootCause[0].Type)
	faint.Print(" > ")
	fmt.Println(e.Info.RootCause[0].Reason)
}

三、生成代码

拉取项目中需要的库:

go mod tidy

生成代码:

go generate ./model

之后,model 目录中会多出两个文件:model_easyjson.goresponse_easyjson.go

这两个文件中的代码为 model 中的结构体完成了mailru/easyjson规定的一些接口。

四、运行项目

go run main.go
Indexing articles...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[200 OK] result=updated index=articles ID=1 version=36
[200 OK] result=updated index=articles ID=2 version=34
[200 OK] result=updated index=articles ID=3 version=34
[200 OK] result=updated index=articles ID=4 version=34
[200 OK] result=updated index=articles ID=5 version=34

Searching articles...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[200 OK] took=2 total=0
────────────────────────────────────────────────────────────────────────────────
posted @ 2021-04-29 12:13  thepoy  阅读(224)  评论(0编辑  收藏  举报