I wrote some basic code with the intention to create a storage backed counters collection using mongodb (official) drivers. There are a few problems with the code.
- It doesn't update the document when I try to use the previous value.
- It is very verbose.
I am posting this use case here with the hope to get a hint as to what's causing the issue and if there a more elegant/concise way to go about $inc
& $set
when key doesn't exist.
package main
import (
"fmt"
"log"
"context"
"github.com/mongodb/mongo-go-driver/bson"
"github.com/mongodb/mongo-go-driver/mongo"
)
func check(e error) {
if e != nil {
log.Fatal(e)
}
}
func main() {
monday := map[string]int64{
"Cumulus": 1, "Stratus": 2, "Stratocumulus": 4, "Altocumulus": 1, "Nimbostratus": 3,
"Altostratus": 2, "Cirrocumulus": 2, "Cirrostratus": 1, "Cirrus": 1, "Cumulonimbus": 0,
}
sunday := map[string]int64{
"Cumulus": 0, "Stratus": 1, "Stratocumulus": 3, "Altocumulus": 2, "Nimbostratus": 2,
"Altostratus": 1, "Cirrocumulus": 3, "Cirrostratus": 2, "Cirrus": 0, "Cumulonimbus": 2,
}
days := []map[string]int64{sunday, monday}
client, err := mongo.Connect(context.Background(), "mongodb://localhost:27017", nil)
check(err)
db := client.Database("weather")
coll := db.Collection("clouds")
for _, today := range days {
clouds := today
for name, count := range clouds {
v := bson.NewDocument()
if err := coll.FindOne(
context.Background(),
bson.NewDocument(
bson.EC.String("name", name),
),
).Decode(v); err == mongo.ErrNoDocuments {
_, err := coll.InsertOne(
context.Background(),
bson.NewDocument(
bson.EC.String("name", name),
bson.EC.Int64("qty", count),
),
)
check(err)
} else {
prev := v.Lookup("qty").Int64()
_, err := coll.UpdateOne(
context.Background(),
bson.NewDocument(
bson.EC.String("item", name),
),
bson.NewDocument(
bson.EC.SubDocumentFromElements("$inc",
bson.EC.Int64("qty", prev + count),
),
),
)
check(err)
}
}
}
cursor, err := coll.Find(
context.Background(),
bson.NewDocument(),
)
doc := bson.NewDocument()
for cursor.Next(context.Background()) {
doc.Reset()
err := cursor.Decode(doc)
check(err)
name := doc.Lookup("name").StringValue()
qty := doc.Lookup("qty").Int64()
fmt.Println(name, qty)
}
}
UPDATE
The other posts didn't address the question entirely. Here is the code snippet I came up with that solves this issue. I am posting it here with the intention to help others who ran into the exact same issue:
for _, today := range days {
clouds := today
for name, count := range clouds {
var opts []option.UpdateOptioner
opts = append(opts, mongo.Opt.Upsert(true))
filter := bson.NewDocument(bson.EC.String("name", name))
update := bson.NewDocument(
bson.EC.SubDocumentFromElements("$inc", bson.EC.Int64("qty", count)),
bson.EC.SubDocumentFromElements("$set", bson.EC.String("name", name)))
_, err := coll.UpdateOne(context.Background(), filter, update, opts...)
check(err)
}
}