mongoDB: $inc of a nonexistent document in an array

I was not able to write a code, which would be able to increment a non-existent value in an array.

Let's consider a following structure in a mongo collection. (This is not the actual structure we use, but it maintains the issue)

{
        "_id" : ObjectId("527400e43ca8e0f79c2ce52c"),
        "content" : "Blotted Science",
        "tags_with_ratings" : [
                {
                        "ratings" : {
                                "0" : 6154,
                                "1" : 4974
                        },
                        "tag_name" : "math_core"
                },
                {
                        "ratings" : {
                                "0" : 154,
                                "1" : 474,
                        },
                        "tag_name" : "progressive_metal"
                }
        ]
}

Example issue: We want to add to this document into the tags_with_ratings attribute an incrementation of a rating of a tag, which is not yet added in the array. For example we would want to increment a "0" value for a tag_name "dubstep".

So the expected behaviour would be, that mongo would upsert a document like this into the "tags_with_ratings" attribute:

            {
                    "ratings" : {
                            "0" : 1
                    },
                    "tag_name" : "dubstep"
            }

At the moment, we need to have one read operation, which checks if the nested document for the tag is there. If it's not, we pull the array tags_with_ratings out, create a new one, re-add the values from the previous one and add the new nested document in there. Shouldn't we be able to do this with one upsert operation, without having the expensive read happen?

The incrementation of the values takes up 90% of the process and more than half of it is consumed by reading, because we are unable to use $inc capability of creating an attribute, if it is non-existent in the array.

Answers


You cannot achieve what you want with one step using this schema.

You could do it however if you used tag_name as the key name instead of using ratings there, but then you may have a different issue when querying.

If the tag_name value was the field name (replacing ratings) you'd have {"dubstep":{"0":1}} instead of { "ratings" : {"0" : 1},"tag_name" : "dubstep"} which you can update dynamically the way you want to. Just keep in mind that this schema will make it more difficult to query - you have to know what the ratings are in advance to be able to query by keyname.


Need Your Help

Prevent duplicates from being saved in bash history

bash command-line cygwin history

I'm trying to prevent bash from saving duplicate commands to my history. Here's what I've got: