Overview
This tutorial will introduce how to use the official Golang driver Mongo Go Driver to connect to MongoDB, manipulate MongoDB and some operations of indexes. More on how to use it, of course, there are some points of note are also mentioned, in fact, it is also stepped in some pits.
API usage
import dependency package
package main
import (
"context"
"fmt"
"os"
"time"
// The official "mongo-go-driver" package
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/x/bsonx"
)
Connecting to the DB
Use options.Client()
to create a parameter option for the MongoDB connection, you can add all the options you need, and the MongoDB URI information is also added here:
[[email protected]]# cat main.go
// Declare the host and port options to be passed to the Connect() method
var opts = options.Client().
ApplyURI(addr).
SetConnectTimeout(time.Second * 5).
SetSocketTimeout(time.Second * 5).
SetWriteConcern(writeconcern.New(writeconcern.WMajority()))
Here I use a number of parameters.
ApplyURI
: specifies the address information for MongoDBSetConnectTimeout
andSetSocketTimeout
: some options for connection controlSetWriteConcern
: settings for read/write separation
Then execute the following command, passing the clientOptions
instance to the mongo.Connect()` method, and make sure you also pass a
context`` object, which you can do a lot of things with, such as controlling the connection timeout timeout and such.
[[email protected]]# cat main.go
// Connect to MongoDB and return the client instance
client, err := mongo.Connect(context.TODO(), clientOptions)
if err ! =nil {
fmt.Println("mongo.Connect() ERROR:", err)
os.Exit(1)
}
Operation Collection
After you have a Client, you still need to specify the DB and the Collection to operate on a specific Collection, e.g.
[[email protected]]# cat main.go
// Accessing a MongoDB collection via the database
col := client.Database("db").Collection("collection")
CRUD would be too simple, just look at the example.
CRUD
Get
The point of the Get method is actually that the value returned by FindOne is SingleResult, and then some common errors can be dealt with.
[[email protected]]# cat main.go
func (r *postRepository) Get(ctx context.Context, id string) (post *postDoc, err error) {
var coll = r.client.Database(r.dbName).Collection(r.collName)
var singleResult *mongo.
if singleResult = coll.FindOne(ctx, bson.M{"post_id": id}); singleResult.Err() ! = nil {
if singleResult.Err() == mongo.ErrNoDocuments {
return nil, utils.ErrNotFound
}
return nil, errors.Wrap(singleResult.Err(), "query doc")
}
var doc postDoc
if err = singleResult.Decode(&doc); err ! = nil {
return nil, errors.Wrap(err, "decode doc")
}
return &doc, nil
}
List
List is a bit more complicated and has several main points.
- Sorting needs to consider the sorted fields are in order
- The query returns a Cursor, which is a connection state, and needs to be concerned about the closure of the connection
[[email protected]]# cat main.go
func (r *postRepository) List(
ctx context.Context,
selector map[string]interface{},
sorter []string,
page, pageSize int,
) (posts []postDoc, err error) {
var coll = r.client.Database(r.dbName).Collection(r.collName)
var cursor *mongo.
var sortOpts = bsonx.Doc{}
for _, s := range sorter {
var order = int32(1)
if strings.HasPrefix(s, "-") {
order = int32(-1)
s = s[1:]
}
sortOpts = append(sortOpts, bsonx.Elem{
Key: s,
Value: bsonx.Int32(order),
})
}
var opts = options.Find().
SetSort(sortOpts).
SetSkip(int64((page - 1) * pageSize)).
SetLimit(int64(pageSize))
Find(ctx, selector, opts); err ! = nil {
return nil, errors.Wrap(err, "query docs")
}
var postDocs []postDoc
if err = cursor.All(ctx, &postDocs); err ! = nil {
return nil, errors.Wrap(err, "decode docs")
}
return postDocs, nil
}
Here I’m using cursor.All
to decode the data, and another more common usage is this
[[email protected]]# cat main.go
var postDocs []postDoc
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var postDoc postDoc
if err = cursor.Decode(&postDoc); err ! = nil {
return nil, errors.Wrap(err, "decode doc")
}
postDocs = append(postDocs, postDoc)
}
Here is the form of iterator to decode data, using this method remember to be sure to actively close the Cursor, otherwise it may cause a connection leak.
Delete
Delete is relatively simple, the only thing worth mentioning is how to determine if the deleted element does not exist, here I am judging the result of the deletion: DeleteCount
to determine if the element is really deleted:
[[email protected]]# cat main.go
func (r *postRepository) Delete(ctx context.Context, id string) (err error) {
var coll = r.client.Database(r.dbName).Collection(r.collName)
var delRst *mongo.DeleteResult
if delRst, err = coll.DeleteOne(ctx, bson.M{"post_id": id}); err ! = nil {
return errors.Wrap(err, "delete doc")
}
if delRst.DeletedCount == 0 {
return utils.ErrNotFound
}
return nil
}
Update
Update is also a relatively simple operation, a point to note is whether to allow upsert
, here is set this option: ``` go
[[email protected]]# cat main.go
func (r *postRepository) Update(ctx context.Context, post *postDoc) (rtn *postDoc, err error) {
var coll = r.client.Database(r.dbName).Collection(r.collName)
var opts = options.Update().SetUpsert(true)
_, err = coll.UpdateOne(ctx, bson.M{"post_id": post.PostId}, bson.M{"$set": post}, opts)
if err ! = nil {
return nil, errors.Wrap(err, "upsert doc")
}
return post, nil
}
Create
One point in creating an element is what the ID of the inserted element is, which is determined by a return value in the Mongo Go Driver, but the return is an interface{}
, which needs to be converted to
[[email protected]]# cat main.go
func (r *postRepository) Save(ctx context.Context, post *postDoc) (rtn *postDoc, err error) {
var coll = r.client.Database(r.dbName).Collection(r.collName)
var insertOneResult *mongo.
InsertOne(ctx, post); err ! = nil {
return nil, errors.Wrap(err, "save doc")
}
post.Id = insertOneResult.InsertedID.(primitive.ObjectID)
return post, nil
}
Indexes
Creating a single index
Regardless of the language used, the method calls to create an index using the latest driver are the createIndex()
, createOne()
, create_index()
methods, or in the case of Go’s official driver API, the Indexes().CreateOne
method. This method call requires passing a key, or a field to index the data, and an integer in sort order, either 1
in ascending order or -1
in descending order. The option for the second parameter may also be required. Here is an example.
[[email protected]]# db.coll.createIndex( |CONTEXT|, { |KEY| : |SORT_ORDER|, |OPTIONS| } )
This is index_view.go
in mongo-go-driver
on Github, the method has the following method prototype.
// CreateOne creates an index in the collection specified by the model.
func (iv IndexView) CreateOne(ctx context.Context, model IndexModel, opts, *options.CreateIndexesOptions) (string, error)
Optionally, the options.Index()
method call can be passed to the Options
parameter of the IndexModel. The following example sets a unique index for the id of the blog.
func (r *postRepository) createIndex(ctx context.Context) (err error) {
var coll = r.client.Database(r.dbName).Collection(r.collName)
var indexModel = mongo.IndexModel{
Keys: bsonx.Doc{
{
Key: "post_id",
},
},
Options: options.Index().SetUnique(true),
}
var createOpts = options.CreateIndexes().SetMaxTime(time.Second * 10)
if _, err = coll.Indexes().CreateOne(ctx, indexModel, createOpts); err != nil {
return errors.Wrap(err, "create unique post_id index")
}
return nil
}
Creating multiple indexes at the same time
The difference between creating multiple indexes and creating a single index is twofold.
- The creation function is changed from
CreateOne
toCreateMany
. - The creation parameter is changed from
IndexModel
to[]IndexModel
.
Everything else is similar:
[[email protected]]# cat main.go
func (r *postRepository) createIndexes(ctx context.Context) (err error) {
var coll = r.client.Database(r.dbName).Collection(r.collName)
var indexModels = []mongo.IndexModel{
{
Keys: bsonx.Doc{
{
Key: "post_id",
},
},
Options: options.Index().SetUnique(true),
},
{
Keys: bsonx.Doc{
{
Key: "type",
Value: bsonx.Int32(1),
},
},
},
{
Keys: bsonx.Doc{
{
Key: "status",
Value: bsonx.Int32(1),
},
},
},
}
var createOpts = options.CreateIndexes().SetMaxTime(time.Second * 10)
if _, err = coll.Indexes().CreateMany(ctx, indexModels, createOpts); err != nil {
return errors.Wrap(err, "create indexes")
}
return nil
}