MongoDB Add new field to each element of array - mongodb

I have a mongo collection myCollection with the following structure :
{ "_id" : ObjectId("xxxxxxx"),
"name" : "name1",
"address" : "address1",
"list" : [
{ "field1" : true,
"field2" : false
},
{ "field1" : true,
"field2" : false
}]
},
...
,
{ "_id" : ObjectId("zzzzzzzzz"),
"name" : "name100",
"address" : "address100",
"list" : [
{ "field1" : true,
"field2" : false
},
{ "field1" : true,
"field2" : true
}]
}
For each document of the collection, I would like to create a new field (field3) with the default value false for each subelement of the array list.
Expected output :
{ "_id" : ObjectId("xxxxxxx"),
"name" : "name1",
"address" : "address1",
"list" : [
{ "field1" : true,
"field2" : false,
"field3" : false
},
{ "field1" : true,
"field2" : false,
"field3" : false
}]
},
...
,
{ "_id" : ObjectId("zzzzzzzzz"),
"name" : "name100",
"address" : "address100",
"list" : [
{ "field1" : true,
"field2" : false,
"field3" : false
},
{ "field1" : true,
"field2" : true,
"field3" : false
}]
}

You can use $addFields to add more fields
db.collection.aggregate([
{
$addFields: {
"list.fields3": false
}
}
])
Working Mongo Playground

Related

Mongo: Query to get count of objects in a nested objects in array

I need to retrieve this data if I have items in funds,
or to retrieve fields with items count.
query :
db.getCollection('contact')
.aggregate([ {$match: {guid: “456”}},
{$unwind: '$accounts'},
{
$lookup: {
from: 'accounts',
localField: 'accounts.id',
foreignField: 'id',
as: 'accounts_info'
}
},
{$project: {'accounts_info.subAccounts.items': 1, 'contacts.Id': 1, fullName : 1}}
]) .toArray();
result:
[
{
"_id" : “234”,
"accounts" : [
{
“subAccounts : {
"items" : [
{
"isExitDataReady" : false,
"Name" : “aaaa”,
"exitDate" : null,
"date" : "2019-08-14T00:00:00+03:00",
"CreatedDate" : "2020-03-05T10:03:40.000+0000",
"Id" : “00000000”,
},
{
"isExitDataReady" : false,
"Name" : “aaaa”,
"exitDate" : null,
"date" : "2019-08-14T00:00:00+03:00",
"CreatedDate" : "2020-03-05T10:03:40.000+0000",
"Id" : “00000000”,
},
{
"isExitDataReady" : false,
"Name" : “aaaa”,
"exitDate" : null,
"date" : "2019-08-14T00:00:00+03:00",
"CreatedDate" : "2020-03-05T10:03:40.000+0000",
"Id" : “00000000”,
},
{
"isExitDataReady" : false,
"Name" : “aaaa”,
"exitDate" : null,
"date" : "2019-08-14T00:00:00+03:00",
"CreatedDate" : "2020-03-05T10:03:40.000+0000",
"Id" : “00000000”,
},
{
"isExitDataReady" : false,
"Name" : “aaaa”,
"exitDate" : null,
"date" : "2019-08-14T00:00:00+03:00",
"CreatedDate" : "2020-03-05T10:03:40.000+0000",
"Id" : “00000000”,
}
]
}
}
]
},
{
"_id" : “567,
"subAccounts" : [
{
“funsd” : {
"items" : [
{
"isExitDataReady" : false,
"Name" : “aaaa”,
"exitDate" : null,
"date" : "2019-08-14T00:00:00+03:00",
"CreatedDate" : "2020-03-05T10:03:40.000+0000",
"Id" : “00000000”,
}
]
}
}
]
}
]
I tried:
To add on $match {'accounts_info.subAccounts.items': {$exist : true}}
But I did not get any data back at all.
To add on $match {'accounts_info.subAccounts.items.itemId': {$gt : 0}}
But I did not get any data back at all.

preserving fields collapsed by $group

