-
Notifications
You must be signed in to change notification settings - Fork 155
Hooks (简体中文)
一般的Hook
会通过被操作文档实现hook
回调方法,以实现Hook
功能。但是Qmgo
的很多操作入参没有文档,同时我们认为这是一种很简洁的操作方式.
所以Qmgo
的Hook v1
和一般的Hook
实现会有不同:
-
用户自己实现Hook的结构体方法,通过具体某个操作的
options
传递给Qmgo
,Qmgo
自动回调 -
这几个API
InsertOne
、InsertMany
、Upsert
andReplaceOne
的回调可以更简单,不依赖于options -
如果
Hook
操作失败,暂时没有做回滚数据库操作的功能 -
某操作前和操作后的hook生效是互相独立的,即只需要实现
BeforeInsert() error
就能实现Insert前的hook
实现Insert Hook
,用户需要实现结构体方法:
BeforeInsert(ctx context.Context) error // 实现本方法让insert前的hook生效
AfterInsert(ctx context.Context) error // 实现本方法让insert后的hook生效
在InsertOne
流程中
-
通过自定义结构(下例的
User
)实现Insert
的Hook
的方法 -
通过
options.InsertOneOptions
传入来实现Hook
回调 -
如果第二个参数
doc
实现了Insert
的Hook
的方法,那么可以不传入options.InsertOneOptions
-
如果使用文档
User
来直接实现Hook
的方法,在BeforeInsert()
中修改文档,修改后的文档会被插入数据库
type User struct {
Name string `bson:"name"`
Age int `bson:"age"`
}
func (u *User) BeforeInsert(ctx context.Context) error {
fmt.Println("before insert called")
return nil
}
func (u *User) AfterInsert(ctx context.Context) error {
fmt.Println("before insert called")
return nil
}
u := &User{Name: "Alice", Age: 7}
_, err := cli.InsertOne(context.Background(), u)
// 下面的例子也可以工作
// _, err := cli.InsertOne(context.Background(), u, options.InsertOneOptions{
// InsertHook: u,
// })
在InsertMany
流程中
-
通过自定义结构(下例的
User
)实现Insert
的Hook
方法, -
通过
options.InsertManyOptions
传入来实现Hook
回调。 -
如果第二个参数
docs
实现了Insert
的Hook
的方法,那么可以不传入options.InsertManyOptions
-
如果使用文档
User
来直接实现Hook
方法,在BeforeInsert()
中修改文档,修改后的文档会被插入数据库 -
因为插入的是多个文档,而
hook
是针对每个文档都会发生,所以Hook
会根据插入文档数量被回调多次
type User struct {
Name string `bson:"name"`
Age int `bson:"age"`
}
func (u *User) BeforeInsert(ctx context.Context) error {
fmt.Println("before insert called")
return nil
}
func (u *User) AfterInsert(ctx context.Context) error {
fmt.Println("before insert called")
return nil
}
u1 := &User{Name: "Lucas", Age: 7}
u2 := &User{Name: "Alice", Age: 7}
us := []*User{u1, u2}
_, err := cli.InsertMany(ctx, us, options.InsertManyOptions{
InsertHook: us,
})
// 下面的代码也能够工作
// _, err := cli.InsertMany(ctx, us, options.InsertManyOptions{
// InsertHook: us,
// })
实现Update
操作的Hook
,用户需要实现结构体方法:
BeforeUpdate(ctx context.Context) error // 实现本方法让update前的hook生效
AfterUpdate(ctx context.Context) error // 实现本方法让update后的hook生效
-
通过自定义结构(下例中的
MyUpdateHook
)实现Update
的Hook
方法 -
通过
options.UpdateOptions
传入来实现Hook
回调。 -
如果使用文档
User
来直接实现Hook
方法,在方法中对文档进行操作,不会 对写入数据库中的文档产生影响。
type MyUpdateHook struct {
beforeUpdateCount int
afterUpdateCount int
}
func (u *MyUpdateHook) BeforeUpdate(ctx context.Context) error {
u.beforeUpdateCount++
return nil
}
func (u *MyUpdateHook) AfterUpdate(ctx context.Context) error {
u.afterUpdateCount++
return nil
}
u := User{Name: "Lucas", Age: 7}
uh := &MyUpdateHook{}
_, err := cli.InsertOne(context.Background(), u)
ast.NoError(err)
err = cli.UpdateOne(ctx, bson.M{"name": "Lucas"}, bson.M{operator.Set: bson.M{"age": 27}}, options.UpdateOptions{
UpdateHook: uh,
})
cli.UpdateAll(ctx, bson.M{"name": "Lucas"}, bson.M{operator.Set: bson.M{"age": 27}}, options.UpdateOptions{
UpdateHook: uh,
})
-
通过自定义结构(下例中的
User
)实现Update
的Hook
方法 -
通过
options.UpdateOptions
传入来实现Hook
回调。 -
如果第3个参数doc实现了
Update
的Hook
方法,则options.UpdateOptions
可以不传入 -
如果使用文档
User
来直接实现Hook
方法,在方法中对文档进行操作,会将改写的文档更新到数据库。
type User struct {
Name string `bson:"name"`
Age int `bson:"age"`
beforeUpdate int
afterUpdate int
}
func (u *User) BeforeUpdate(ctx context.Context) error {
u.beforeUpdate++
return nil
}
func (u *User) AfterUpdate(ctx context.Context) error {
u.afterUpdate++
return nil
}
u := &User{}
err = cli.ReplaceOne(ctx, bson.M{"name": "Lucas"}, &u)
// 下面的代码也可以工作
// err = cli.ReplaceOne(ctx, bson.M{"name": "Lucas"}, &u, options.UpdateOptions{
// UpdateHook: u,
// })
实现Upsert
操作的Hook
,用户需要实现结构体方法:
BeforeUpsert(ctx context.Context) error // 实现本方法让upsert前的hook生效
AfterUpsert(ctx context.Context) error // 实现本方法让upser后的hook生效
在Upsert
流程中
-
通过自定义结构(下例的
User
)实现Upsert
的Hook
的方法 -
通过
options.UpsertOptions
传入来实现Hook
回调 -
如果第二个参数
doc
实现了Upsert
的Hook
的方法,那么可以不传入options.UpsertOptions
-
如果使用文档
User
来直接实现Hook
的方法,在BeforeUpsert()
中修改文档,修改后的文档会被插入数据库
type User struct {
Name string `bson:"name"`
Age int `bson:"age"`
}
func (u *User) BeforeUpsert(ctx context.Context) error {
return nil
}
func (u *User) AfterUpsert(ctx context.Context) error {
return nil
}
u := &User{Name: "Alice", Age: 7}
_, err = cli.Upsert(context.Background(), bson.M{"name": "Lucas"}, u)
// 下面的代码也可以运行
// _, err = cli.Upsert(context.Background(), bson.M{"name": "Lucas"}, u, options.UpsertOptions{
// UpsertHook: myHook,
//})
实现Remove
操作的Hook
,用户需要实现结构体方法:
BeforeRemove(ctx context.Context) error // 实现本方法让remove前的hook生效
AfterRemove(ctx context.Context) error // 实现本方法让remove后的hook生效
- 自定义结构(下例中的
MyRemoveHook
)实现Remove
的Hook
方法 - 通过
options.RemoveOptions
传入来实现Hook
回调。
type MyRemoveHook struct {
beforeCount int
afterCount int
}
func (m *MyRemoveHook) BeforeRemove(ctx context.Context) error {
m.beforeCount++
return nil
}
func (m *MyRemoveHook) AfterRemove(ctx context.Context) error {
m.afterCount++
return nil
}
rh := &MyRemoveHook{}
err = cli.Remove(ctx, bson.M{"age": 17}, options.RemoveOptions{
RemoveHook: rh,
})
rh = &MyRemoveHook{}
_, err = cli.RemoveAll(ctx, bson.M{"age": "7"}, options.RemoveOptions{
RemoveHook: rh,
})
实现Query
操作的Hook
,用户需要实现结构体方法:
BeforeQuery(ctx context.Context) error // 实现本方法让query前的hook生效
AfterQuery(ctx context.Context) error // 实现本方法让query后的hook生效
- 自定义结构(下例中的
MyQueryHook
)实现Query
的Hook
方法 - 通过
options.FindOptions
传入来实现Hook
回调。
type MyQueryHook struct {
beforeCount int
afterCount int
}
func (q *MyQueryHook) BeforeQuery(ctx context.Context) error {
q.beforeCount++
return nil
}
func (q *MyQueryHook) AfterQuery(ctx context.Context) error {
q.afterCount++
return nil
}
qk := &MyQueryHook{}
err = cli.Find(ctx, bson.M{"age": 17}, options.FindOptions{
QueryHook: qk,
}).One(ur)
err = cli.Find(ctx, bson.M{"age": 17}, options.FindOptions{
QueryHook: qh,
}).All(&ur)