@@ -2,9 +2,11 @@ package om
22
33import (
44 "context"
5+ "fmt"
56 "github.com/oklog/ulid/v2"
67 "reflect"
78 "strconv"
9+ "strings"
810 "time"
911
1012 "github.com/redis/rueidis"
@@ -146,6 +148,78 @@ func (r *HashRepository[T]) CreateIndex(ctx context.Context, cmdFn func(schema F
146148 return r .client .Do (ctx , cmdFn (r .client .B ().FtCreate ().Index (r .idx ).OnHash ().Prefix (1 ).Prefix (r .prefix + ":" ).Schema ())).Error ()
147149}
148150
151+ // CreateAndAliasIndex creates a new index, aliases it, and drops the old index if needed.
152+ func (r * HashRepository [T ]) CreateAndAliasIndex (ctx context.Context , cmdFn func (schema FtCreateSchema ) rueidis.Completed ) error {
153+ alias := r .idx
154+
155+ var currentIndex string
156+ aliasExists := false
157+ infoCmd := r .client .B ().FtInfo ().Index (alias ).Build ()
158+ infoResp , err := r .client .Do (ctx , infoCmd ).ToMap ()
159+ if err != nil {
160+ if strings .Contains (err .Error (), "Unknown index name" ) {
161+ // This is expected when the alias doesn't exist yet
162+ aliasExists = false
163+ } else {
164+ // This is an unexpected error (network, connection, etc.)
165+ return fmt .Errorf ("failed to check if index exists: %w" , err )
166+ }
167+ } else {
168+ aliasExists = true
169+ }
170+
171+ if aliasExists {
172+ message , ok := infoResp ["index_name" ]
173+ if ! ok {
174+ return fmt .Errorf ("index_name not found in FT.INFO response" )
175+ }
176+
177+ currentIndex , err = message .ToString ()
178+ if err != nil {
179+ return fmt .Errorf ("failed to convert index_name to string: %w" , err )
180+ }
181+ }
182+
183+ newIndex := alias + "_v1"
184+ if aliasExists && currentIndex != "" {
185+ // Find the last occurrence of "_v" followed by digits
186+ lastVersionIndex := strings .LastIndex (currentIndex , "_v" )
187+ if lastVersionIndex != - 1 && lastVersionIndex + 2 < len (currentIndex ) {
188+ versionStr := currentIndex [lastVersionIndex + 2 :]
189+ if version , err := strconv .Atoi (versionStr ); err == nil {
190+ newIndex = fmt .Sprintf ("%s_v%d" , alias , version + 1 )
191+ }
192+ }
193+ }
194+
195+ // Create the new index
196+ cmd := r .client .B ().FtCreate ().Index (newIndex ).OnHash ().Prefix (1 ).Prefix (r .prefix + ":" )
197+ if err := r .client .Do (ctx , cmdFn (cmd .Schema ())).Error (); err != nil {
198+ return err
199+ }
200+
201+ // Update or add the alias
202+ var aliasErr error
203+ if aliasExists {
204+ aliasErr = r .client .Do (ctx , r .client .B ().FtAliasupdate ().Alias (alias ).Index (newIndex ).Build ()).Error ()
205+ } else {
206+ aliasErr = r .client .Do (ctx , r .client .B ().FtAliasadd ().Alias (alias ).Index (newIndex ).Build ()).Error ()
207+ }
208+
209+ if aliasErr != nil {
210+ return fmt .Errorf ("failed to update alias: %w" , aliasErr )
211+ }
212+
213+ // Drop the old index if it exists and differs from the new one
214+ if aliasExists && currentIndex != "" && currentIndex != newIndex {
215+ if err := r .client .Do (ctx , r .client .B ().FtDropindex ().Index (currentIndex ).Build ()).Error (); err != nil {
216+ return fmt .Errorf ("failed to drop old index: %w" , err )
217+ }
218+ }
219+
220+ return nil
221+ }
222+
149223// DropIndex uses FT.DROPINDEX from the RediSearch module to drop the index whose name is `hashidx:{prefix}`
150224func (r * HashRepository [T ]) DropIndex (ctx context.Context ) error {
151225 return r .client .Do (ctx , r .client .B ().FtDropindex ().Index (r .idx ).Build ()).Error ()
0 commit comments