I want to summarize a set of documents by counting on a field named code. How can I summarize my data and preserve details from the original documents?
The pipeline input contains the documents below.
{
"_id" : ObjectId("5ff38e0eb09dec2cbce14760"),
"code" : "U",
"date" : ISODate("2021-04-09T00:00:00.000+0000"),
"full_day" : false,
"remote" : false,
"student_id" : 9441
}
{
"_id" : ObjectId("5ff38e0eb09dec2cbce14807"),
"code" : "E",
"date" : ISODate("2020-11-02T00:00:00.000+0000"),
"full_day" : false,
"remote" : false,
"student_id" : 9441
}
{
"_id" : ObjectId("5ff39854b09dec2cbce1494c"),
"code" : "E",
"date" : ISODate("2020-11-03T08:00:00.000+0000"),
"full_day" : true,
"remote" : false,
"student_id" : 9441
}
The desired output groups by code, promotes student_id to the root level, and nests the other details in a details array:
{
"code" : "U",
"student_id": 9441,
"count" : 1.0,
"details" : [
{
"date" : ISODate("2021-04-09T00:00:00.000+0000"),
"full_day" : false,
"remote" : false,
}
]
}
{
"code" : "E",
"student_id": 9441,
"count" : 2.0,
"details" : [
{
"date" : ISODate("2020-11-02T00:00:00.000+0000"),
"full_day" : false,
"remote" : false,
},
{
"date" : ISODate("2020-11-03T08:00:00.000+0000"),
"full_day" : true,
"remote" : false,
}
]
}
Combining $group and $push I've only been able to produce:
{
"_id" : "U",
"count" : 1.0,
"details" : [
{
"date" : ISODate("2021-04-09T00:00:00.000+0000"),
"full_day" : false,
"remote" : false,
"student_id" : 9441
}
]
}
{
"_id" : "E",
"count" : 2.0,
"details" : [
{
"date" : ISODate("2020-11-02T00:00:00.000+0000"),
"full_day" : false,
"remote" : false,
"student_id" : 9441
},
{
"date" : ISODate("2020-11-03T08:00:00.000+0000"),
"full_day" : true,
"remote" : false,
"student_id" : 9441.0
}
]
}
The results above were achieved with this pipeline:
[
{
"$match" : {
"student_id" : 9441.0
}
},
{
"$group" : {
"_id" : "$code",
"count" : {
"$sum" : 1.0
},
"details" : {
"$push" : {
"date" : "$date",
"full_day" : "$full_day",
"remote" : "$remote",
"student_id" : "$student_id"
}
}
}
},
{
"$addFields" : {
"student_id" : "$student_id"
}
}
]
If you expect all of the input documents to have the same value for a field, and want that field to be included in the $group output, use the $first accumulation operator:
{
"$group" : {
"_id" : "$code",
"student_id" : {$first: "$student_id"},
"count" : {
"$sum" : 1.0
},
"details" : {
"$push" : {
"date" : "$date",
"full_day" : "$full_day",
"remote" : "$remote"
}
}
}
}
If you need to rename _id back to code, use a $project stage after the group.

Remove array element from document matching a field in array element

I have a document looking like this:
{
"_id" : ObjectId("5f60ffc5aefd067a9ff9345c"),
"_class" : "com.kalsym.smart.sms.data.MSASMS",
"number1" : NumberLong(923211105469),
"numbers2" : [
{
"field1" : "20200915532E888",
"number2" : NumberLong(923018565627),
"field2" : "abcd",
"datefield" : ISODate("2020-10-10T17:54:09.886Z")
},
{
"field1" : "2020092570A6948",
"number2" : NumberLong(923018565627),
"field2" : "efgh",
"datefield" : ISODate("2020-10-06T15:23:04.891Z")
},
{
"field1" : "2020092570A6948",
"number2" : NumberLong(923018565627),
"field2" : "ijkl",
"datefield" : ISODate("2020-10-03T15:23:04.891Z")
}
],
"optInCount" : 0
}
I want to delete array indices containing datefield value greater than 2020-10-04
After executing the command document must be updated like this:
{
"_id" : ObjectId("5f60ffc5aefd067a9ff9345c"),
"_class" : "com.kalsym.smart.sms.data.MSASMS",
"number1" : NumberLong(923211105469),
"numbers2" : [
{
"field1" : "2020092570A6948",
"number2" : NumberLong(923018565627),
"field2" : "ijkl",
"datefield" : ISODate("2020-10-03T15:23:04.891Z")
}
],
"optInCount" : 0
}
You can use updateMany() or update(), and $pull to remove matching record form array,
db.collection.updateMany({},
{
$pull: {
numbers2: { datefield: { $gt: ISODate("2020-10-04T00:00:00.000Z") } }
}
})
Playground

Update attribute value in nested array

