0

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.

  1. It doesn't update the document when I try to use the previous value.
  2. 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)
        }
    }
Grokify
  • 15,092
  • 6
  • 60
  • 81
John Difool
  • 5,572
  • 5
  • 45
  • 80
  • You're basically looking for ["upserts"](https://docs.mongodb.com/manual/reference/method/db.collection.update/#upsert-option) which are an option available to any "update" variant. In short, where the query condition does not find anything then a new document is created. In such a case [`$inc`](https://docs.mongodb.com/manual/reference/operator/update/inc/) would actually create the new value where not present anyway, so there is no need to alternately use `$set` since `$inc: ` where either the field or document does not exist simply "sets" the value. – Neil Lunn Jun 14 '18 at 03:39
  • @Neil: I found upserts and I updated my post to show what I came up with. The problem was two fold: upserts (your point) and the counter not accumulating. I found the error and fixed it. Thank you. – John Difool Jun 14 '18 at 03:59

0 Answers0