Update attribute value in nested array - mongodb

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.

Related

Update query on in the collection by "_id"

{
"_id" : "tenant/data/EMAIL/ENGLISH",
"tenantId" : "tenant2",
"channelType" : "EMAIL",
"template" : [
{
"_id" : "1",
"templateName" : "abc",
"effectiveStartDate" : ISODate("2017-01-01T12:00:00.000Z"),
"modifiedDate" : ISODate("2017-06-02T22:08:55.782Z"),
"active" : false
}
]
}
I need to update the "templateName" : "xyz" on the basis of "_id" : "tenant/data/EMAIL/ENGLISH"
I have tried these queries but got no success
db.getCollection('data').updateOne({"_id": "tenant/data/EMAIL/ENGLISH"},
{$set : { "template.$.templateName" : "XYZ"}}); 
db.getCollection('data').updateOne({"_id": "tenant/data/EMAIL/ENGLISH"},
{$set : { "template.templateName" : "XYZ"}}); 
Any help will be appreciated.
I have used positional-all operator to update the array.
Here is the query:
db.sample.update(
{
"_id": "tenant/data/EMAIL/ENGLISH"
},
{
$set:{
"template.$[].templateName":"XYZ"
}
}
)
Output
{
"_id" : "tenant/data/EMAIL/ENGLISH",
"tenantId" : "tenant2",
"channelType" : "EMAIL",
"template" : [
{
"_id" : "1",
"templateName" : "XYZ",
"effectiveStartDate" : ISODate("2017-01-01T12:00:00Z"),
"modifiedDate" : ISODate("2017-06-02T22:08:55.782Z"),
"active" : false
}
]
}
hope this will help :)

$concat string with $cond in mongodb aggregation

[
{
"user_id" : 12453,
"profile_type" : "demo_type_1",
"records" : [
{
"type" : "typ_11",
"value" : {
"high" : 115,
"low" : 78
},
"_meta" : {
"data_type" : "text"
}
},
{
"type" : "type_1",
"files" : [
{
"title" : "path_prescription_1",
"url" : "/file_name.extension"
},
{
"title" : "path_prescription_2",
"url" : "/file_name__1.extension"
}
],
"_meta" : {
"data_type" : "file"
}
}
]
},
{
"user_id" : 12455,
"profile_type" : "demo_type_1",
"records" : [
{
"type" : "typ_11",
"value" : {
"high" : 115,
"low" : 78
},
"_meta" : {
"data_type" : "text"
}
},
{
"type" : "type_1",
"files" : [
{
"title" : "path_prescription_1",
"url" : "/file_name.extension"
},
{
"title" : "path_prescription_2",
"url" : "/file_name__1.extension"
}
],
"_meta" : {
"data_type" : "file"
}
}
]
},
...
]
i want to append prefix the url value to make it an absolute path while retrieving from the database only when the _meta fields data_type is file not for text. The is stored in the above format and retrieved in the same format only with the appended url.
is there any way to do this by using aggregation pipeline ?
You can try using $addFields like this:
db.getCollection('test').aggregate(
[
{
$unwind: {
path:"$records",
preserveNullAndEmptyArrays: true
}
},
{
$unwind: {
path:"$records.files",
preserveNullAndEmptyArrays: true
}
},
{
$addFields: {
"records.files.url":{
$cond: {
if: {
$eq: ['$records.files', undefined]
},
then: null,
else: {$concat: ["some_prefix", "$records.files.url"]}
}
}
}
}
]
)
This will give you:
/* 1 */
{
"_id" : ObjectId("5b6c484b9a8ea6a11c508520"),
"user_id" : 12453.0,
"profile_type" : "demo_type_1",
"records" : {
"type" : "typ_11",
"value" : {
"high" : 115.0,
"low" : 78.0
},
"_meta" : {
"data_type" : "text"
},
"files" : {
"url" : null
}
}
}
/* 2 */
{
"_id" : ObjectId("5b6c484b9a8ea6a11c508520"),
"user_id" : 12453.0,
"profile_type" : "demo_type_1",
"records" : {
"type" : "type_1",
"files" : {
"title" : "path_prescription_1",
"url" : "some_prefix/file_name.extension"
},
"_meta" : {
"data_type" : "file"
}
}
}
/* 3 */
{
"_id" : ObjectId("5b6c484b9a8ea6a11c508520"),
"user_id" : 12453.0,
"profile_type" : "demo_type_1",
"records" : {
"type" : "type_1",
"files" : {
"title" : "path_prescription_2",
"url" : "some_prefix/file_name__1.extension"
},
"_meta" : {
"data_type" : "file"
}
}
}
/* 4 */
{
"_id" : ObjectId("5b6c484b9a8ea6a11c508521"),
"user_id" : 12455.0,
"profile_type" : "demo_type_1",
"records" : {
"type" : "typ_11",
"value" : {
"high" : 115.0,
"low" : 78.0
},
"_meta" : {
"data_type" : "text"
},
"files" : {
"url" : null
}
}
}
/* 5 */
{
"_id" : ObjectId("5b6c484b9a8ea6a11c508521"),
"user_id" : 12455.0,
"profile_type" : "demo_type_1",
"records" : {
"type" : "type_1",
"files" : {
"title" : "path_prescription_1",
"url" : "some_prefix/file_name.extension"
},
"_meta" : {
"data_type" : "file"
}
}
}
/* 6 */
{
"_id" : ObjectId("5b6c484b9a8ea6a11c508521"),
"user_id" : 12455.0,
"profile_type" : "demo_type_1",
"records" : {
"type" : "type_1",
"files" : {
"title" : "path_prescription_2",
"url" : "some_prefix/file_name__1.extension"
},
"_meta" : {
"data_type" : "file"
}
}
}
But remember to ignore records.files.url field when the record is type text.

