@@ -12,7 +12,6 @@ import (
1212 "encoding/json"
1313 "errors"
1414 "fmt"
15- "os"
1615 "strings"
1716
1817 elasticsearchRetriever "github.com/Tencent/WeKnora/internal/application/repository/retriever/elasticsearch"
@@ -29,22 +28,31 @@ type elasticsearchRepository struct {
2928 client * elasticsearch.Client
3029 index string
3130 useKeywordSuffix bool // Whether to append .keyword suffix to ID field names in queries
31+ numberOfShards int // Shard count for index creation (0 = ES default)
32+ numberOfReplicas int // Replica count for index creation (-1 = unset, use ES default)
3233}
3334
35+ // NewElasticsearchEngineRepository creates and initializes a new Elasticsearch v7 repository.
36+ // indexCfg is optional — pass nil to use env var / default values (env path).
3437func NewElasticsearchEngineRepository (client * elasticsearch.Client ,
3538 config * config.Config ,
39+ indexCfg * typesLocal.IndexConfig ,
3640) interfaces.RetrieveEngineRepository {
3741 log := logger .GetLogger (context .Background ())
3842 log .Info ("[ElasticsearchV7] Initializing Elasticsearch v7 retriever engine repository" )
3943
40- indexName := os .Getenv ("ELASTICSEARCH_INDEX" )
41- if indexName == "" {
42- log .Warn ("[ElasticsearchV7] ELASTICSEARCH_INDEX environment variable not set, using default index name" )
43- indexName = "xwrag_default"
44- }
44+ indexName := typesLocal .ResolveIndexName (indexCfg , "ELASTICSEARCH_INDEX" , "xwrag_default" )
4545
4646 log .Infof ("[ElasticsearchV7] Using index: %s" , indexName )
47- res := & elasticsearchRepository {client : client , index : indexName }
47+ res := & elasticsearchRepository {
48+ client : client ,
49+ index : indexName ,
50+ numberOfShards : indexCfg .GetNumberOfShards (0 ),
51+ numberOfReplicas : indexCfg .GetNumberOfReplicas (- 1 ),
52+ }
53+ if err := res .createIndexIfNotExists (context .Background ()); err != nil {
54+ log .Errorf ("[ElasticsearchV7] Failed to create index: %v" , err )
55+ }
4856 res .detectFieldTypes (context .Background ())
4957 return res
5058}
@@ -118,6 +126,63 @@ func (e *elasticsearchRepository) detectFieldTypes(ctx context.Context) {
118126 }
119127}
120128
129+ // createIndexIfNotExists checks if the specified index exists and creates it if not.
130+ // Uses esapi low-level client since v7 SDK does not have typed API.
131+ func (e * elasticsearchRepository ) createIndexIfNotExists (ctx context.Context ) error {
132+ log := logger .GetLogger (ctx )
133+ log .Debugf ("[ElasticsearchV7] Checking if index exists: %s" , e .index )
134+
135+ res , err := e .client .Indices .Exists ([]string {e .index }, e .client .Indices .Exists .WithContext (ctx ))
136+ if err != nil {
137+ return fmt .Errorf ("check index existence: %w" , err )
138+ }
139+ defer res .Body .Close ()
140+
141+ if ! res .IsError () {
142+ log .Debugf ("[ElasticsearchV7] Index already exists: %s" , e .index )
143+ return nil
144+ }
145+
146+ // Build settings body with optional shards/replicas
147+ var body string
148+ if e .numberOfShards > 0 || e .numberOfReplicas >= 0 {
149+ settings := make (map [string ]interface {})
150+ if e .numberOfShards > 0 {
151+ settings ["number_of_shards" ] = e .numberOfShards
152+ }
153+ if e .numberOfReplicas >= 0 {
154+ settings ["number_of_replicas" ] = e .numberOfReplicas
155+ }
156+ bodyBytes , err := json .Marshal (map [string ]interface {}{"settings" : settings })
157+ if err != nil {
158+ return fmt .Errorf ("marshal index settings: %w" , err )
159+ }
160+ body = string (bodyBytes )
161+ }
162+
163+ log .Infof ("[ElasticsearchV7] Creating index: %s" , e .index )
164+ var opts []func (* esapi.IndicesCreateRequest )
165+ opts = append (opts , e .client .Indices .Create .WithContext (ctx ))
166+ if body != "" {
167+ opts = append (opts , e .client .Indices .Create .WithBody (strings .NewReader (body )))
168+ }
169+
170+ createRes , err := e .client .Indices .Create (e .index , opts ... )
171+ if err != nil {
172+ return fmt .Errorf ("create index: %w" , err )
173+ }
174+ defer createRes .Body .Close ()
175+
176+ if createRes .IsError () {
177+ // Log detailed response server-side; return generic message to avoid leaking cluster info
178+ log .Errorf ("[ElasticsearchV7] Create index response: %s" , createRes .String ())
179+ return fmt .Errorf ("failed to create index %s" , e .index )
180+ }
181+
182+ log .Infof ("[ElasticsearchV7] Index created successfully: %s" , e .index )
183+ return nil
184+ }
185+
121186func (e * elasticsearchRepository ) EngineType () typesLocal.RetrieverEngineType {
122187 return typesLocal .ElasticsearchRetrieverEngineType
123188}
0 commit comments