A generic MongoDB repository library for Go that provides a type-safe, fluent API for CRUD operations, query building, pagination, and aggregation. Built on top of the official MongoDB Go Driver.
go get github.com/MetaDiv-AI/metamongo- Go 1.23+
- MongoDB 4.0+
- Generic repository pattern — Type-safe repositories for any model
- Fluent query builder — Chainable API for building MongoDB queries
- CRUD operations — Save, SaveAll, Delete, DeleteAll, QueriedDelete
- Rich query operators — Eq, Neq, Gt, Lt, In, Like, Regex, geospatial, and more
- Pagination & sorting — Built-in support for paginated queries
- Aggregation — Pipeline builder and direct aggregation support
- Index management — IndexBuilder for creating text, compound, unique, TTL, and geospatial indexes
- Connection helper — MongoConnector for easy connection setup (local and Atlas)
Models must have an ID field of type primitive.ObjectID or string, and optionally implement IModel for collection name:
import (
"go.mongodb.org/mongo-driver/bson/primitive"
)
type User struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id"`
Name string `bson:"name" json:"name"`
Email string `bson:"email" json:"email"`
}
func (u *User) CollectionName() string {
return "users"
}import (
"github.com/MetaDiv-AI/metamongo"
)
// Using the connector (Atlas or local)
connector := metamongo.NewConnector()
client, err := connector.
URI("cluster0.xxxxx.mongodb.net"). // or "localhost:27017" for local
Username("user").
Password("password").
Database("mydb").
Connect()
if err != nil {
log.Fatal(err)
}
defer client.Disconnect(context.Background())
// Or with an existing mongo.Client
db := metamongo.NewDatabase(client, "mydb")// Create repository
repo := metamongo.NewRepository[User](db, "users")
// Save (upsert)
user := &User{Name: "Alice", Email: "alice@example.com"}
afterSave := repo.Save(user)
savedUser, err := afterSave.Model()
// Find by ID
user, err := repo.FindByHex("507f1f77bcf86cd799439011")
// Find with query
qb := metamongo.NewQueryBuilder()
users, err := repo.
Query(qb.Field("name").Eq("Alice")).
Sorting(metamongo.Sort("name", true)).
FindMany()
// Paginated find
users, pagination, err := repo.
Query(qb.Field("email").Like("example")).
Pagination(metamongo.Paginate(1, 10)).
Sorting(metamongo.Sort("name", true)).
PagedFindMany()
// Delete
err = repo.Delete(user)Build complex queries with a fluent API:
qb := metamongo.NewQueryBuilder()
// Simple comparisons
qb.Field("age").Eq(25)
qb.Field("age").Gt(18).Lt(65)
qb.Field("status").In([]string{"active", "pending"})
qb.Field("name").Like("john")
qb.Field("email").Regex("^admin@", "i")
// Null checks
qb.Field("deletedAt").IsNull()
qb.Field("optional").Exists()
// Array operations
qb.Field("tags").Size(3)
qb.Field("tags").All([]string{"go", "mongodb"})
qb.Field("items").ElemMatch(bson.M{"status": "active"})
// Logical operators
qb.And(
qb.Field("age").Gte(18),
qb.Field("status").Eq("active"),
)
qb.Or(
qb.Field("role").Eq("admin"),
qb.Field("role").Eq("moderator"),
)
// Text search (requires text index)
qb.Text("search term")
// Geospatial
qb.Field("location").NearSphere(bson.M{
"$geometry": bson.M{
"type": "Point",
"coordinates": []float64{-73.9667, 40.78},
},
})| Method | Description |
|---|---|
Save(model) |
Upsert a single document |
SaveAll(models) |
Upsert multiple documents |
Delete(model) |
Delete by document reference |
DeleteAll(models) |
Delete multiple documents |
QueriedDelete(query) |
Delete documents matching query |
| Method | Description |
|---|---|
FindOne() |
Find single document |
FindMany() |
Find multiple documents |
FindById(id) |
Find by ObjectID |
FindByHex(hex) |
Find by hex string ID |
FindManyByIds(ids) |
Find by slice of hex IDs |
Count() |
Count matching documents |
PagedFindMany() |
Find with pagination metadata |
Aggregate(pipeline) |
Run aggregation pipeline |
Distinct(field) |
Get distinct values |
Chain these before executing a query:
Query(query)— Set filterPagination(pagination)— Set page and sizeSorting(sorting)— Set sort field and directionProjection(projection)— Set field projectionSkip(n)/Limit(n)— Set skip/limit
Create indexes on collections:
indexes := metamongo.NewIndexBuilder().
AddTextIndex("name", "description").
AddCompoundIndex(bson.D{{Key: "email", Value: 1}}).
AddUniqueIndex(bson.D{{Key: "email", Value: 1}}).
AddTTLIndex("expiresAt", 3600).
AddGeospatialIndex("location", "2dsphere").
Build()
err := metamongo.CreateIndexes(db, "users", indexes)Build aggregation pipelines fluently:
pipeline := metamongo.NewAggregateBuilder().
Match(bson.M{"status": "active"}).
Group(bson.M{
"_id": "$category",
"count": bson.M{"$sum": 1},
}).
Sort(bson.M{"count": -1}).
Limit(10).
Build()
results, err := repo.Aggregate(pipeline)Or execute directly:
results, err := metamongo.NewAggregateBuilder().
Match(bson.M{"status": "active"}).
Count("total").
Execute(db, "users")See the repository for license information.