-
Notifications
You must be signed in to change notification settings - Fork 155
Hooks
Generally, User use Hook
will implement the callback method through the operated document. But many operations of Qmgo
don't have documents as parameter(like Update method), which we think it's a very concise way.
So the Hook v1
of Qmgo
will be different from the "general" Hook
implementation:
-
The user implements the struct method of Hook, and passes it through
options
of a specific operation API, thenQmgo
automatically do callback. -
Only
InsertOne
、InsertMany
、Upsert
andReplaceOne
can do hook withoutoptions
-
If the
Hook
operation fails, there is no rollback operation in current version. -
The hook before and after a certain operation takes effect independently, for example, you only need to implement
BeforeInsert() error
to make hook before insert work.
User need to implement struct methods to use Insert Hook
:
BeforeInsert(ctx context.Context) error // implement it for hook before insert
AfterInsert(ctx context.Context) error // implement it for hook after insert
In the InsertOne
process
-
Implement the
Hook
method ofInsert
through a custom struct (User
in the following example) -
Pass in
options.InsertOneOptions
to makeHook
work -
If the second parameter doc implement the
Hook
method,options.InsertOneOptions
can be ignore in InsertOne. -
If you use the document
User
to implement the method and modify the document inBeforeInsert()
, the modified document will be inserted into the database
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)
// following example also works
// _, err := cli.InsertOne(context.Background(), u, options.InsertOneOptions{
// InsertHook: u,
// })
In the InsertMany
process
-
Implement the
Hook
method ofInsert
through a custom structure (User
in the following example), -
Pass in
options.InsertManyOptions
to makeHook
work. -
If the second parameter
docs
implement theHook
method,options.InsertManyOptions
can be ignore inInsertMany
. -
If you use the document
User
to implement method and modify the document inBeforeInsert()
, the modified document will be inserted into the database -
Because multiple documents are inserted, and
Hook
happens for each document, soHook
will be called back multiple times according to the number of inserted documents
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)
// following example also works
// _, err := cli.InsertMany(ctx, us, options.InsertManyOptions{
// InsertHook: us,
// })
User need to implement struct methods to use Update Hook
:
BeforeUpdate(ctx context.Context) error // implement it for hook before update
AfterUpdate(ctx context.Context) error // implement it for hook after update
-
Implement the
Hook
method ofUpdate
through a custom struct (MyUpdateHook
in the following example) -
Pass in
options.UpdateOptions
to makeHook
work. -
If you use the document
User
to directly implement methods and modify the document in theBeforeUpdate()
, will not affect the document written in the database.
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,
})
-
Implement the
Hook
method ofUpdate
through a custom struct (User
in the following example) -
Pass in
options.UpdateOptions
to makeHook
work. -
If the third parameter
doc
implement theHook
method,options.UpdateOptions
can be ignore inReplaceOne
. -
If you use the document
User
to directly implement methods and modify the document in theBeforeUpdate()
, the modified document will be updated into the database.
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)
// following example also works
// err = cli.ReplaceOne(ctx, bson.M{"name": "Lucas"}, &u, options.UpdateOptions{
// UpdateHook: u,
// })
User need to implement struct methods to use Upsert Hook
:
BeforeUpsert(ctx context.Context) error // implement it for hook before upsert
AfterUpsert(ctx context.Context) error // implement it for hook before upsert
In the Upsert
API
-
Implement the
Hook
method ofUpsert
through a custom struct (User
in the following example) -
Pass in
options.UpsertOptions
to makeHook
work -
If the second parameter doc implement the
Hook
method,options.UpsertOptions
can be ignore in Upsert. -
If you use the document
User
to implement the method and modify the document inBeforeUpsert()
, the modified document will be inserted into the database
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)
// following example also works
// _, err = cli.Upsert(context.Background(), bson.M{"name": "Lucas"}, u, options.UpsertOptions{
// UpsertHook: myHook,
//})
User need to implement struct methods to use Remove Hook
:
BeforeRemove(ctx context.Context) error // implement it for hook before remove
AfterRemove(ctx context.Context) error // implement it for hook before remove
-
A custom structure (
MyRemoveHook
in the following example) implements theHook
method ofRemove
-
Pass in
options.RemoveOptions
to makeHook
work.
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,
})
User need to implement struct methods to use Query Hook
:
BeforeQuery(ctx context.Context) error // implement it for hook before query
AfterQuery(ctx context.Context) error // implement it for hook before query
- A custom struct (
MyQueryHook
in the following example) implements theHook
method ofQuery
- Pass in
options.FindOptions
to makeHook
work.
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)