mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-12 09:29:43 +08:00
♻️ refactor(elasticsearch): 改用轻量 REST 客户端
- 使用标准库 HTTP 客户端实现 ES ping、索引、mapping 和查询请求 - 保留代理、TLS、超时和 BasicAuth 配置能力 - 移除 go-elasticsearch SDK 及间接依赖,降低 dev 构建下载风险 - 更新 Elasticsearch 后端单测适配 REST 客户端
This commit is contained in:
9
go.mod
9
go.mod
@@ -8,7 +8,6 @@ require (
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.43.0
|
||||
github.com/caretdev/go-irisnative v0.2.1
|
||||
github.com/duckdb/duckdb-go/v2 v2.5.5
|
||||
github.com/elastic/go-elasticsearch/v8 v8.19.6
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/highgo/pq-sm3 v0.0.0
|
||||
@@ -30,11 +29,9 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/elastic/elastic-transport-go/v8 v8.9.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.39.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
19
go.sum
19
go.sum
@@ -38,6 +38,7 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
|
||||
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -64,19 +65,10 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM=
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
|
||||
github.com/elastic/elastic-transport-go/v8 v8.9.0 h1:KeT/2P54F0xS0S8Y3Pf+tFDg4HmBgReQMB+BMz8dDAs=
|
||||
github.com/elastic/elastic-transport-go/v8 v8.9.0/go.mod h1:ssMTvNS2hwf7CaiGsRRsx4gQHFZ/jS/DkLcISxekWzc=
|
||||
github.com/elastic/go-elasticsearch/v8 v8.19.6 h1:4qa7ecJkr5rLsoHKIVGbaqcFt2o57CnOHQJi9Pts/rk=
|
||||
github.com/elastic/go-elasticsearch/v8 v8.19.6/go.mod h1:jeWebApE1oFEW/hKZqx/IRYmP/aa2+WMJkOfk+AduSI=
|
||||
github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
|
||||
github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
|
||||
github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
|
||||
github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
@@ -135,6 +127,7 @@ github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxh
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
@@ -193,6 +186,7 @@ github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0
|
||||
github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -210,6 +204,7 @@ github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTK
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
||||
@@ -281,14 +276,8 @@ go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5
|
||||
go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
||||
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
||||
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
|
||||
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
||||
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
||||
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
|
||||
@@ -15,9 +15,6 @@ import (
|
||||
|
||||
"GoNavi-Wails/internal/connection"
|
||||
proxytunnel "GoNavi-Wails/internal/proxy"
|
||||
|
||||
"github.com/elastic/go-elasticsearch/v8"
|
||||
"github.com/elastic/go-elasticsearch/v8/esapi"
|
||||
)
|
||||
|
||||
const defaultEsPort = 9200
|
||||
@@ -116,17 +113,31 @@ func esSSLAttemptLabel(config connection.ConnectionConfig, fallback bool) string
|
||||
return "明文"
|
||||
}
|
||||
|
||||
type esHTTPClientConfig struct {
|
||||
BaseURL string
|
||||
Username string
|
||||
Password string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
type esRESTClient struct {
|
||||
baseURL string
|
||||
username string
|
||||
password string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// buildESClientConfig 从连接配置构建 ES 客户端配置。
|
||||
func buildESClientConfig(config connection.ConnectionConfig) elasticsearch.Config {
|
||||
func buildESClientConfig(config connection.ConnectionConfig) esHTTPClientConfig {
|
||||
scheme := "http"
|
||||
if config.UseSSL {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
cfg := elasticsearch.Config{
|
||||
Addresses: []string{
|
||||
fmt.Sprintf("%s://%s:%d", scheme, config.Host, config.Port),
|
||||
},
|
||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
|
||||
cfg := esHTTPClientConfig{
|
||||
BaseURL: fmt.Sprintf("%s://%s:%d", scheme, config.Host, config.Port),
|
||||
Username: strings.TrimSpace(config.User),
|
||||
Password: config.Password,
|
||||
}
|
||||
@@ -134,36 +145,85 @@ func buildESClientConfig(config connection.ConnectionConfig) elasticsearch.Confi
|
||||
// TLS 配置
|
||||
tlsConfig, _ := resolveGenericTLSConfig(config)
|
||||
if tlsConfig != nil {
|
||||
cfg.Transport = &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
transport.TLSClientConfig = tlsConfig
|
||||
}
|
||||
|
||||
// 代理支持
|
||||
if config.UseProxy {
|
||||
transport, ok := cfg.Transport.(*http.Transport)
|
||||
if !ok {
|
||||
transport = http.DefaultTransport.(*http.Transport).Clone()
|
||||
}
|
||||
proxyCfg := config.Proxy
|
||||
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return proxytunnel.DialContext(ctx, proxyCfg, network, addr)
|
||||
}
|
||||
cfg.Transport = transport
|
||||
}
|
||||
|
||||
// 超时设置
|
||||
timeout := getConnectTimeout(config)
|
||||
if cfg.Transport == nil {
|
||||
cfg.Transport = http.DefaultTransport.(*http.Transport).Clone()
|
||||
}
|
||||
if transport, ok := cfg.Transport.(*http.Transport); ok {
|
||||
transport.ResponseHeaderTimeout = timeout
|
||||
}
|
||||
transport.ResponseHeaderTimeout = timeout
|
||||
cfg.HTTPClient = &http.Client{Transport: transport}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func newESRESTClient(config esHTTPClientConfig) (*esRESTClient, error) {
|
||||
baseURL := strings.TrimRight(strings.TrimSpace(config.BaseURL), "/")
|
||||
if baseURL == "" {
|
||||
return nil, fmt.Errorf("Elasticsearch 地址不能为空")
|
||||
}
|
||||
if _, err := url.ParseRequestURI(baseURL); err != nil {
|
||||
return nil, fmt.Errorf("Elasticsearch 地址无效:%w", err)
|
||||
}
|
||||
httpClient := config.HTTPClient
|
||||
if httpClient == nil {
|
||||
httpClient = http.DefaultClient
|
||||
}
|
||||
return &esRESTClient{
|
||||
baseURL: baseURL,
|
||||
username: strings.TrimSpace(config.Username),
|
||||
password: config.Password,
|
||||
httpClient: httpClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *esRESTClient) do(ctx context.Context, method string, path string, query url.Values, body io.Reader) (*http.Response, error) {
|
||||
if c == nil || c.httpClient == nil {
|
||||
return nil, fmt.Errorf("连接未打开")
|
||||
}
|
||||
requestURL := c.baseURL + path
|
||||
if len(query) > 0 {
|
||||
requestURL += "?" + query.Encode()
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, method, requestURL, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
if body != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
if c.username != "" || c.password != "" {
|
||||
req.SetBasicAuth(c.username, c.password)
|
||||
}
|
||||
return c.httpClient.Do(req)
|
||||
}
|
||||
|
||||
func esPathSegment(value string) string {
|
||||
if value == "*" {
|
||||
return "*"
|
||||
}
|
||||
return url.PathEscape(value)
|
||||
}
|
||||
|
||||
func esResponseIsError(res *http.Response) bool {
|
||||
return res == nil || res.StatusCode >= http.StatusBadRequest
|
||||
}
|
||||
|
||||
func esResponseStatus(res *http.Response) string {
|
||||
if res == nil {
|
||||
return ""
|
||||
}
|
||||
return res.Status
|
||||
}
|
||||
|
||||
// ---- 查询响应解析 ----
|
||||
|
||||
// esIndexInfo 用于解析 Cat Indices JSON 响应。
|
||||
@@ -196,11 +256,7 @@ func (e *ElasticsearchDB) esQueryWithDSL(ctx context.Context, dsl string) ([]map
|
||||
indexName = "*"
|
||||
}
|
||||
|
||||
res, err := e.client.Search(
|
||||
e.client.Search.WithContext(ctx),
|
||||
e.client.Search.WithIndex(indexName),
|
||||
e.client.Search.WithBody(strings.NewReader(dsl)),
|
||||
)
|
||||
res, err := e.client.do(ctx, http.MethodPost, "/"+esPathSegment(indexName)+"/_search", nil, strings.NewReader(dsl))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Elasticsearch DSL 查询失败:%w", err)
|
||||
}
|
||||
@@ -218,11 +274,7 @@ func (e *ElasticsearchDB) esQueryWithString(ctx context.Context, queryStr string
|
||||
|
||||
dsl := fmt.Sprintf(`{"query":{"query_string":{"query":"%s"}}}`, strings.ReplaceAll(queryStr, `"`, `\"`))
|
||||
|
||||
res, err := e.client.Search(
|
||||
e.client.Search.WithContext(ctx),
|
||||
e.client.Search.WithIndex(indexName),
|
||||
e.client.Search.WithBody(strings.NewReader(dsl)),
|
||||
)
|
||||
res, err := e.client.do(ctx, http.MethodPost, "/"+esPathSegment(indexName)+"/_search", nil, strings.NewReader(dsl))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Elasticsearch 查询失败:%w", err)
|
||||
}
|
||||
@@ -232,8 +284,8 @@ func (e *ElasticsearchDB) esQueryWithString(ctx context.Context, queryStr string
|
||||
}
|
||||
|
||||
// parseSearchResponse 解析 ES _search 响应为标准行格式。
|
||||
func (e *ElasticsearchDB) parseSearchResponse(res *esapi.Response) ([]map[string]interface{}, []string, error) {
|
||||
if res.IsError() {
|
||||
func (e *ElasticsearchDB) parseSearchResponse(res *http.Response) ([]map[string]interface{}, []string, error) {
|
||||
if esResponseIsError(res) {
|
||||
body, _ := io.ReadAll(res.Body)
|
||||
return nil, nil, fmt.Errorf("Elasticsearch 查询错误:%s", string(body))
|
||||
}
|
||||
@@ -277,17 +329,14 @@ func (e *ElasticsearchDB) esFetchIndexMapping(indexName string) (map[string]inte
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := e.client.Indices.GetMapping(
|
||||
e.client.Indices.GetMapping.WithContext(ctx),
|
||||
e.client.Indices.GetMapping.WithIndex(indexName),
|
||||
)
|
||||
res, err := e.client.do(ctx, http.MethodGet, "/"+esPathSegment(indexName)+"/_mapping", nil, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取索引 mapping 失败:%w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.IsError() {
|
||||
return nil, fmt.Errorf("获取索引 mapping 失败:%s", res.Status())
|
||||
if esResponseIsError(res) {
|
||||
return nil, fmt.Errorf("获取索引 mapping 失败:%s", esResponseStatus(res))
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -15,8 +17,6 @@ import (
|
||||
"GoNavi-Wails/internal/connection"
|
||||
"GoNavi-Wails/internal/logger"
|
||||
"GoNavi-Wails/internal/ssh"
|
||||
|
||||
"github.com/elastic/go-elasticsearch/v8"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,7 +26,7 @@ const (
|
||||
|
||||
// ElasticsearchDB 实现 Database 接口,提供 Elasticsearch 数据源连接能力。
|
||||
type ElasticsearchDB struct {
|
||||
client *elasticsearch.Client
|
||||
client *esRESTClient
|
||||
database string // 默认索引名
|
||||
pingTimeout time.Duration
|
||||
forwarder *ssh.LocalForwarder
|
||||
@@ -85,7 +85,7 @@ func (e *ElasticsearchDB) Connect(config connection.ConnectionConfig) error {
|
||||
idx+1, len(attempts), sslLabel, attempt.Host, attempt.Port)
|
||||
|
||||
esCfg := buildESClientConfig(attempt)
|
||||
client, err := elasticsearch.NewClient(esCfg)
|
||||
client, err := newESRESTClient(esCfg)
|
||||
if err != nil {
|
||||
logger.Warnf("Elasticsearch 创建客户端失败:%d/%d 模式=%s 错误=%v", idx+1, len(attempts), sslLabel, err)
|
||||
lastErr = err
|
||||
@@ -137,14 +137,14 @@ func (e *ElasticsearchDB) Ping() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
res, err := e.client.Ping(e.client.Ping.WithContext(ctx))
|
||||
res, err := e.client.do(ctx, http.MethodHead, "/", nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.IsError() {
|
||||
return fmt.Errorf("Elasticsearch Ping 失败:%s", res.Status())
|
||||
if esResponseIsError(res) {
|
||||
return fmt.Errorf("Elasticsearch Ping 失败:%s", esResponseStatus(res))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -183,18 +183,17 @@ func (e *ElasticsearchDB) GetDatabases() ([]string, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := e.client.Cat.Indices(
|
||||
e.client.Cat.Indices.WithContext(ctx),
|
||||
e.client.Cat.Indices.WithFormat("json"),
|
||||
e.client.Cat.Indices.WithH("index"),
|
||||
)
|
||||
query := url.Values{}
|
||||
query.Set("format", "json")
|
||||
query.Set("h", "index")
|
||||
res, err := e.client.do(ctx, http.MethodGet, "/_cat/indices", query, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取索引列表失败:%w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.IsError() {
|
||||
return nil, fmt.Errorf("获取索引列表失败:%s", res.Status())
|
||||
if esResponseIsError(res) {
|
||||
return nil, fmt.Errorf("获取索引列表失败:%s", esResponseStatus(res))
|
||||
}
|
||||
|
||||
var indices []struct {
|
||||
@@ -240,17 +239,14 @@ func (e *ElasticsearchDB) GetCreateStatement(dbName, tableName string) (string,
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := e.client.Indices.Get(
|
||||
[]string{indexName},
|
||||
e.client.Indices.Get.WithContext(ctx),
|
||||
)
|
||||
res, err := e.client.do(ctx, http.MethodGet, "/"+esPathSegment(indexName), nil, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("获取索引定义失败:%w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.IsError() {
|
||||
return "", fmt.Errorf("获取索引定义失败:%s", res.Status())
|
||||
if esResponseIsError(res) {
|
||||
return "", fmt.Errorf("获取索引定义失败:%s", esResponseStatus(res))
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
@@ -322,19 +318,17 @@ func (e *ElasticsearchDB) GetIndexes(dbName, tableName string) ([]connection.Ind
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := e.client.Cat.Indices(
|
||||
e.client.Cat.Indices.WithContext(ctx),
|
||||
e.client.Cat.Indices.WithIndex(indexName),
|
||||
e.client.Cat.Indices.WithFormat("json"),
|
||||
e.client.Cat.Indices.WithH("index", "health", "status", "docs.count", "store.size"),
|
||||
)
|
||||
query := url.Values{}
|
||||
query.Set("format", "json")
|
||||
query.Set("h", "index,health,status,docs.count,store.size")
|
||||
res, err := e.client.do(ctx, http.MethodGet, "/_cat/indices/"+esPathSegment(indexName), query, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取索引信息失败:%w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.IsError() {
|
||||
return nil, fmt.Errorf("获取索引信息失败:%s", res.Status())
|
||||
if esResponseIsError(res) {
|
||||
return nil, fmt.Errorf("获取索引信息失败:%s", esResponseStatus(res))
|
||||
}
|
||||
|
||||
var info []esIndexInfo
|
||||
|
||||
@@ -11,8 +11,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"GoNavi-Wails/internal/connection"
|
||||
|
||||
"github.com/elastic/go-elasticsearch/v8"
|
||||
)
|
||||
|
||||
// ---- 测试辅助函数 ----
|
||||
@@ -29,13 +27,10 @@ func newMockESServer(t *testing.T, handler http.HandlerFunc) *httptest.Server {
|
||||
return server
|
||||
}
|
||||
|
||||
// newTestESClient 创建连接到测试服务器的 ES 客户端。
|
||||
func newTestESClient(t *testing.T, serverURL string) *elasticsearch.Client {
|
||||
// newTestESClient 创建连接到测试服务器的 ES REST 客户端。
|
||||
func newTestESClient(t *testing.T, serverURL string) *esRESTClient {
|
||||
t.Helper()
|
||||
cfg := elasticsearch.Config{
|
||||
Addresses: []string{serverURL},
|
||||
}
|
||||
client, err := elasticsearch.NewClient(cfg)
|
||||
client, err := newESRESTClient(esHTTPClientConfig{BaseURL: serverURL})
|
||||
if err != nil {
|
||||
t.Fatalf("创建测试 ES 客户端失败: %v", err)
|
||||
}
|
||||
@@ -292,7 +287,7 @@ func TestElasticsearchQueryDSL(t *testing.T) {
|
||||
t.Run("指定索引的 DSL 查询", func(t *testing.T) {
|
||||
server := newMockESServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodPost && strings.HasSuffix(r.URL.Path, "/_search") {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{
|
||||
"hits": {
|
||||
"total": {"value": 1},
|
||||
@@ -339,7 +334,7 @@ func TestElasticsearchQueryDSL(t *testing.T) {
|
||||
server := newMockESServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodPost && strings.HasSuffix(r.URL.Path, "/_search") {
|
||||
capturedPath = r.URL.Path
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"hits":{"total":{"value":0},"hits":[]}}`))
|
||||
return
|
||||
}
|
||||
@@ -379,7 +374,7 @@ func TestElasticsearchQueryString(t *testing.T) {
|
||||
buf := make([]byte, r.ContentLength)
|
||||
_, _ = r.Body.Read(buf)
|
||||
capturedBody = string(buf)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{
|
||||
"hits": {
|
||||
"total": {"value": 2},
|
||||
@@ -895,8 +890,8 @@ func TestBuildESClientConfig(t *testing.T) {
|
||||
Port: 9200,
|
||||
User: "elastic",
|
||||
})
|
||||
if len(cfg.Addresses) != 1 || cfg.Addresses[0] != "http://localhost:9200" {
|
||||
t.Fatalf("HTTP 地址期望 http://localhost:9200,实际:%v", cfg.Addresses)
|
||||
if cfg.BaseURL != "http://localhost:9200" {
|
||||
t.Fatalf("HTTP 地址期望 http://localhost:9200,实际:%v", cfg.BaseURL)
|
||||
}
|
||||
if cfg.Username != "elastic" {
|
||||
t.Fatalf("用户名期望 elastic,实际:%q", cfg.Username)
|
||||
@@ -909,8 +904,8 @@ func TestBuildESClientConfig(t *testing.T) {
|
||||
Port: 9200,
|
||||
UseSSL: true,
|
||||
})
|
||||
if len(cfg.Addresses) != 1 || cfg.Addresses[0] != "https://es.example.com:9200" {
|
||||
t.Fatalf("HTTPS 地址期望 https://es.example.com:9200,实际:%v", cfg.Addresses)
|
||||
if cfg.BaseURL != "https://es.example.com:9200" {
|
||||
t.Fatalf("HTTPS 地址期望 https://es.example.com:9200,实际:%v", cfg.BaseURL)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user