KTAnj
KTAnj

Reputation: 1356

MongoDB - Increase nested values in Object

Please consider "group" collection following document

"auto_availability" : {
                "54c5c59d75de3e8d0a8b4567" : 12,
                "54c5c59d75de3e8d0a8b4568" : 12,
                "54c5c59d75de3e8d0a8b4569" : 12,
                "54c5c59d75de3e8d0a8b456a" : 12,
                "54c5c59d75de3e8d0a8b456b" : 12,
                "54c5c59d75de3e8d0a8b456c" : 12,
                "54c5c59d75de3e8d0a8b456d" : 12,
                "54c5c59d75de3e8d0a8b456e" : 12,
                "54c5c59d75de3e8d0a8b456f" : 12,
                "54c5c59d75de3e8d0a8b4570" : 12,
                "54c5c59d75de3e8d0a8b4571" : 12,
                "54c5c59d75de3e8d0a8b4572" : 12,
                "54c5c59d75de3e8d0a8b4573" : 12,
                "54c5c59d75de3e8d0a8b4574" : 12,
                "54c5c59d75de3e8d0a8b4575" : 12,
                "54c5c59d75de3e8d0a8b4576" : 12,
                "54c5c59d75de3e8d0a8b4577" : 12,
                "54c5c59d75de3e8d0a8b4578" : 12
            }

I need to increase 4 fields from starting "54c5c59d75de3e8d0a8b4568" by 10.

Expected Result:

"auto_availability" : {
                "54c5c59d75de3e8d0a8b4567" : 12,
                "54c5c59d75de3e8d0a8b4568" : 22,
                "54c5c59d75de3e8d0a8b4569" : 22,
                "54c5c59d75de3e8d0a8b456a" : 22,
                "54c5c59d75de3e8d0a8b456b" : 22,
                "54c5c59d75de3e8d0a8b456c" : 12,
                "54c5c59d75de3e8d0a8b456d" : 12,
                "54c5c59d75de3e8d0a8b456e" : 12,
                "54c5c59d75de3e8d0a8b456f" : 12,
                "54c5c59d75de3e8d0a8b4570" : 12,
                "54c5c59d75de3e8d0a8b4571" : 12,
                "54c5c59d75de3e8d0a8b4572" : 12,
                "54c5c59d75de3e8d0a8b4573" : 12,
                "54c5c59d75de3e8d0a8b4574" : 12,
                "54c5c59d75de3e8d0a8b4575" : 12,
                "54c5c59d75de3e8d0a8b4576" : 12,
                "54c5c59d75de3e8d0a8b4577" : 12,
                "54c5c59d75de3e8d0a8b4578" : 12
            }

If you have any idea please help me.

Upvotes: 1

Views: 228

Answers (1)

Neil Lunn
Neil Lunn

Reputation: 151122

Several things that are very wrong with how you are approaching this. The most basic being this is not an array so you need to learn the difference.

  1. Data as key names. Those key values are clearly derived from ObjectId values and have no place being the names of keys in your object. It's a bad pattern. Key names cannot be indexed, only data can be indexed. Whoever taught you that was a viable design pattern, send them to me and I'll set them straight. Don't do that.

  2. You could present the data as an array, but you cannot really say "get the next four starting from x" as a value to match without really doing other query work first that requires a lot of overhead.

  3. Given both of the above you should probably just make these discrete documents in a collection. The usage is not clear, but it's probably the cleanest.


So here's what I mean by that:

Case 1


Not the best pattern and you maybe should be doing this since you cannot query the values of names:

"auto_availability" : [
    { "_id": "54c5c59d75de3e8d0a8b4567", "value": 12 },
    { "_id": "54c5c59d75de3e8d0a8b4568", "value": 12 },  // <-- Maybe start here
    { "_id": "54c5c59d75de3e8d0a8b4569", "value": 12 },
    { "_id": "54c5c59d75de3e8d0a8b456a", "value": 12 },
    { "_id": "54c5c59d75de3e8d0a8b456b", "value": 12 },
    { "_id": "54c5c59d75de3e8d0a8b456c", "value": 12 },
    { "_id": "54c5c59d75de3e8d0a8b456d", "value": 12 },
    { "_id": "54c5c59d75de3e8d0a8b456e", "value": 12 }
]

That's a lot better than the case you started with since there is something traversal can solve within array elements and mostly because MongoDB cannot "walk" document "keys" without using JavaScript, which results in very poor performance.

Case 2


Considering the above, you can always do this when using an array in a document. It is very far from optimal and not recommended.

db.collection.aggregate([
    // Match possible documents
    { "$match": { 
        "auto_availibility._id": { "$gte": "54c5c59d75de3e8d0a8b4568" }
    }},

    // Unwind the array
    { "$unwind": "$auto_availabilty" },

    // Filter the documents from the de-normalized array
    { "$match": { 
        "auto_availibility._id": { "$gte": "54c5c59d75de3e8d0a8b4568" }
    }},

    // Keep the current 4 items only
    { "$limit": 4 },

    // Maybe group back to the document in an array
    { "$group": {
        "_id": "$_id",
        "auto_availability": { "$push": "$auto_availability" }
    }}
])

Case 3


Better yet, in a seperate collection like so:

    { "_id": "54c5c59d75de3e8d0a8b4567", "value": 12 },
    { "_id": "54c5c59d75de3e8d0a8b4568", "value": 12 },  // <-- Maybe start here
    { "_id": "54c5c59d75de3e8d0a8b4569", "value": 12 },
    { "_id": "54c5c59d75de3e8d0a8b456a", "value": 12 },
    { "_id": "54c5c59d75de3e8d0a8b456b", "value": 12 },
    { "_id": "54c5c59d75de3e8d0a8b456c", "value": 12 },

Really simple query now:

db.collection.find({
    "_id": { "$gte": "54c5c59d75de3e8d0a8b4568" }
}).limit(4);

The main point is about how you structure this. A document using "keys" as where they should be "values" is a very bad pattern that should be avoided. Additionally the processing of an array does also not seem optimal even though it is better than the former, for which there is only JavaScript traversal available, and I will not give that bad example.

In the final event, this shows that even an array is not practical. The best course is using a plain collection. This makes the query selection simple and fast with standard operators.

Change how you structure and record the data for the most perform ant option.


Of course the end case of all of this is that you need to 'select' the items you want to update by the criteria you want, and then issue a separate "update" statement with the _id values from the present "documents" or possibly "array elements" as applicable.

Bit of a shame that something like $limit is not available as an "update" modifier. But there is actually a feature request in for that. So it might happen some-day.

Upvotes: 3

Related Questions