Skip to content

Latest commit

 

History

History
340 lines (257 loc) · 9.4 KB

README.md

File metadata and controls

340 lines (257 loc) · 9.4 KB

Qmgo

Build Status Coverage Status Go Report Card GitHub release GoDoc Join the chat at https://gitter.im/qiniu/qmgo

English | 简体中文

Qmgo is a MongoDB driver for Go . It is based on MongoDB official driver, but easier to use like mgo (such as the chain call).

  • Qmgo can allow user to use the new features of MongoDB in a more elegant way.

  • Qmgo is the first choice for migrating from mgo to the new MongoDB driver with minimal code changes.

Requirements

-Go 1.10 and above.

-MongoDB 2.6 and above.

Features

  • CRUD to documents
  • Sort、limit、count、select、distinct
  • Transactions
  • Hooks
  • Automatically update default and custom fields
  • Predefine operator keys
  • Aggregate、indexes operation、cursor
  • All options when create connection, like: connection pool、pool monitor、auth、readPreference

Installation

  • Use go mod to automatically install dependencies by import github.com/qiniu/qmgo

Or

  • Use go get github.com/qiniu/qmgo

Usage

  • Start

import and create a new connection

import (
    "context"
  
    "github.com/qiniu/qmgo"
)

ctx := context.Background()
client, err := qmgo.NewClient(ctx, &qmgo.Config{Uri: "mongodb://localhost:27017"})
db := client.Database("class")
coll := db.Collection("user")

If your connection points to a fixed database and collection, recommend using the following way to initialize the connection. All operations can be based on cli:

cli, err := qmgo.Open(ctx, &qmgo.Config{Uri: "mongodb://localhost:27017", Database: "class", Coll: "user"})

The following examples will be based on cli, if you use the first way for initialization, replace cli with clientdb or coll

Make sure to defer a call to Disconnect after instantiating your client:

defer func() {
if err = cli.Close(ctx); err != nil {
        panic(err)
    }
}()
  • Create index

Before doing the operation, we first initialize some data:

type UserInfo struct {
	Name   string `bson:"name"`
	Age    uint16 `bson:"age"`
	Weight uint32 `bson:"weight"`
}

var userInfo = UserInfo{
    Name: "xm",
    Age: 7,
    Weight: 40,
}

Create index

cli.CreateOneIndex(context.Background(), options.IndexModel{Key: []string{"name"}, Unique: true})
cli.CreateIndexes(context.Background(), []options.IndexModel{{Key: []string{"id2", "id3"}}})
  • Insert a document
// insert one document
result, err := cli.Insert(ctx, userInfo)
  • Find a document
// find one document
  one := UserInfo{}
  err = cli.Find(ctx, bson.M{"name": userInfo.Name}).One(&one)
  • Delete documents
err = cli.Remove(ctx, bson.M{"age": 7})
  • Insert multiple data
// multiple insert
var userInfos = []UserInfo{
	UserInfo{Name: "a1", Age: 6, Weight: 20},
	UserInfo{Name: "b2", Age: 6, Weight: 25},
	UserInfo{Name: "c3", Age: 6, Weight: 30},
	UserInfo{Name: "d4", Age: 6, Weight: 35},
	UserInfo{Name: "a1", Age: 7, Weight: 40},
	UserInfo{Name: "a1", Age: 8, Weight: 45},
}
result, err = cli.Collection.InsertMany(ctx, userInfos)
  • Search all, sort and limit
// find all, sort and limit
batch := []UserInfo{}
cli.Find(ctx, bson.M{"age": 6}).Sort("weight").Limit(7).All(&batch)
  • Count
count, err := cli.Find(ctx, bson.M{"age": 6}).Count()
  • Update
// UpdateOne one
err := cli.UpdateOne(ctx, bson.M{"name": "d4"}, bson.M{"$set": bson.M{"age": 7}})

// UpdateAll
result, err := cli.UpdateAll(ctx, bson.M{"age": 6}, bson.M{"$set": bson.M{"age": 10}})
  • Select
err := cli.Find(ctx, bson.M{"age": 10}).Select(bson.M{"age": 1}).One(&one)
  • Aggregate
matchStage := bson.D{{"$match", []bson.E{{"weight", bson.D{{"$gt", 30}}}}}}
groupStage := bson.D{{"$group", bson.D{{"_id", "$name"}, {"total", bson.D{{"$sum", "$age"}}}}}}
var showsWithInfo []bson.M
err = cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}).All(&showsWithInfo)
  • Support All mongoDB Options when create connection
