MongoDB $elemMatch of $elemMatch and good practice - mongodb

H,
I'm trying to update the version field in this object but I'm not able to make a query with 2 nested $match. So what I would like to do is get the record with file id 12 and version 1.
I would ask also if is it a good practice have more the one nested array in mongoDB (like this object)...
Query:
db.collection.find({"my_uuid":"434343"},{"item":{$elemMatch:{"file_id":12,"changes":{$elemMatch:{"version":1}}}}}).pretty()
Object:
{
"my_uuid": "434343",
"item": [
{
"file_id": 12,
"no_of_versions" : 1,
"changes": [
{
"version": 1,
"commentIds": [
4,
5,
7
]
},
{
"version": 2,
"commentIds": [
10,
11,
15
]
}
]
},
{
"file_id": 234,
"unseen_comments": 3,
"no_of_versions" : 2,
"changes": [
{
"version": 1,
"commentIds": [
100,
110,
150
]
}
]
}
]
}
Thank you

If you want the entire documents that satisfy the criteria returned in the result, then I think it's fine. But if you want to limit the array contents of item and changes to just the matching elements, then it could be a problem. That's because, you'll have to use the $ positional operator in the projection to limit the contents of the array and only one such operator can appear in the projection. So, you'll not be able to limit the contents of multiple arrays within the document.

Related

Querying MongoDB collection consisting of one document which in turn is a multi-level nested object with objects/arrays nested inside

