Categorygithub.com/qiniu/qmgo
modulepackage
1.1.9
Repository: https://github.com/qiniu/qmgo.git
Documentation: pkg.go.dev

# README

Qmgo

Build Status Coverage Status Go Report Card GitHub release GoDoc

English | 简体中文

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

  • Qmgo allows users 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, with all official supported options
  • Sort、limit、count、select、distinct
  • Transactions
  • Hooks
  • Automatically default and custom fields
  • Predefine operator keys
  • Aggregate、indexes operation、cursor
  • Validation tags
  • Plugin

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"}})
    cli.CreateIndexes(context.Background(), []options.IndexModel{{Key: []string{"id2", "id3"}}})
    
  • Insert a document

    // insert one document
    result, err := cli.InsertOne(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(ctx context.Context) error {
        fmt.Println("before insert called")
        return nil
    }
    func (u *User) AfterInsert(ctx context.Context) error {
        fmt.Println("after insert called")
        return nil
    }
    
    u := &User{Name: "Alice", Age: 7}
    _, err := cli.InsertOne(context.Background(), u)
    

    More about hooks

  • Automatically 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

  • Validation tags

    Qmgo Validation tags is Based on go-playground/validator.

    So Qmgo support all validations on structs in go-playground/validator, such as:

    type User struct {
        FirstName string            `bson:"fname"`
        LastName  string            `bson:"lname"`
        Age       uint8             `bson:"age" validate:"gte=0,lte=130" `    // Age must in [0,130]
        Email     string            `bson:"e-mail" validate:"required,email"` //  Email can't be empty string, and must has email format
        CreateAt  time.Time         `bson:"createAt" validate:"lte"`          // CreateAt must lte than current time
        Relations map[string]string `bson:"relations" validate:"max=2"`       // Relations can't has more than 2 elements
    }
    

    Qmgo tags only supported in following API: InsertOne、InsertyMany、Upsert、UpsertId、ReplaceOne

  • Plugin

    • Implement following method:
    func Do(ctx context.Context, doc interface{}, opType operator.OpType, opts ...interface{}) error{
      // do anything
    }
    
    • Call Register() in package middleware, register the method Do

      Qmgo will call Do before and after the operation

    middleware.Register(Do)
    

    Example

    The hookautomatically fields and validation tags in Qmgo run on plugin.

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:

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Functions

CompareVersions compares two version number strings (i.e.
IsDup check if err is mongo E11000 (duplicate err)。.
IsErrNoDocuments check if err is no documents, both mongo-go-driver error and qmgo custom error Deprecated, simply call if err == ErrNoSuchDocuments or if err == mongo.ErrNoDocuments.
NewClient creates Qmgo MongoDB client.
NewObjectID generates a new ObjectID.
Now return Millisecond current time.
Open creates client instance according to config QmgoClient can operates all qmgo.client 、qmgo.database and qmgo.collection.
SplitSortField handle sort symbol: "+"/"-" in front of field if "+", return sort as 1 if "-", return sort as -1.

# Variables

ErrNoSuchDocuments return if no document found.
ErrNotSupportedPassword return if password is invalid.
ErrNotSupportedUsername return if username is invalid.
ErrNotValidSliceToInsert return if insert argument is not valid slice.
ErrQueryNotSlicePointer return if result argument is not a pointer to a slice.
ErrQueryNotSliceType return if result argument is not slice address.
ErrQueryResultTypeInconsistent return if result type is not equal mongodb value type.
ErrQueryResultValCanNotChange return if the value of result can not be changed.
ErrReplacementContainUpdateOperators return if replacement document contain update operators.
ErrTransactionNotSupported return if transaction not supported.
ErrTransactionRetry return if transaction need to retry.

# Structs

Aggregate is a handle to a aggregate.
Bulk is context for batching operations to be sent to database in a single bulk write.
BulkResult is the result type returned by Bulk.Run operation.
Change holds fields for running a findAndModify command via the Query.Apply method.
Client creates client to mongo.
Collection is a handle to a MongoDB collection.
Config for initial mongodb instance.
Credential can be used to provide authentication options when configuring a Client.
Cursor struct define.
Database is a handle to a MongoDB database.
DeleteResult is the result type returned by DeleteOne and DeleteMany operations.
InsertManyResult is a result type returned by an InsertMany operation.
InsertOneResult is the result type returned by an InsertOne operation.
QmgoClient specifies the instance to operate mongoDB.
Query struct definition.
ReadPref determines which servers are considered suitable for read operations.
Session is an struct that represents a MongoDB logical session.
UpdateResult is the result type returned from UpdateOne, UpdateMany, and ReplaceOne operations.

# Interfaces

AggregateI define the interface of aggregate.
CursorI Cursor interface.
QueryI Query interface.

# Type aliases

alias mongo drive bson primitives thus user don't need to import go.mongodb.org/mongo-driver/mongo, it's all in qmgo.
alias mongo drive bson primitives thus user don't need to import go.mongodb.org/mongo-driver/mongo, it's all in qmgo.
alias mongo drive bson primitives thus user don't need to import go.mongodb.org/mongo-driver/mongo, it's all in qmgo.
alias mongo drive bson primitives thus user don't need to import go.mongodb.org/mongo-driver/mongo, it's all in qmgo.
Pipeline define the pipeline for aggregate.