Skip to content

Commit 77e8d6a

Browse files
authored
fix: Resolve the issue where FindOneAndUpdate does not update the timestamp (#84)
1 parent 3f473ec commit 77e8d6a

File tree

4 files changed

+113
-11
lines changed

4 files changed

+113
-11
lines changed

finder/finder.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"time"
2020

21+
"github.com/chenmingyong0423/go-mongox/v2/bsonx"
2122
"github.com/chenmingyong0423/go-mongox/v2/field"
2223

2324
"github.com/chenmingyong0423/go-mongox/v2/callback"
@@ -34,6 +35,8 @@ type IFinder[T any] interface {
3435
FindOne(ctx context.Context, opts ...options.Lister[options.FindOneOptions]) (*T, error)
3536
Find(ctx context.Context, opts ...options.Lister[options.FindOptions]) ([]*T, error)
3637
Count(ctx context.Context, opts ...options.Lister[options.CountOptions]) (int64, error)
38+
FindOneAndUpdate(ctx context.Context, opts ...options.Lister[options.
39+
FindOneAndUpdateOptions]) (*T, error)
3740
}
3841

3942
func NewFinder[T any](collection *mongo.Collection, callbacks *callback.Callback, fields []*field.Filed) *Finder[T] {
@@ -235,6 +238,12 @@ func (f *Finder[T]) DistinctWithParse(ctx context.Context, fieldName string, res
235238
func (f *Finder[T]) FindOneAndUpdate(ctx context.Context, opts ...options.Lister[options.FindOneAndUpdateOptions]) (*T, error) {
236239
currentTime := time.Now()
237240
t := new(T)
241+
242+
updates := bsonx.ToBsonM(f.updates)
243+
if len(updates) != 0 {
244+
f.updates = updates
245+
}
246+
238247
globalOpContext := operation.NewOpContext(f.collection, operation.WithFilter(f.filter), operation.WithUpdates(f.updates), operation.WithMongoOptions(opts), operation.WithModelHook(f.modelHook), operation.WithStartTime(currentTime), operation.WithFields(f.fields))
239248
opContext := NewOpContext(f.collection, f.filter, WithUpdates[T](f.updates), WithMongoOptions[T](opts), WithModelHook[T](f.modelHook), WithStartTime[T](currentTime), WithFields[T](f.fields))
240249

finder/finder_e2e_test.go

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,9 +1227,10 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
12271227
beforeHook []beforeHookFn[TestUser]
12281228
afterHook []afterHookFn[TestUser]
12291229

1230-
ctx context.Context
1231-
want *TestUser
1232-
wantErr error
1230+
ctx context.Context
1231+
want *TestUser
1232+
wantErr error
1233+
resultIsUpdated bool
12331234
}{
12341235
{
12351236
name: "nil document",
@@ -1248,8 +1249,9 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
12481249

12491250
finder.filter = bson.D{}
12501251
},
1251-
filter: query.Eq("name", "burt"),
1252-
wantErr: mongo.ErrNilDocument,
1252+
filter: query.Eq("name", "burt"),
1253+
wantErr: mongo.ErrNilDocument,
1254+
resultIsUpdated: false,
12531255
},
12541256
{
12551257
name: "find by name and update age",
@@ -1262,6 +1264,8 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
12621264
require.NotNil(t, insertOneResult.InsertedID)
12631265
},
12641266
after: func(ctx context.Context, t *testing.T) {
1267+
result, err := finder.Filter(query.Eq("name", "Mingyong Chen")).FindOne(ctx)
1268+
require.NotZero(t, result.UpdatedAt)
12651269
deleteOneResult, err := collection.DeleteOne(ctx, query.Eq("name", "Mingyong Chen"))
12661270
require.NoError(t, err)
12671271
require.Equal(t, int64(1), deleteOneResult.DeletedCount)
@@ -1275,6 +1279,7 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
12751279
Name: "Mingyong Chen",
12761280
Age: 24,
12771281
},
1282+
resultIsUpdated: true,
12781283
},
12791284
{
12801285
name: "global before hook error",
@@ -1290,7 +1295,8 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
12901295
},
12911296
},
12921297
},
1293-
wantErr: errors.New("global before hook error"),
1298+
wantErr: errors.New("global before hook error"),
1299+
resultIsUpdated: false,
12941300
},
12951301
{
12961302
name: "global after hook error",
@@ -1321,7 +1327,8 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
13211327
},
13221328
},
13231329
},
1324-
wantErr: errors.New("global after hook error"),
1330+
wantErr: errors.New("global after hook error"),
1331+
resultIsUpdated: false,
13251332
},
13261333
{
13271334
name: "global before and after hook",
@@ -1351,7 +1358,8 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
13511358
if opCtx.Filter.(bson.D)[0].Key != "name" || opCtx.Filter.(bson.D)[0].Value.(bson.D)[0].Value != "Mingyong Chen" {
13521359
return errors.New("filter error")
13531360
}
1354-
if opCtx.Updates.(bson.D)[0].Value.(bson.D)[0].Key != "age" || opCtx.Updates.(bson.D)[0].Value.(bson.D)[0].Value != 24 {
1361+
1362+
if opCtx.Updates.(bson.M)["$set"].(bson.M)["age"].(int32) != 24 {
13551363
return errors.New("updates error")
13561364
}
13571365
return nil
@@ -1365,6 +1373,7 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
13651373
if user.Name != "Mingyong Chen" || user.Age != 24 {
13661374
return errors.New("result error")
13671375
}
1376+
require.NotZero(t, user.UpdatedAt)
13681377
return nil
13691378
},
13701379
},
@@ -1373,6 +1382,7 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
13731382
Name: "Mingyong Chen",
13741383
Age: 24,
13751384
},
1385+
resultIsUpdated: true,
13761386
},
13771387
{
13781388
name: "before hook error",
@@ -1384,7 +1394,8 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
13841394
return errors.New("before hook error")
13851395
},
13861396
},
1387-
wantErr: errors.New("before hook error"),
1397+
wantErr: errors.New("before hook error"),
1398+
resultIsUpdated: false,
13881399
},
13891400
{
13901401
name: "after hook error",
@@ -1411,7 +1422,8 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
14111422
return errors.New("after hook error")
14121423
},
14131424
},
1414-
wantErr: errors.New("after hook error"),
1425+
wantErr: errors.New("after hook error"),
1426+
resultIsUpdated: false,
14151427
},
14161428
{
14171429
name: "before and after hook",
@@ -1438,7 +1450,7 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
14381450
if opCtx.Filter.(bson.D)[0].Key != "name" || opCtx.Filter.(bson.D)[0].Value.(bson.D)[0].Value != "Mingyong Chen" {
14391451
return errors.New("filter error")
14401452
}
1441-
if opCtx.Updates.(bson.D)[0].Value.(bson.D)[0].Key != "age" || opCtx.Updates.(bson.D)[0].Value.(bson.D)[0].Value != 24 {
1453+
if opCtx.Updates.(bson.M)["$set"].(bson.M)["age"].(int32) != 24 {
14421454
return errors.New("updates error")
14431455
}
14441456
return nil
@@ -1450,13 +1462,15 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
14501462
if user.Name != "Mingyong Chen" || user.Age != 24 {
14511463
return errors.New("after error")
14521464
}
1465+
require.NotZero(t, user.UpdatedAt)
14531466
return nil
14541467
},
14551468
},
14561469
want: &TestUser{
14571470
Name: "Mingyong Chen",
14581471
Age: 24,
14591472
},
1473+
resultIsUpdated: true,
14601474
},
14611475
}
14621476