MongoDB 3.2 Update Field name in embedded array

Im trying to do a i migration in my MongoDB. I have updated a Field from Content -> StringContent. Now I want to update all records that exists with the new Field name.
This is how a document looks like.
{
"_id" : "c4af0b19-4c78-4e58-bbe5-ac9e5cce2c3f",
"Type" : "Onboarding",
"Cards" : [
{
"_id" : LUUID("3f328a1c-658d-ee4e-8f06-561760eb5be5"),
"Width" : 1,
"Title" : "",
"Type" : "Freetext",
"Description" : "",
"Content" : "This is a test" // -> "StringContent" : "This is a Test"
},
{
"_id" : LUUID("2f328a1c-658d-ee4e-8f06-561760eb5be5"),
"Width" : 1,
"Title" : "",
"Type" : "Freetext",
"Description" : "",
"Content" : "This is another test" //-> "StringContent" : "This is another Test"
}
],
"DocumentType" : "Template",
"History" : [
{
"Date" : ISODate("2017-07-13T12:03:01.620Z"),
"ByUserId" : LUUID("4ecaa6ca-2ce6-f84c-81f3-28f8f0256e6e")
}
],
"Name" : "Default Template"
}
I have created this script:
var bulk = db.getCollection('OnboardingPortal').initializeOrderedBulkOp(),
count = 0;
db.getCollection('OnboardingPortal').find({"DocumentType": "Template"}).
forEach(function(doc) {
doc.Cards.forEach(function(card) {
if(card.hasOwnProperty("Content")){
print(card);
bulk.find({"_id": doc._id, "Cards._id": card._id}).update(
{
$set: {"Cards.$.StringContent": card.Content}
});
bulk.find({"_id": doc._id, "Cards._id": card._id}).update(
{
$unset: {"Cards.$.Content": 1}
});
count += 2;
if(count % 500 == 0) {
bulk.execute();
bulk = db.getCollection('OnboardingPortal').initializeOrderedBulkOp();
}
}
});
});
if ( count % 500 !== 0 ){
bulk.execute();
}
This Does not update anything, but if I change bulk.operations -> to explicit set index on array like this, it will do the job. but only for one card :
bulk.find({"_id": doc._id, "Cards.1._id": card._id}).update(
{
$set: {"Cards.1.StringContent": card.Content}
});
bulk.find({"_id": doc._id, "Cards.1._id": card._id}).update(
{
$unset: {"Cards.1.Content": 1}
});
What am i missing in my script so this can iterates over several documents and change Content-> StringContent in each Card. ?
EDIT
I have added a bulk.getOperations(); in my script. this is what it returns. Should it not have replaced $ with index ?
/* 1 */
[
{
"originalZeroIndex" : 0.0,
"batchType" : 2.0,
"operations" : [
{
"q" : {
"_id" : "c4af0b19-4c78-4e58-bbe5-ac9e5cce2c3f",
"Cards._id" : "1c8a323f-8d65-4eee-8f06-561760eb5be5"
},
"u" : {
"$set" : {
"Cards.$.StringContent" : "This is a cool test"
}
},
"multi" : false,
"upsert" : false
},
{
"q" : {
"_id" : "c4af0b19-4c78-4e58-bbe5-ac9e5cce2c3f",
"Cards._id" : "1c8a323f-8d65-4eee-8f06-561760eb5be5"
},
"u" : {
"$unset" : {
"Cards.$.Content" : 1.0
}
},
"multi" : false,
"upsert" : false
},
{
"q" : {
"_id" : "c4af0b19-4c78-4e58-bbe5-ac9e5cce2c3f",
"Cards._id" : "1c8a322f-8d65-4eee-8f06-561760eb5be5"
},
"u" : {
"$set" : {
"Cards.$.StringContent" : "This is a test"
}
},
"multi" : false,
"upsert" : false
},
{
"q" : {
"_id" : "c4af0b19-4c78-4e58-bbe5-ac9e5cce2c3f",
"Cards._id" : "1c8a322f-8d65-4eee-8f06-561760eb5be5"
},
"u" : {
"$unset" : {
"Cards.$.Content" : 1.0
}
},
"multi" : false,
"upsert" : false
}
]
}
]

