笔者在学习Go语言查询Elasticsearch的时候, 遇到了下面这句代码:
res, err = es.Search(
es.Search.WithContext(context.Background()),
es.Search.WithIndex("test"),
es.Search.WithBody(strings.NewReader(query)),
es.Search.WithTrackTotalHits(true),
es.Search.WithPretty(),
)
很好奇es.Search究竟是一个什么东西, 但是Go语言的文档里面只写了:
type Search
type Search func(o ...func(*SearchRequest)) (*Response, error)
Search returns results matching a query.
类型上面定义WithXXX方法没问题,但是Search本身是一个类型,类型怎么可以当作函数来调用呢?这究竟是怎样一种语法结构呢?
经过分析elasticsearch库的源代码,笔者发现原来这其中的奥秘就隐藏在newSearchFunc函数里面。
简单来说, es.Search是一个变量, 变量的值为一个函数, 所以我们可以调用es.Search()。函数体是定义在newSearchFunc函数里面的, 通过返回值返回后,赋给了es.Search变量。
而es.Search这个变量所属的类型上定义了很多With开头的方法, 这些方法可通过es.Search变量来访问, 所以我们可以调用es.Search.WithXXX()。
es是main函数里面的变量名, 不是包名。
es变量的类型是elasticsearch.Client, 也就是elasticsearch包里面定义的Client类型。
elasticsearch.Client类型是一个结构体, 首先他继承了esapi.API结构体(esapi包里面定义的API结构体), 然后增加了类型为estransport.Interface的Transport变量。
esapi.API结构体里面就有一个Search变量,变量的类型为esapi.Search,是一个函数类型,函数的参数列表是可变的。
因此, es.Search实际上是elasticsearch.Client结构体继承的esapi.API结构体里面的一个esapi.Search类型的变量。
在esapi.Search这个类型上又定义了很多With开头的方法,这些方法需要通过esapi.Search变量来访问,不能直接通过类型名访问。
也就是说我们可以执行es.Search.WithIndex("index")但是不能执行esapi.Search.WithIndex("index")。
因为es.Search是一个变量, 而esapi.Search是一个类型。
es.Search变量的类型是esapi.Search, WithXXX方法是在esapi.Search这个类型上定义的。
结构简化代码:
package main
import (
"fmt"
"strconv"
)
type Search func(o ...int) string
type API struct {
Id int
Value string
Search Search
}
type Client struct {
*API
Transport string
}
func NewDefaultClient() *Client {
return &Client{Transport: "Transport", API: New()}
}
func New() *API {
return &API{Id: 10, Value: "hello", Search: newSearchFunc()}
}
func newSearchFunc() Search {
return func(o ...int) string {
s := ""
for i, n := range o {
if i != 0 {
s += ","
}
s += strconv.Itoa(n)
}
return s
}
}
func (s *Search) WithA() int {
return -10
}
func (s *Search) WithB() int {
return -20
}
func (s *Search) WithC(offset string) int {
n, _ := strconv.Atoi(offset)
return -30 + n
}
func main() {
es := NewDefaultClient()
fmt.Println(es.Id, es.Value, es.Search, es.Transport)
str := es.Search(
es.Search.WithA(),
es.Search.WithB(),
es.Search.WithC("-3"),
)
fmt.Println(str)
}
程序运行结果:
10 hello 0x49c300 Transport
-10,-20,-33