My collection named user have 6 fields. Profile field is document. It have only array named Packages. Packages is collection of documents, which contains all packages current user have.
My database is like:
{
"_id" : "AayujR3SLT5MtmTKf",
"createdAt" : ISODate("2015-09-18T07:19:05.069Z"),
"services" : {
"password" : {
"bcrypt" : "encripted_password_here"
}
},
"username" : "test_user",
"emails" : [{
"address" : "1#gmail.com",
"verified" : false
}],
"profile" : {
"packages" : [{
"packageId" : "67fmCMNTdqejFs7NE",
"name" : "package1"
"active" : true
}, {
"packageId" : "Dcn4PkmHNe8APuk73",
"name" : "package2"
"active" : true
}, {
"packageId" : "yvdXkPeNHEWwwLKjC",
"name" : "package2"
"active" : true
}]
}
}
I want set all active to false. What should I do? My current code is like (not working):
Meteor.users.update({ _id: Session.get('user_id') }, { $set: {'profile.packages.active': false} });
It cannot be done in a single query as there are many elements in the "packages" array. Even if you try the below query, only the first match will get updated in the current document and rest will remain same.
db.exp10.update({"profile.packages.active":true},{$set:{"profile.packages.$.active":false}},{multi:true})
Output :
{
"_id" : "AayujR3SLT5MtmTKf",
"createdAt" : ISODate("2015-09-18T07:19:05.069Z"),
"services" : {
"password" : {
"bcrypt" : "encripted_password_here"
}
},
"username" : "test_user",
"emails" : [
{
"address" : "1#gmail.com",
"verified" : false
}
],
"profile" : {
"packages" : [
{
"packageId" : "67fmCMNTdqejFs7NE",
"name" : "package1",
"active" : false
},
{
"packageId" : "Dcn4PkmHNe8APuk73",
"name" : "package2",
"active" : true
},
{
"packageId" : "yvdXkPeNHEWwwLKjC",
"name" : "package2",
"active" : true
}
]
}
}
So better we can do it using the below piece of code :
db.user.find({"profile.packages.active":true}).forEach(function(doc){
for( var count = 0; count < doc.profile.packages.length; count++ )
{
if( doc.profile.packages[count].active == true )
doc.profile.packages[count].active = false;
}
db.user.save(doc);
});
Output :
{
"_id" : "AayujR3SLT5MtmTKf",
"createdAt" : ISODate("2015-09-18T07:19:05.069Z"),
"services" : {
"password" : {
"bcrypt" : "encripted_password_here"
}
},
"username" : "test_user",
"emails" : [
{
"address" : "1#gmail.com",
"verified" : false
}
],
"profile" : {
"packages" : [
{
"packageId" : "67fmCMNTdqejFs7NE",
"name" : "package1",
"active" : false
},
{
"packageId" : "Dcn4PkmHNe8APuk73",
"name" : "package2",
"active" : false
},
{
"packageId" : "yvdXkPeNHEWwwLKjC",
"name" : "package2",
"active" : false
}
]
}
}
P.S :
If the collection has many documents, For better performance we can use Bulk Operations.

MongoDB find documents if a property array doesn't contain an object

I have a list of documents like this.
[
{
"name" : "test",
"data" : [
{ "code" : "name", "value" : "Diego" },
{ "code" : "nick", "value" : "Darko" },
{ "code" : "special", "value" : true }
]
},
{
"name" : "another",
"data" : [
{ "code" : "name", "value" : "Antonio" },
{ "code" : "nick", "value" : "Tony" }
]
}
]
now I need to find all the documents that:
a) don't contain a "data" item with code "special"
OR
b) contains a "data" item with code "special" and value false
It's like I needed the opposite of $elemMatch or I'm missing something?
I'm assuming that you're inserting each document in your list of documents as a separate member of a collection test.
For a,
db.test.find({ "data.code" : { "$ne" : "special" } })
For b.,
db.test.find({ "data" : { "$elemMatch" : { "code" : "special", "value" : false } } })
Combining the two with $or,
db.test.find({ "$or" : [
{ "data.code" : { "$ne" : "special" } },
{ "data" : { "$elemMatch" : { "code" : "special", "value" : false } } }
] })
Hope this $nin will solve your issues.
I insertd your docs into "so" collection
db.so.find({}).pretty();
{
"_id" : ObjectId("5489cd4f4cb16307b808d4b2"),
"name" : "test",
"data" : [
{ "code" : "name",
"value" : "Diego"
},
{ "code" : "nick",
"value" : "Darko"
},
{ "code" : "special",
"value" : true
}
]
}
{
"_id" : ObjectId("5489cd674cb16307b808d4b3"),
"name" : "another",
"data" : [
{"code" : "name",
"value" : "Antonio"
},
{ "code" : "nick",
"value" : "Tony"
}
]
}
don't contain a "data" item with code "special"
> db.so.find({"data.code":{$nin:["special"]}}).pretty();
{
"_id" : ObjectId("5489cd674cb16307b808d4b3"),
"name" : "another",
"data" : [
{ "code" : "name",
"value" : "Antonio"
},
{ "code" : "nick",
"value" : "Tony"
}
]
}
contains a "data" item with code "special" and value false
db.so.find({$and:[{"data.code":"special"},{"data.value":false}]}).pretty();