I’m writing a complex query (for a beginner user) for mongodb using the golang official driver, i’d like share my code to help other user.
I’ll use an aggragate to do the complex query, we need to step the first one to count the records of the query, the second step to get the elements at the request page.
var basePipeline = mongo.Pipeline{
{{“$lookup”, bson.D{
{“from”, “makers”},
{“localField”, “maker”},
{“foreignField”, “username”},
{“as”, “makerinfo”},
}}}}
The first piece fo the pipeline is the lookup, a similar left-join, we want join from the collection of products the makers. With the subpipeline we specify the conditions of the query:
var countPipeline mongo.Pipeline
var productsPipeline mongo.Pipeline
subpipeline := bson.D{
{“$match”, bson.D{{“$and”,
bson.A{
bson.D{{“name”, bson.M{“$regex”: “.*” + initial + “.*”, “$options”: “-i”}}},
bson.D{{“makers”, maker}},
}}
I use two different pipeline, the countPipeline to count of the products, the second to gets the products. To have the count we must add other pipeline:
var countPipeline mongo.Pipeline
var productsPipeline mongo.Pipeline
subpipeline := bson.D{
{"$match", bson.D{{"$and",
bson.A{
bson.D{{"name", bson.M{"$regex": ".*" + initial + ".*", "$options": "-i"}}},
bson.D{{"makers", maker}},
}}
countPipeline = append(basePipeline, subpipeline)
countCondition := bson.D{{"$count", "numberOfProduct"}}
countPipeline = append(countPipeline, countCondition)
productsPipeline = append(basePipeline, subpipeline)
cur, err = GetDB().Collection("products").Aggregate(GetCtx(), countPipeline)
// number of elements
var total int
if err != nil {
fmt.Printf("[Debug] decode error %s", err)
panic(err)
} else {
for cur.Next(GetCtx()) {
dum := bson.M{"np": 0}
if err = cur.Decode(&dum); err != nil {
fmt.Printf("[Debug] decode error %s", err)
}
total = int(dum["numberOfProduct"].(int32))
}
}
Verify the page is in the range of the pages
// number of page
pages := total / 10
if (total % 10) > 0 {
pages++
}
jumpPage, conError := strconv.Atoi(page)
if pages == 0 || conError != nil || jumpPage > pages {
finalResult := bson.M{"products": result, "productsCount": 0, "pages": 0}
return finalResult
}
Finally to gets the elements of the page:
result := []bson.M{}
var nProducts = 0
// get elements
jumpPagePipeline := bson.D{{"$skip", jumpPage}}
limitPipeline := bson.D{{"$limit", 10}}
productsPipeline = append(productsPipeline, jumpPagePipeline)
productsPipeline = append(productsPipeline, limitPipeline)
cur, err = GetDB().Collection("products").Aggregate(GetCtx(), productsPipeline)
// number of elements
if err != nil {
fmt.Printf("[Debug] decode error %s", err)
panic(err)
} else {
for cur.Next(GetCtx()) {
dum := bson.M{}
//var resultDecode bson.M
if err = cur.Decode(&dum); err != nil {
fmt.Printf("[Debug] decode error %s", err)
}
result = append(result, dum)
nProducts++
}
}
finalResult := bson.M{"products": result, "productsCount": nProducts}
I used jumpedPipeline to jump to the page and limit to get only 10 elemets for page.
This example is not the better way to do the pagination, but it can be helpful to understand how build complex query.
For some better code you can see:
https://github.com/gobeam/mongo-go-pagination (it’s not my code)