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
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.
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.
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
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.
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();