mongoDB Array length on children element [duplicate] - arrays

This question already has answers here:
MongoDB: count the number of items in an array
(3 answers)
Closed 5 years ago.
I have a data similar to the below in my mongo DB. Obviously the length of array1 is two and I am looking on a way to count the length of such array with mongo db script. Thanks
{
"_id" : ObjectId("65465465465465465468"),
"header" : {
"test" : "test",
},
"value" : 11.763548134011048,
"array1" : [
{
"q" : {
"value" : 250
}
},
{
"q" : {
"value" : 30,
}
}
],
"array2" : [ ]
}

You can use the $size aggregation operator.
Given the document you showed in your OP, the following command ...
db.collection.aggregate(
[
{
$project: {
sizeOfArray1: { $size: "$array1" }
}
}
]
)
... will return:
{
"_id" : ObjectId("65465465465465465468"),
"sizeOfArray1" : 2
}
This approach is valid for all versions of Mongo >= 2.6.
Update: in response to this comment ...
If I have more nested objects until I reach to my array I can use they same code and I just change the name of the field to the item that I am interested in?
You address any document attribute using path notation with a . to denote each 'level'. So, if your document looked like this:
{
"_id" : ObjectId("65465465465465465468"),
"header" : {
"test" : "test",
"array" : [1, 2, 3]
},
"value" : 11.763548134011048,
"array1" : [
{
"q" : {
"value" : 250
}
},
{
"q" : {
"value" : 30,
}
}
],
"array2" : [ ]
}
... then the following command would return the count of the array field inside the header nested sub document:
db.collection.aggregate(
[
{
$project: {
sizeOfHeaderArray: { $size: "$header.array" }
}
}
]
)
So, perhaps you can now see that you would address the array field inside the header nested sub document using header.array.
Hope that helps.

Related

Get documents from an array in MongoDB [duplicate]

This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 5 years ago.
The community reviewed whether to reopen this question 4 months ago and left it closed:
Original close reason(s) were not resolved
I have array in subdocument like this
{
"_id" : ObjectId("512e28984815cbfcb21646a7"),
"list" : [
{
"a" : 1
},
{
"a" : 2
},
{
"a" : 3
},
{
"a" : 4
},
{
"a" : 5
}
]
}
Can I filter subdocument for a > 3
My expect result below
{
"_id" : ObjectId("512e28984815cbfcb21646a7"),
"list" : [
{
"a" : 4
},
{
"a" : 5
}
]
}
I try to use $elemMatch but returns the first matching element in the array
My query:
db.test.find( { _id" : ObjectId("512e28984815cbfcb21646a7") }, {
list: {
$elemMatch:
{ a: { $gt:3 }
}
}
} )
The result return one element in array
{ "_id" : ObjectId("512e28984815cbfcb21646a7"), "list" : [ { "a" : 4 } ] }
and I try to use aggregate with $match but not work
db.test.aggregate({$match:{_id:ObjectId("512e28984815cbfcb21646a7"), 'list.a':{$gte:5} }})
It's return all element in array
{
"_id" : ObjectId("512e28984815cbfcb21646a7"),
"list" : [
{
"a" : 1
},
{
"a" : 2
},
{
"a" : 3
},
{
"a" : 4
},
{
"a" : 5
}
]
}
Can I filter element in array to get result as expect result?
Using aggregate is the right approach, but you need to $unwind the list array before applying the $match so that you can filter individual elements and then use $group to put it back together:
db.test.aggregate([
{ $match: {_id: ObjectId("512e28984815cbfcb21646a7")}},
{ $unwind: '$list'},
{ $match: {'list.a': {$gt: 3}}},
{ $group: {_id: '$_id', list: {$push: '$list.a'}}}
])
outputs:
{
"result": [
{
"_id": ObjectId("512e28984815cbfcb21646a7"),
"list": [
4,
5
]
}
],
"ok": 1
}
MongoDB 3.2 Update
Starting with the 3.2 release, you can use the new $filter aggregation operator to do this more efficiently by only including the list elements you want during a $project:
db.test.aggregate([
{ $match: {_id: ObjectId("512e28984815cbfcb21646a7")}},
{ $project: {
list: {$filter: {
input: '$list',
as: 'item',
cond: {$gt: ['$$item.a', 3]}
}}
}}
])
$and:
get data between 0-5:
cond: {
$and: [
{ $gt: [ "$$item.a", 0 ] },
{ $lt: [ "$$item.a", 5) ] }
]}
Above solution works best if multiple matching sub documents are required.
$elemMatch also comes in very use if single matching sub document is required as output
db.test.find({list: {$elemMatch: {a: 1}}}, {'list.$': 1})
Result:
{
"_id": ObjectId("..."),
"list": [{a: 1}]
}
Use $filter aggregation
Selects a subset of the array to return based on the specified
condition. Returns an array with only those elements that match the
condition. The returned elements are in the original order.
db.test.aggregate([
{$match: {"list.a": {$gt:3}}}, // <-- match only the document which have a matching element
{$project: {
list: {$filter: {
input: "$list",
as: "list",
cond: {$gt: ["$$list.a", 3]} //<-- filter sub-array based on condition
}}
}}
]);