Mongodb find subdocument all values

I want to find all the values from a subdocument like this:
{ "_id" : ObjectId("XXXXXXXXXXXX"), "consumers" : { "AAAAAAAA" : { "CLIENT" : { "AA" : true } } }, "country" : "ES", "history" : [ ], "last_time_updated" : ISODate("2014-11-28T13:32:19.948Z"), "msisdn" : "123", "operator" : "ES", "time_created" : ISODate("2014-11-28T13:32:19.948Z") }
{ "_id" : ObjectId("XXXXXXXXXXXX"), "consumers" : { "AAAAAAAA" : { "CLIENT" : { "BB" : true } } }, "country" : "ES", "history" : [ ], "last_time_updated" : ISODate("2014-11-28T13:32:19.971Z"), "msisdn" : "123", "operator" : "ES", "time_created" : ISODate("2014-11-28T13:32:19.971Z") }
{ "_id" : ObjectId("XXXXXXXXXXXX"), "consumers" : { "AAAAAAAA" : { "CLIENT" : { "CC" : false } } }, "country" : "ES", "history" : [ ], "last_time_updated" : ISODate("2014-11-28T13:32:19.977Z"), "msisdn" : "123", "operator" : "ES", "time_created" : ISODate("2014-11-28T13:32:19.977Z") }
That include all the values from "CLIENT" that i don't know, i am triying with:
db.collection.find({"consumers" : { "AAAAAAAA" : { "CLIENT" : { $exists : true } } }})
But is not a valid query, please some help?
Thank you very much.
Dot notation can be used to match by fields in a sub document.
db.collection.find({"consumers.AAAAAAAA.CLIENT": {"$exists":true}})

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