poolMonitor := &event.PoolMonitor{
	Event: func(evt *event.PoolEvent) {
		switch evt.Type {
		case event.GetSucceeded:
			fmt.Println("GetSucceeded")
		case event.ConnectionReturned:
			fmt.Println("ConnectionReturned")
		}
	},
}
opt := options.Client().SetPoolMonitor(poolMonitor)  // more options use the chain options.
cli, err := Open(ctx, &Config{Uri: URI, Database: DATABASE, Coll: COLL}, opt) 
  • Transactions

The super simple and powerful transaction, with features like timeoutretry:

callback := func(sessCtx context.Context) (interface{}, error) {
    // Important: make sure the sessCtx used in every operation in the whole transaction
    if _, err := cli.InsertOne(sessCtx, bson.D{{"abc", int32(1)}}); err != nil {
        return nil, err
    }
    if _, err := cli.InsertOne(sessCtx, bson.D{{"xyz", int32(999)}}); err != nil {
        return nil, err
    }
    return nil, nil
}
result, err = cli.DoTransaction(ctx, callback)

More about transaction

  • Predefine operator keys
// aggregate
matchStage := bson.D{{operator.Match, []bson.E{{"weight", bson.D{{operator.Gt, 30}}}}}}
groupStage := bson.D{{operator.Group, bson.D{{"_id", "$name"}, {"total", bson.D{{operator.Sum, "$age"}}}}}}
var showsWithInfo []bson.M
err = cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}).All(&showsWithInfo)
  • Hooks

Qmgo flexible hooks:

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("after insert called")
  	return nil
}

u := &User{Name: "Alice", Age: 7}
_, err := cli.InsertOne(context.Background(), u)

More about hooks

  • Automatically update fields

    Qmgo support two ways to make specific fields automatically update in specific API

    • Default fields

    Inject field.DefaultField in document struct, Qmgo will update createAtupdateAt and _id in update and insert operation.

    type User struct {
      field.DefaultField `bson:",inline"`
    
      Name string `bson:"name"`
      Age  int    `bson:"age"`
    }
    
    u := &User{Name: "Lucas", Age: 7}
    _, err := cli.InsertOne(context.Background(), u)
    // Fields with tag createAt、updateAt and _id will be generated automatically 
    • Custom fields

    Define the custom fields, Qmgo will update them in update and insert operation.

    type User struct {
        Name string `bson:"name"`
        Age  int    `bson:"age"`
    
        MyId         string    `bson:"myId"`
        CreateTimeAt time.Time `bson:"createTimeAt"`
        UpdateTimeAt int64     `bson:"updateTimeAt"`
    }
    // Define the custom fields
    func (u *User) CustomFields() field.CustomFieldsBuilder {
        return field.NewCustom().SetCreateAt("CreateTimeAt").SetUpdateAt("UpdateTimeAt").SetId("MyId")
    }
    
    u := &User{Name: "Lucas", Age: 7}
    _, err := cli.InsertOne(context.Background(), u)
    // CreateTimeAt、UpdateTimeAt and MyId will be generated automatically 
    
    // suppose Id and ui is ready
    err = cli.ReplaceOne(context.Background(), bson.M{"_id": Id}, &ui)
    // UpdateTimeAt will update

Check examples here

More about automatically fields

Qmgo vs go.mongodb.org/mongo-driver

Below we give an example of multi-file search、sort and limit to illustrate the similarities between qmgo and mgo and the improvement compare to go.mongodb.org/mongo-driver. How do we do ingo.mongodb.org/mongo-driver:

// go.mongodb.org/mongo-driver
// find all, sort and limit
findOptions := options.Find()
findOptions.SetLimit(7) // set limit
var sorts D
sorts = append(sorts, E{Key: "weight", Value: 1})
findOptions.SetSort(sorts) // set sort

batch := []UserInfo{}
cur, err := coll.Find(ctx, bson.M{"age": 6}, findOptions)
cur.All(ctx, &batch)

How do we do in Qmgo and mgo:

// qmgo
// find all, sort and limit
batch := []UserInfo{}
cli.Find(ctx, bson.M{"age": 6}).Sort("weight").Limit(7).All(&batch)

// mgo
// find all, sort and limit
coll.Find(bson.M{"age": 6}).Sort("weight").Limit(7).All(&batch)

Qmgo vs mgo

Differences between qmgo and mgo

Contributing

The Qmgo project welcomes all contributors. We appreciate your help!

Communication:

avatar