service/vendor/github.com/qiniu/qmgo/README.md

382 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Qmgo
[![Build Status](https://travis-ci.org/qiniu/qmgo.png?branch=master)](https://travis-ci.org/qiniu/qmgo)
[![Coverage Status](https://codecov.io/gh/qiniu/qmgo/branch/master/graph/badge.svg)](https://codecov.io/gh/qiniu/qmgo)
[![Go Report Card](https://goreportcard.com/badge/github.com/qiniu/qmgo)](https://goreportcard.com/report/github.com/qiniu/qmgo)
[![GitHub release](https://img.shields.io/github/v/tag/qiniu/qmgo.svg?label=release)](https://github.com/qiniu/qmgo/releases)
[![GoDoc](https://pkg.go.dev/badge/github.com/qiniu/qmgo?status.svg)](https://pkg.go.dev/github.com/qiniu/qmgo?tab=doc)
English | [简体中文](README_ZH.md)
`Qmgo` is a `Go` `driver` for `MongoDB` . It is based on [MongoDB official driver](https://github.com/mongodb/mongo-go-driver), but easier to use like [mgo](https://github.com/go-mgo/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
```go
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`:
```go
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 `client`、`db` or `coll`***
Make sure to defer a call to Disconnect after instantiating your client:
```go
defer func() {
if err = cli.Close(ctx); err != nil {
panic(err)
}
}()
```
- Create index
Before doing the operation, we first initialize some data:
```go
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
```go
cli.CreateOneIndex(context.Background(), options.IndexModel{Key: []string{"name"}})
cli.CreateIndexes(context.Background(), []options.IndexModel{{Key: []string{"id2", "id3"}}})
```
- Insert a document
```go
// insert one document
result, err := cli.InsertOne(ctx, userInfo)
```
- Find a document
```go
// find one document
one := UserInfo{}
err = cli.Find(ctx, bson.M{"name": userInfo.Name}).One(&one)
```
- Delete documents
```go
err = cli.Remove(ctx, bson.M{"age": 7})
```
- Insert multiple data
```go
// 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
```go
// find all, sort and limit
batch := []UserInfo{}
cli.Find(ctx, bson.M{"age": 6}).Sort("weight").Limit(7).All(&batch)
```
- Count
````go
count, err := cli.Find(ctx, bson.M{"age": 6}).Count()
````
- Update
````go
// 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
````go
err := cli.Find(ctx, bson.M{"age": 10}).Select(bson.M{"age": 1}).One(&one)
````
- Aggregate
```go
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
````go
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 `timeout`、`retry`:
````go
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](https://github.com/qiniu/qmgo/wiki/Transactions)
- Predefine operator keys
````go
// 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:
````go
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](https://github.com/qiniu/qmgo/wiki/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 `createAt`、`updateAt` and `_id` in update and insert operation.
````go
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.
```go
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](https://github.com/qiniu/qmgo/blob/master/field_test.go)
[More about automatically fields](https://github.com/qiniu/qmgo/wiki/Automatically-update-fields)
- Validation tags
Qmgo Validation tags is Based on [go-playground/validator](https://github.com/go-playground/validator).
So Qmgo support [all validations on structs in go-playground/validator](https://github.com/go-playground/validator#usage-and-documentation), such as:
```go
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:
```go
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](operator/operate_type.go)
```go
middleware.Register(Do)
```
[Example](middleware/middleware_test.go)
The `hook`、`automatically 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 in`go.mongodb.org/mongo-driver`:
```go
// 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`:
```go
// 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](https://github.com/qiniu/qmgo/wiki/Differences-between-Qmgo-and-Mgo)
## Contributing
The Qmgo project welcomes all contributors. We appreciate your help!
## Communication:
- Join [qmgo discussions](https://github.com/qiniu/qmgo/discussions)