Elasticsearch 的 easyjson 示例
mailru/easyjson
库的特点就是我们只需要提供结构体,然后用这些结构体生成编码解码的代码。
示例的项目名为elasticsearch/encoding/json
。
一、创建 models
在项目中新建 model 目录,目录中新建两个文件model.go
和response.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.go
和response_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
────────────────────────────────────────────────────────────────────────────────