Skip to content
jiangz222 edited this page Nov 13, 2020 · 18 revisions

About 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, then Qmgo automatically do callback.

  • Only InsertOneInsertManyUpsert and ReplaceOne can do hook without options

  • If the Hook operation fails, there is no rollback operation in current version.

Insert Hook

User need to implement struct methods to use Insert Hook

BeforeInsert() error // implement it for hook before insert
AfterInsert() error  // implement it for hook after insert

InsertOne

In the InsertOne process

  • Implement the Hook method of Insert through a custom struct (User in the following example)

  • Pass in options.InsertOneOptions to make Hook 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 in BeforeInsert(), the modified document will be inserted into the database

type User struct {
	Name         string    `bson:"name"`
	Age          int       `bson:"age"`
}
func (u *User) BeforeInsert() error {
  fmt.Println("before insert called")
	return nil
}
func (u *User) AfterInsert() 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,
// })

InsertMany

In the InsertMany process

  • Implement the Hook method of Insert through a custom structure (User in the following example),

  • Pass in options.InsertManyOptions to make Hook work.

  • If the second parameter docs implement the Hook method, options.InsertManyOptions can be ignore in InsertMany.

  • If you use the document User to implement method and modify the document in BeforeInsert(), the modified document will be inserted into the database

  • Because multiple documents are inserted, and Hook happens for each document, so Hook 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() error {
  fmt.Println("before insert called")
	return nil
}
func (u *User) AfterInsert() 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,
// })

Update Hook

User need to implement struct methods to use Update Hook

BeforeUpdate() error // implement it for hook before update
AfterUpdate() error // implement it for hook after update

UpdateOne/UpdateAll

  • Implement the Hook method of Update through a custom struct (MyUpdateHook in the following example)

  • Pass in options.UpdateOptions to make Hook work.

  • If you use the document User to directly implement methods and modify the document in the BeforeUpdate(), will not affect the document written in the database.

type MyUpdateHook struct {
	beforeUpdateCount int
	afterUpdateCount  int
}
func (u *MyUpdateHook) BeforeUpdate() error {
	u.beforeUpdateCount++
	return nil
}
func (u *MyUpdateHook) AfterUpdate() 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,
})

ReplaceOne

  • Implement the Hook method of Update through a custom struct (User in the following example)

  • Pass in options.UpdateOptions to make Hook work.

  • If the third parameter doc implement the Hook method, options.UpdateOptions can be ignore in ReplaceOne.

  • If you use the document User to directly implement methods and modify the document in the BeforeUpdate(), 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() error {
	u.beforeUpdate++
	return nil
}

func (u *User) AfterUpdate() 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,
// })

Upsert Hook

User need to implement struct methods to use Upsert Hook

BeforeUpsert() error // implement it for hook before upsert
AfterUpsert() error // implement it for hook before upsert

In the Upsert API

  • Implement the Hook method of Upsert through a custom struct (User in the following example)

  • Pass in options.UpsertOptions to make Hook 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 in BeforeUpsert(), the modified document will be inserted into the database

type User struct {
	Name         string    `bson:"name"`
	Age          int       `bson:"age"`
}
func (u *User) BeforeUpsert() error {
	return nil
}

func (u *User) AfterUpsert() 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,
//})

Remove Hook

User need to implement struct methods to use Remove Hook

BeforeRemove() error // implement it for hook before remove
AfterRemove() error // implement it for hook before remove

Remove/RemoveAll

  • A custom structure (MyRemoveHook in the following example) implements the Hook method of Remove

  • Pass in options.RemoveOptions to make Hook work.

type MyRemoveHook struct {
	beforeCount int
	afterCount  int
}

func (m *MyRemoveHook) BeforeRemove() error {
	m.beforeCount++
	return nil
}

func (m *MyRemoveHook) AfterRemove() 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,
})

QueryHook

User need to implement struct methods to use Query Hook

BeforeQuery() error
AfterQuery() error

Find().One/Find().All()

  • A custom struct (MyQueryHook in the following example) implements the Hook method of Query
  • Pass in options.FindOptions to make Hook work.
type MyQueryHook struct {
	beforeCount int
	afterCount  int
}

func (q *MyQueryHook) BeforeQuery() error {
	q.beforeCount++
	return nil
}

func (q *MyQueryHook) AfterQuery() 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)
Clone this wiki locally