@@ -1473,6 +1487,10 @@ func TestFinder_e2e_FindOneAndUpdate(t *testing.T) {
14731487
require.Equal(t, tc.wantErr, err)
14741488
if err == nil {
14751489
tc.want.ID = user.ID
1490+
if tc.resultIsUpdated {
1491+
require.NotZero(t, user.UpdatedAt)
1492+
tc.want.UpdatedAt = user.UpdatedAt
1493+
}
14761494
require.Equal(t, tc.want, user)
14771495
}
14781496
for _, hook := range tc.globalHook {

finder/finder_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,56 @@ func TestFinder_Count(t *testing.T) {
216216
})
217217
}
218218
}
219+
220+
func TestFindOneAndUpdate(t *testing.T) {
221+
type TestUser struct {
222+
ID bson.ObjectID `bson:"_id,omitempty"`
223+
Name string `bson:"name"`
224+
Age int64
225+
UnknownField string `bson:"-"`
226+
CreatedAt time.Time `bson:"created_at"`
227+
UpdatedAt time.Time `bson:"updated_at"`
228+
}
229+
testCases := []struct {
230+
name string
231+
mock func(ctx context.Context, ctl *gomock.Controller) IFinder[TestUser]
232+
233+
ctx context.Context
234+
want *TestUser
235+
wantErr error
236+
}{
237+
{
238+
name: "error",
239+
mock: func(ctx context.Context, ctl *gomock.Controller) IFinder[TestUser] {
240+
mockCollection := mocks.NewMockIFinder[TestUser](ctl)
241+
mockCollection.EXPECT().FindOneAndUpdate(ctx).Return(nil, mongo.ErrNoDocuments).Times(1)
242+
return mockCollection
243+
},
244+
245+
ctx: context.Background(),
246+
want: nil,
247+
wantErr: mongo.ErrNoDocuments,
248+
},
249+
{
250+
name: "match the first one and update",
251+
mock: func(ctx context.Context, ctl *gomock.Controller) IFinder[TestUser] {
252+
mockCollection := mocks.NewMockIFinder[TestUser](ctl)
253+
mockCollection.EXPECT().FindOneAndUpdate(ctx).Return(&TestUser{Name: "hejiangda", Age: 18}, nil).Times(1)
254+
return mockCollection
255+
},
256+
ctx: context.Background(),
257+
want: &TestUser{Name: "hejiangda", Age: 18},
258+
},
259+
}
260+
for _, tc := range testCases {
261+
t.Run(tc.name, func(t *testing.T) {
262+
ctl := gomock.NewController(t)
263+
defer ctl.Finish()
264+
finder := tc.mock(tc.ctx, ctl)
265+
266+
user, err := finder.FindOneAndUpdate(tc.ctx)
267+
assert.Equal(t, tc.wantErr, err)
268+
assert.Equal(t, tc.want, user)
269+
})
270+
}
271+
}

mock/finder.mock.go

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)