MongoDB not in any array (nested arrays)

We have scheme like this
{
"_id" : ObjectId("5e2ebceb2fb28c43520ef313"),
"attempts" : [
{
"ideas" : [
{
"ideaId" : ObjectId("5d314e8ade83a139bf31352a"),
"ratings" : [
{
"userId" : "20-432-3",
"points" : 3.0,
"isBest" : true
}
]
},
{
"ideaId" : ObjectId("5d314e8ade83a139bf31352c"),
"ratings" : [
{
"userId" : "20-432-2",
"points" : 3.0,
"isBest" : true
}
]
},
{
"ideaId" : ObjectId("5d314e8ade83a139bf31352e")
}
]
}
]
I need to to update document only if it does not have given userId in attempts.ideas.ratings. For example when user with userId with "20-432-3" want to update document, it will fail (because there is already rating from him in first idea), on the other hand user with userId "20-432-1" can update document.
it's like 3 nested scheme, my question is: is it possible in one query???
thank you in advance
EDIT.
Found solution. Obviously it was the easiest way...
_id: ObjectId("5e2ebceb2fb28c43520ef313"),
"attempts.ideas.ratings.uuIdentity": { $ne: "20-432-1" }

How to apply the "arrayFilters" on the top-level array?

My question is similar to Remove Object from Nested Array by Multiple Criteria. The major difference is that I want to apply the arrayFilter on the top-level array first and then apply another arrayFilter on the nested array of the matched top-level array element.
First of all, here is the document sample I copied over from the post with some adjustments to suit my case...
{
"OtherField1" : ISODate("2016-05-06T05:09:14.589Z"),
"OtherField2" : "something",
"Distributions" : [
{
"DeliveryType" : 1,
"DeliverySubType" : 2,
"DistributionData" : [
{
"Key" : "Topic",
"Value" : "Topics",
"Children" : null
},
{
"Key" : "Message",
"Value" : "test",
"Children" : null
}
]
},
{
"DeliveryType" : 1,
"DeliverySubType" : 3,
"DistributionData" : [
{
"Key" : "Topic",
"Value" : "Topics",
"Children" : ""
},
{
"Key" : "Message",
"Value" : "test",
"Children" : null
}
]
}
]
}
What I want to achieve is to, first of all, locate the 2nd element of the top-level Distributions array by matching "DeliveryType" : 1 and "DeliverySubType" : 3, and then locate the 1st element of its nested DistributionData array by matching "Key": "Topic" and "Value" : "Topics", and finally update the Children field of matched nested array element.
For the top-level match, I tried the following forms ...
{
"arrayFilters": [
{ "filter1" : {
"elemMatch" : {
"DeliveryType" : 1,
"DeliverySubType" : 3,
}
}
}
}
and referenced the filter as Distributions.$[filter1]..., but it didn't work.
Or,
{
"arrayFilters": [
{ "filter1.Distributions" : {
"elemMatch" : {
"DeliveryType" : 1,
"DeliverySubType" : 3,
}
}
}
}
and referenced the filter as $[filter1].Distributions...., but once again it failed with a parsing error.
All the examples I can found are about using the arrayFilters on the nested arrays and myself had some success in that regard, but no matter how hard I tried to apply the same tricks to the filters on the top-level array, they just failed with all sorts of errors. Could anyone help please? Thanks.
The solution is somewhat hidden but it's actually documented:
db.collection.update({}, {
$set: { "Distributions.$[distFilter].DistributionData.$[distDataFilter].Children": "new value" }
}, {
"arrayFilters": [
{
"distFilter.DeliveryType" : 1,
"distFilter.DeliverySubType" : 3
}, {
"distDataFilter.Key" : "Topic",
"distDataFilter.Value" : "Topics"
}
]
})

Query collection containing BsonArray

Sorry, first time trying mongo.
Given the following data ...
db.masterList.findOne()
{
"_id" : ObjectId("59d128805b19310ac8ab3fc2"),
"MasterDefinition" : {
"Location" : [
"Whole House",
"Master Bedroom",
"Hallway 2"
],
"DeviceType" : [
"Receptacle",
"GFI",
"LED dimmer"
],
"Style" : [
"Decora",
"Standard"
],
"Color" : [
"White",
"Light Almond"
]
}
}
How do I retrieve the contents of the Color array? I expect something like
["White","Light Almond"]
How do I list the 4 arrays directly subordinate to MasterDefintion? I expect to see
["Location","DeviceType","Style","Color"]
Thanks
For the first part, you can simply do a
collection.aggregate({
$project: {
"_id": 0, // exclude the "_id" field from the result
"result": "$MasterDefinition.Color"
}
})
The second part requires a little magic (documentation can be found here: aggregation framework, $project, $objectToArray):
collection.aggregate({
$project: {
"temp": {
$objectToArray: "$MasterDefinition" // transform the "MasterDefinition" subdocument into an array
}
}
}, {
$project:{
"_id": 0, // do not include the "_id" field in the result - this is an optional step
"result": "$temp.k" // only get the keys (as in "k" fiels) from the array
}
})
How do I retrieve the contents of the Color array? I expect something like ["White","Light Almond"]
// the first argument here is a filter, the second argument is a projection
// since you specified no filter I have only included a projection
// this projection tells MongoDB to return the Color subdocument
// from within the MasterDefinition sub document
db.getCollection('masterList').find({}, {'MasterDefinition.Color': 1})
The above command will return:
{
"_id" : ObjectId("59d128805b19310ac8ab3fc2"),
"MasterDefinition" : {
"Color" : [
"White",
"Light Almond"
]
}
}
How do I list the 4 arrays directly subordinate to MasterDefintion? I expect to see ["Location","DeviceType","Style","Color"]
This is a bit trickier because "Location","DeviceType","Style","Color" are not elements in an array instead they are the names of attributes in the MasterDefinition subdocument. You can use the $objectToArray aggregation operator to turn these attribute names into an array but the resulting document doesn't looks exactly like what you hoped for. Here's an example ...
db.getCollection('masterList').aggregate([
// creates an array named "categories" from the attributes of the MasterDefinition sub document
{ $project: { categories: { $objectToArray: "$MasterDefinition" } } },
// projects on the keys of the "categories" array
{$project: {'categories.k': 1}}
])
... which produces this output:
{
"_id" : ObjectId("59d128805b19310ac8ab3fc2"),
"categories" : [
{
"k" : "Location"
},
{
"k" : "DeviceType"
},
{
"k" : "Style"
},
{
"k" : "Color"
}
]
}

update nested array element value in node js mongoDB [duplicate]

This question already has answers here:
How to Update Multiple Array Elements in mongodb
(16 answers)
Closed 6 years ago.
Hi i am new in nodejs i need to update a value in nested array using _id of document my database document is look like this..
"complaints" : [
{
"complaint" : "head light is not working",
"complaintid" : ObjectId("57205219a56d2b8c0f9274a4"),
"_id" : ObjectId("57454c9249218eb40c1c0d1f"),
"labour" : 350,
"partCost" : 0,
"part" : [
{
"id" : ObjectId("56f12eaab915bd9800272ed7"),
"estimate" : 450,
"partname" : "clutch",
"_id" : ObjectId("57454cef49218eb40c1c0d25"),
"quantity" : 0,
"qrcodes" : []
},
{
"id" : ObjectId("56f12eaab915bd9800272ed7"),
"estimate" : 450,
"partname" : "rear tyre",
"_id" : ObjectId("57454cef49218eb40c1c0d24"),
"quantity" : 0,
"qrcodes" : []
}
],
"acceptance" : true,
"inspection" : false,
"color" : "#8929A9",
"status" : "APPROVED",
"estimate" : 1200,
"solution" : "HEAD LIGHT CHANGE",
"problems" : "HEAD LIGHT IS NOT WORKING"
},
i need to update quantity value of part array exist inside the part array using _id of part array
i am trying this but its not working what should i do for update this value...
var partdata = req.payload.parts;
for(var k = 0; k< partdata.length ; k++ ){
CPS.update({
'complaints.part._id' : partdata[k].partid
}, {
"$inc" : {
'complaints.part.$.quantity' : partdata[k].quantity
}
}).exec
(function(err,temp) {
if(err){
res(err).code(500);
}else{
console.log(temp);
}
});
}
MongoDB doesn't support matching into more than one level of an array.
Consider altering your document model so each document represents an
operation, with information common to a set of operations duplicated
in the operation documents.
Following is not the solution for your case.
But in-case you know the index then you could do something like this:
Assume a sample document like:
{
"_id" : ObjectId("57454c9249218eb40c1c0d1f"),
"part" : [{ "quantity" : 111 }, { "quantity" : 222 }]
}
Then this query should work.
db.test.update({ "_id" : ObjectId("57454c9249218eb40c1c0d1f") }, { "$set" : { "part.1.quantity" : 999 } })
Document will get modified as follows :
{
"_id" : ObjectId("57454c9249218eb40c1c0d1f"),
"array" : [{ "quantity" : 222 }, { "quantity" : 999 }]
}
Update: You can try following way of doing the update. But its not recommended way of doing probably you need to restructure your schema.
db.test.aggregate([
{ "$unwind": "$complaints" },
{ "$unwind": "$complaints.part" },
{ "$project":
{
_id: "$complaints.part._id",
partqty: "$complaints.part.quantity"
}
},
]);
This should return as follows:
{
"_id" : ObjectId("57454cef49218eb40c1c0d25"),
"partqty" : 111
}
{
"_id" : ObjectId("57454cef49218eb40c1c0d24"),
"partqty" : 222
}
Now you can use this information to update, e.g
var cur = db.test.aggregate([
{ "$unwind": "$complaints" },
{ "$unwind": "$complaints.part" },
{ "$project":
{
_id: "$complaints.part._id",
partqty: "$complaints.part.quantity"
}
},
]);
while (cur.hasNext()) {
var doc = cur.next();
//Note the index should be know again :|
db.test.update({ "complaints.part._id": ObjectId("57454cef49218eb40c1c0d25") },
{ "$set": { "complaints.$.part.1.quantity": 55 }},
{ "multi": true})
}

Resources