mongofunc is a Go package that provides a fluent query builder for MongoDB. It allows constructing MongoDB find queries using a chained syntax.
The library aims to make querying MongoDB easy and intuitive for Go developers.
- Introduction
- Getting Started
- Moper: Building Queries
- Mocom: MongoClient Commands
- Transaction
- License
With mongofunc, you can write MongoDB find queries using chained method calls instead of constructing nested documents. This results in query code that is:
- Clean - Query logic reads clearly from top to bottom
- Composable - Combine and reuse query conditions easily
- Typed - Fully integrated with Go's type system
For example:
query := moper.Query().
Greater("age", 21).
Equal("status", "active")
The above query filters for age greater than 21 and status equal to "active".
mongofunc handles converting the chained calls to proper MongoDB query filter documents under the hood.
Install the package:
$ go get github.com/func25/mongofunc/v2
Import it in your code:
import "github.com/func25/mongofunc/v2/moper"
You can find full example code at Using Commands
To start a new query:
query := moper.Query()
Start chaining query methods to build your query, let's find all users name Sarah and age greater than 18:
filter := query.Equal("name", "Sarah").
Greater("age", 18)
The package provides functions for MongoDB queries to perform time-based comparisons on documents.
filter := moper.Query().
EqualGreaterTime("expired", time.Now())
Let's take a complex update command as an example:
update := moper.Query().
Inc("age", 1).
Push("hobbies", "guitar").
Unset("addresses.0").
Set("addresses.1", bson.M{
"street": "123 Main St",
"city": "Springfield",
})
Support simple aggregation:
intArr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
matchStage := moper.Query().MatchD(moper.Query().InArray("damage", intArr))
groupStage := moper.Query().Group(
moper.P{K: "_id", V: nil},
moper.P{K: "total", V: moper.Query().Sum("damage")},
)
req := &mocom.AggregationRequest[Hero]{
Pipeline: []moper.D{matchStage, groupStage},
Options: []*options.AggregateOptions{},
}
Let's skim through some query methods:
- Equal(fieldName, value): Adds an equality filter to the pipeline.
- NotEqual(fieldName, value): Adds a non-equality filter to the pipeline.
- InEll(fieldName, value...): Adds an "in" filter to the pipeline.
- NotInEll(fieldName, value...): Adds a "not in" filter to the pipeline.
- EqualLess(fieldName, value): Adds a "less than or equal" filter to the pipeline.
- EqualGreater(fieldName, value): Adds a "greater than or equal" filter to the pipeline.
- Less(fieldName, value): Adds a "less than" filter to the pipeline.
- Greater(fieldName, value): Adds a "greater than" filter to the pipeline.
- Exists(fieldName, exist): Adds an "exists" filter to the pipeline.
- Or(filters...): Adds an "or" filter to the pipeline
- ...
mocom is a Go package that provides a simple MongoDB client wrapper to handle MongoDB connections and collections. It offers convenient functions to connect to a MongoDB server, set the default database, retrieve the client, and get collections for reading and writing.
Use the Connect function to establish a connection to the MongoDB server:
ctx := context.Background()
uri := "mongodb://localhost:27017/?w=majority&retryWrites=false"
dbName := "your-database-name"
if err := mocom.Connect(ctx, uri, dbName); err != nil {
log.Fatal("Failed to connect to MongoDB:", err)
}
// Now you can perform database operations using the mocom and moper package
You can now use the package to retrieve MongoDB collections for reading and writing:
// Get a collection for reading with read preference "Nearest"
readColl := mocom.CollRead("your-collection-name")
// Get a collection for writing with read preference "Primary"
writeColl := mocom.CollWrite("your-collection-name")
Mocom supports CRUD operations with moper commands, let's check a fulll example:
package main
import (
"context"
"fmt"
"time"
"github.com/func25/mongofunc/v2/mocom"
"github.com/func25/mongofunc/v2/moper"
"go.mongodb.org/mongo-driver/mongo/options"
)
type User struct {
mocom.ObjectID `json:",inline" bson:",inline"`
Username string `json:"username" bson:"username"`
Email string `json:"email" bson:"email"`
Age int `json:"age" bson:"age"`
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
}
// This function is needed to satisfy the mocom.Model interface, so we can use it with mocom generic functions
func (u User) CollName() string {
return "users"
}
func main() {
// Connect to MongoDB
ctx, cancel := context.WithTimeout(context.Background(), 1000*time.Second)
defer cancel()
uri := "mongodb://localhost:27017/?w=majority&retryWrites=false"
dbName := "exampledb"
err := mocom.Connect(ctx, uri, dbName)
if err != nil {
fmt.Println("Failed to connect to MongoDB:", err)
return
}
// Perform CRUD operations with the User model
user := User{
Username: "john_doe",
Email: "[email protected]",
Age: 30,
CreatedAt: time.Now(),
}
// Create a new user using mocom.Create[User] syntax
insertedID, err := mocom.CreateT[User](ctx, user)
if err != nil {
fmt.Println("Failed to create user:", err)
return
}
fmt.Println("User created with ID:", insertedID)
// Find all users using moper package
matchStage := moper.Query().MatchD(moper.Query().InArray("age", []int{30, 40}))
groupStage := moper.Query().Group(moper.P{K: "_id", V: nil}, moper.P{K: "total", V: moper.Query().Sum("age")})
// Aggregate the users with matching age and calculate the total age using mocom
aggRequest := &mocom.AggregationRequest{
Pipeline: []moper.D{matchStage, groupStage},
Options: []*options.AggregateOptions{},
}
aggResult, err := mocom.AggregateT[User](ctx, aggRequest)
if err != nil {
fmt.Println("Failed to aggregate users:", err)
return
}
fmt.Println("Users aggregated:", aggResult)
// Update the user's email using moper package
filter := moper.Query().Equal("_id", insertedID)
update := moper.Query().Set(moper.Pr("email", "[email protected]"))
_, err = mocom.UpdateOneT[User](ctx, filter, update)
if err != nil {
fmt.Println("Failed to update user:", err)
return
}
fmt.Println("User updated")
// Find a user by ID using mocom
foundUser, err := mocom.FindOne[User](ctx, filter)
if err != nil {
fmt.Println("Failed to find user by ID:", err)
return
}
fmt.Println("User found by ID:", foundUser)
// Delete the user using mocom
_, err = mocom.DeleteOneT[User](ctx, filter)
if err != nil {
fmt.Println("Failed to delete user:", err)
return
}
fmt.Println("User deleted")
}
mocom provides support for nested transactions to help you manage complex transactional workflows. If a nested transaction is detected, the Tx and TxOptimal functions will execute the transaction with the provided context.
The Tx function executes a MongoDB transaction based on the provided configuration (cfg) and context (ctx). It allows you to perform a series of operations in a transactional manner, ensuring that either all operations succeed, or none of them are applied.
result, err := mocom.Tx(ctx, TransactionConfig{
SessConfig: options.Session(),
Options: options.Transaction(),
Func: func(ctx mongo.SessionContext) (interface{}, error) {
// Your transaction logic here
// e.g., insert, update, or delete operations
// e.g., perform multiple actions as a single atomic transaction
},
})
The TxOptimal function provides an optimal MongoDB transaction without read operations. It is suitable for scenarios where you only need to perform write operations, ensuring atomicity and consistency across multiple writes.
result, err := mocom.TxOptimal(ctx, func(ctx mongo.SessionContext) (interface{}, error) {
// Your transaction logic here
// e.g., insert, update, or delete operations
// e.g., write operations without read operations
})
mongofunc is provided under the MIT License. Use it freely in your Go applications.
Let me know if you would like any changes to the README. I'm happy to help make it as useful as possible!