From 864ad8a371e222e35fccba982bb5692f7107e0bf Mon Sep 17 00:00:00 2001 From: Syngnat Date: Tue, 2 Jun 2026 15:30:32 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(elasticsearch):?= =?UTF-8?q?=20=E6=94=B9=E7=94=A8=E8=BD=BB=E9=87=8F=20REST=20=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用标准库 HTTP 客户端实现 ES ping、索引、mapping 和查询请求 - 保留代理、TLS、超时和 BasicAuth 配置能力 - 移除 go-elasticsearch SDK 及间接依赖,降低 dev 构建下载风险 - 更新 Elasticsearch 后端单测适配 REST 客户端 --- go.mod | 9 +- go.sum | 19 +--- internal/db/elasticsearch_helpers.go | 129 +++++++++++++++++-------- internal/db/elasticsearch_impl.go | 50 +++++----- internal/db/elasticsearch_impl_test.go | 25 ++--- 5 files changed, 128 insertions(+), 104 deletions(-) diff --git a/go.mod b/go.mod index ea49a54..49044f2 100644 --- a/go.mod +++ b/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 ( diff --git a/go.sum b/go.sum index b15bbc0..aa64ac7 100644 --- a/go.sum +++ b/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= diff --git a/internal/db/elasticsearch_helpers.go b/internal/db/elasticsearch_helpers.go index dbcc34e..5ace051 100644 --- a/internal/db/elasticsearch_helpers.go +++ b/internal/db/elasticsearch_helpers.go @@ -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) diff --git a/internal/db/elasticsearch_impl.go b/internal/db/elasticsearch_impl.go index ac41a7d..ece3554 100644 --- a/internal/db/elasticsearch_impl.go +++ b/internal/db/elasticsearch_impl.go @@ -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 diff --git a/internal/db/elasticsearch_impl_test.go b/internal/db/elasticsearch_impl_test.go index ea28ebf..10afc2b 100644 --- a/internal/db/elasticsearch_impl_test.go +++ b/internal/db/elasticsearch_impl_test.go @@ -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) } }) }