DB collection seatsObj:
{
"product_id": 46539040,
"freeSeating": false,
"tempTransId": "1ecae165f2d86315fea19963d0ded41a",
"seatLayout": {
"colAreas": {
"Count": 2,
"intMaxSeatId": 43,
"intMinSeatId": 2,
"objArea": [
{
"AreaDesc": "EXECUTIVE",
"AreaCode": "0000000003",
"AreaNum": "1",
"HasCurrentOrder": true,
"objRow": [
{
"GridRowId": 1,
"PhyRowId": "A",
"objSeat": [
{
"GridSeatNum": 1,
"SeatStatus": "1",
"seatNumber": 1,
"seatPrice": 400,
"ID": 111
},
{
"GridSeatNum": 2,
"SeatStatus": "0",
"seatNumber": 2,
"seatPrice": 450,
"ID": 112
},
I was able to find ways to locate and update specific fields using:
seatsObj.updateOne(
{"seatLayout.colAreas.objArea.0.objRow.0.objSeat.seatPrice": 470},
{$set: {"seatLayout.colAreas.objArea.0.objRow.0.objSeat.$.ID": 888}});
but i cannot find simple way to return a specific field value from objSeat array element based on search criteria (for example: get 400 as a result of querying seatPrice for the seat with ID = 111). Could anyone give me a direction? From my initial research I have to go into crazy nested $unwind -s and $objectToArray -s, etc... Isn't there a simpler way? Thank you!!

MongoDB updating wrong subdocument in an array

my gamefamilies collection looks like this
{
"_id": ObjectId('54cc3ee7894ae60c1c9d6c74'),
"game_ref_id": "REF123",
..
"yearwise_details": [
{
"year": 1,
...
"other_details": [
{
"type": "cash",
"openingstock": 988
..
},
{
"type": "FLU",
"openingstock": 555
..
},
..other items
]
},
{
"year": 2,
...
"other_details": [
{
"type": "cash",
"openingstock": 3000,
....
},
...
{
"type": "ghee",
"openingstock": 3000,
...
},
..
]
}
]
}
My update query
db.gamefamilies.update({"game_ref_id": "REF123", "teamname": "manisha","yearwise_details.year": 2, "yearwise_details.other_details.type": "ghee"}, {"$set": {"yearwise_details.0.other_details.$.openingstock": 555} });
Document is getting picked up correctly. I expect to update year 2's item type="ghee" but instead year 1's 2nd item (type FLU) gets updated. What am I doing wrong ?
Any help would be greatly appreciated.
regards
Manisha
Unfortunately, there is not yet support for nested $ positional operator updates.
So you can hardcode the update with
db.gamefamilies.update({"game_ref_id": "REF123",
"teamname": "manisha",
"yearwise_details.year": 2,
"yearwise_details.other_details.type": "ghee"},
{"$set":
{"yearwise_details.1.other_details.$.openingstock": 555}});
But notice that the yearwise_details.1.other_details is hardcoding that you want the second value of the array (it is 0-indexed, so the 1 is referencing the second element). I am assuming you found the command you have in your question because it worked for the first element of the array. But it will only ever work on the first element and the command above will only ever work on the second element.

Conditional $inc in a nested MongoDB array

My database looks like this:
{
_id: 1,
values: [ 1, 2, 3, 4, 5 ]
},
{
_id: 2,
values: [ 2, 4, 6, 8, 10 ]
}, ...
I'd like to update every value in every document's nested array ("values") that meets some criterion. For instance, I'd like to increment every value that's >= 4 by one, which ought to yield:
{
_id: 1,
values: [ 1, 2, 3, 5, 6 ]
},
{
_id: 2,
values: [ 2, 5, 7, 8, 11 ]
}, ...
I'm used to working with SQL, where the nested array would be a seperated table connected with a unique ID. I'm a little lost in this new NoSQL world.
Thank you kindly,
This sort of update is not really possible using nested arrays, the reason for this is given in the positional $ operator documentation, and that states that you can only match the first array element for a given condition in the query.
So a statement like this:
db.collection.update(
{ "values": { "$gte": 4 } },
{ "$inc": { "values.$": 1 } }
)
Will not work in the sense that only the "first" array element that was matched would be incremented. So on your first document you would get this:
{ "_id" : 1, "values" : [ 1, 2, 3, 6, 6 ] }
In order to update the values as you are suggesting you would need to iterate the documents and the array elements to produce the result:
db.collecction.find({ "values": { "$gte": 4 } }).forEach(function(doc) {
for ( var i=0; i < doc.values.length; i++ ) {
if ( doc.values[i] >= 4 ) {
doc.values[i]++;
}
}
db.collection.update(
{ "_id": doc._id },
{ "$set": { "values": doc.values } }
);
})
Or whatever code equivalent of that basic concept.
Generally speaking, this sort of update does not lend itself well to a structure that contains elements in an array. If that is really your need, then the elements are better off listed within a separate collection.
Then again, the presentation of this question is more of a "hypothetical" situation without understanding your actual use case for performing this sort of udpate. So if you possibly described what you actually need to do and how your data really looks in another question, then that might get a more meaningful response in terms of the best approach for you to use.

MongoDB: Embedded docs that do NOT match query

I am working on a Mongo database, where I need users to be able to specify dates when they won't be available. I started with this structure:
{
"_id": "demo-spe",
"SC": [ { "SS": 14, "SA": [ 2, 3, 5 ] } ],
"SU": [
{ "IY": 2013, "IM": 12, "ID": 30, "H0": 0, "N0": 0, "H1": 23, "N1": 59 },
{ "IY": 2013, "IM": 12, "ID": 31, "H0": 0, "N0": 0, "H1": 23, "N1": 59 }
]
}
Using this structure, if I want to know which users will be available on a certain date, that is, the "SU" array should NOT have a document that matches IY:(year), IM:(month) and ID:(day). I am really lost with $and and $nin, could please someone guide me?
Thanks, and sorry for the noob question and the non-descriptive fields! :)
You can use a combination of $elemMatch and $not to do this. $elemMatch to match against multiple fields in the same array element, and $not to perform a logical NOT on the expression:
db.test.find({SU: {$not: {$elemMatch: {IY:2013, IM:12, ID:29}}}})

Slice an array in MongoDB's aggregation framework

I am saving game results in MongoDB and would like to calculate the sum of the 3 best results for every player.
With the aggregation framework I am able to built the following intermediate pipeline result from my database of finished games (each player below has finished 5 games with the gives score):
{
"_id" : "Player1",
"points" : [ 324, 300, 287, 287, 227]
},
{
"_id" : "Player2",
"points" : [ 324, 324, 300, 287, 123]
}
Now I need to sum up the three best values for each player. I was able to sort the array so it would also be ok here to get only the first 3 elements of each array to build the sum of the array in the next pipeline step.
$limit would work fine if I only need the result for one player. I also tried using $slice but that doesn't seem to work in the aggregation framework.
So how do I get the sum of the three best results for each player?
You mentioned that it would also be ok here to get only the first 3 elements of each array to build the sum of the array in the next pipeline step., so do it first, then use:
db.test.aggregate({'$unwind':'$points'},{'$group':{'_id':'$_id','result':{'$sum':'$points'}}}
to get the result.
$slice method for aggregation framework was added in 3.2 version of mongo. For a more detailed answer, take a look here.
And a couple of examples from the mongo page:
{ $slice: [ [ 1, 2, 3 ], 1, 1 ] } // [ 2 ]
{ $slice: [ [ 1, 2, 3 ], -2 ] } // [ 2, 3 ]
{ $slice: [ [ 1, 2, 3 ], 15, 2 ] } // [ ]
{ $slice: [ [ 1, 2, 3 ], -15, 2 ] } // [ 1, 2 ]