MongoDB 3.2 Update Field name in embedded array - mongodb

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
}
]
}
]

Related

Return specific array value field in aggregate

I have a issue in MongoDB i'm trying to build a very complex aggregate query, and its work almost as i want it, but i still have trobles, and the problems is i need to move a spefiect field so i can use it later.
My aggregate look like this right now.
db.getCollection('travel_sights').aggregate([{
'$match': {
'preview.photo' : {
'$exists':true
},
'_id': {
'$in' : [ObjectId("5b7af9701fbad410e10f32f7")]
}
}
},{
'$unwind' : '$preview.photo'
}, {
'$lookup':{
'from' : 'media_data',
'localField' : '_id',
'foreignField':'bind',
'as':'media'
}
}])
and it will return data like this.
{
"_id" : ObjectId("5b7af9701fbad410e10f32f7"),
"preview" : {
"photo" : {
"id" : ObjectId("5b7affea1fbad441494a663b"),
"sort" : 0
}
},
"media" : [
{
"_id" : ObjectId("5b7affea1fbad441494a663b")
},
{
"_id" : ObjectId("5b7b002d1fbad441494a663c")
},
{
"_id" : ObjectId("5b7b00351fbad441494a663d")
},
{
"_id" : ObjectId("5b7d9baa1fbad410de638bbb")
},
{
"_id" : ObjectId("5b7d9bae1fbad410e10f32f9")
},
{
"_id" : ObjectId("5b7d9bb11fbad441494a663e")
},
{
"_id" : ObjectId("5b7d9bb41fbad4ff97273402")
},
{
"_id" : ObjectId("5b7d9bb71fbad4ff99527e82")
},
{
"_id" : ObjectId("5b7d9bbb1fbad410de638bbc")
},
{
"_id" : ObjectId("5b7d9bbe1fbad410e10f32fa")
},
{
"_id" : ObjectId("5b7d9bc11fbad441494a663f")
},
{
"_id" : ObjectId("5b7d9bc41fbad4ff97273403")
},
{
"_id" : ObjectId("5b7d9bc71fbad4ff99527e83")
},
{
"_id" : ObjectId("5b7d9bca1fbad410de638bbd")
},
{
"_id" : ObjectId("5b7d9bcd1fbad441494a6640")
},
{
"_id" : ObjectId("5b7d9bd01fbad4ff97273404")
}
]
}
{
"_id" : ObjectId("5b7af9701fbad410e10f32f7"),
"preview" : {
"photo" : {
"id" : ObjectId("5b7b002d1fbad441494a663c"),
"sort" : 0
}
},
"media" : [
{
"_id" : ObjectId("5b7affea1fbad441494a663b")
},
{
"_id" : ObjectId("5b7b002d1fbad441494a663c")
},
{
"_id" : ObjectId("5b7b00351fbad441494a663d")
},
{
"_id" : ObjectId("5b7d9baa1fbad410de638bbb")
},
{
"_id" : ObjectId("5b7d9bae1fbad410e10f32f9")
},
{
"_id" : ObjectId("5b7d9bb11fbad441494a663e")
},
{
"_id" : ObjectId("5b7d9bb41fbad4ff97273402")
},
{
"_id" : ObjectId("5b7d9bb71fbad4ff99527e82")
},
{
"_id" : ObjectId("5b7d9bbb1fbad410de638bbc")
},
{
"_id" : ObjectId("5b7d9bbe1fbad410e10f32fa")
},
{
"_id" : ObjectId("5b7d9bc11fbad441494a663f")
},
{
"_id" : ObjectId("5b7d9bc41fbad4ff97273403")
},
{
"_id" : ObjectId("5b7d9bc71fbad4ff99527e83")
},
{
"_id" : ObjectId("5b7d9bca1fbad410de638bbd")
},
{
"_id" : ObjectId("5b7d9bcd1fbad441494a6640")
},
{
"_id" : ObjectId("5b7d9bd01fbad4ff97273404")
}
]
}
{
"_id" : ObjectId("5b7af9701fbad410e10f32f7"),
"preview" : {
"photo" : {
"id" : ObjectId("5b7b00351fbad441494a663d"),
"sort" : 0,
"primary" : false
}
},
"media" : [
{
"_id" : ObjectId("5b7affea1fbad441494a663b")
},
{
"_id" : ObjectId("5b7b002d1fbad441494a663c")
},
{
"_id" : ObjectId("5b7b00351fbad441494a663d")
},
{
"_id" : ObjectId("5b7d9baa1fbad410de638bbb")
},
{
"_id" : ObjectId("5b7d9bae1fbad410e10f32f9")
},
{
"_id" : ObjectId("5b7d9bb11fbad441494a663e")
},
{
"_id" : ObjectId("5b7d9bb41fbad4ff97273402")
},
{
"_id" : ObjectId("5b7d9bb71fbad4ff99527e82")
},
{
"_id" : ObjectId("5b7d9bbb1fbad410de638bbc")
},
{
"_id" : ObjectId("5b7d9bbe1fbad410e10f32fa")
},
{
"_id" : ObjectId("5b7d9bc11fbad441494a663f")
},
{
"_id" : ObjectId("5b7d9bc41fbad4ff97273403")
},
{
"_id" : ObjectId("5b7d9bc71fbad4ff99527e83")
},
{
"_id" : ObjectId("5b7d9bca1fbad410de638bbd")
},
{
"_id" : ObjectId("5b7d9bcd1fbad441494a6640")
},
{
"_id" : ObjectId("5b7d9bd01fbad4ff97273404")
}
]
}
and what you can se the last data have preview.photo.primary on it, and this field i want to return when i'm done with my aggregate query.
My final query look like this:
db.getCollection('travel_sights').aggregate([{
'$match': {
'preview.photo' : {
'$exists':true
},
'_id': {
'$in' : [ObjectId("5b7af9701fbad410e10f32f7")]
}
}
},{
'$unwind' : '$preview.photo'
}, {
'$lookup':{
'from' : 'media_data',
'localField' : '_id',
'foreignField':'bind',
'as':'media'
}
},{
'$unwind':'$media'
},{
'$project' : {
'preview' : 1,
'media': 1,
}
}, {
'$group': {
'_id':'$media._id',
'primary': {
'$first':'$preview'
}
}
}])
The problem here is when i want $preview return so i can find the primary about it, its allways only return the first where the value not exists, if i use $push the problem is i get every thing.
is there a way so i can pick the right primary value in my return? have trying $addFields to but whitout eny kind of lock.
Travel_sights data:
{
"_id" : ObjectId("5b7af9701fbad410e10f32f7"),
"city_id" : ObjectId("5b6d0cb6222d4c70b803eaeb"),
"activated" : true,
"deleted" : false,
"url" : "url is here",
"name" : "title of it here",
"updated_at" : ISODate("2018-08-22T17:22:27.000Z"),
"content" : "content here",
"preview" : {
"photo" : [
{
"id" : ObjectId("5b7affea1fbad441494a663b"),
"sort" : 0
},
{
"id" : ObjectId("5b7b002d1fbad441494a663c"),
"sort" : 0
},
{
"id" : ObjectId("5b7b00351fbad441494a663d"),
"sort" : 0,
"primary" : true
},
{
"id" : ObjectId("5b7d9baa1fbad410de638bbb"),
"sort" : 0
},
{
"id" : ObjectId("5b7d9bae1fbad410e10f32f9"),
"sort" : 0
},
{
"id" : ObjectId("5b7d9bb11fbad441494a663e"),
"sort" : 0
},
{
"id" : ObjectId("5b7d9bb41fbad4ff97273402"),
"sort" : 0,
"primary" : false
},
{
"id" : ObjectId("5b7d9bb71fbad4ff99527e82"),
"sort" : 0,
"primary" : false
},
{
"id" : ObjectId("5b7d9bbb1fbad410de638bbc"),
"sort" : 0
},
{
"id" : ObjectId("5b7d9bbe1fbad410e10f32fa"),
"sort" : 0
},
{
"id" : ObjectId("5b7d9bc11fbad441494a663f"),
"sort" : 0
},
{
"id" : ObjectId("5b7d9bc41fbad4ff97273403"),
"sort" : 0,
"primary" : false
},
{
"id" : ObjectId("5b7d9bc71fbad4ff99527e83"),
"sort" : 0,
"primary" : false
},
{
"id" : ObjectId("5b7d9bca1fbad410de638bbd"),
"sort" : 0,
"primary" : false
},
{
"id" : ObjectId("5b7d9bcd1fbad441494a6640"),
"sort" : 0,
"primary" : false
},
{
"id" : ObjectId("5b7d9bd01fbad4ff97273404"),
"sort" : 0
}
]
}
}
3 sample foto bind data here:
{
"_id" : ObjectId("5b7affea1fbad441494a663b"),
"file-name" : "55575110311__0F115282-B5A0-4654-AA44-B7DC2C682992.jpeg",
"options" : [
ObjectId("5b6fb855222d4c70b8041093")
],
"type" : "images",
"files" : [
{
"width" : 70,
"height" : 53
},
{
"width" : 400,
"height" : 300
},
{
"width" : 800,
"height" : 600
},
{
"width" : 1600,
"height" : 1200
}
],
"bind" : [
ObjectId("5b7af9701fbad410e10f32f7")
]
}
{
"_id" : ObjectId("5b7b002d1fbad441494a663c"),
"file-name" : "55575110748__E7B07EFD-9F7E-40D6-8B57-38F708E4C0C0.jpeg",
"options" : [
ObjectId("5b6fb855222d4c70b8041093")
],
"type" : "images",
"files" : [
{
"width" : 70,
"height" : 53
},
{
"width" : 400,
"height" : 300
},
{
"width" : 800,
"height" : 600
},
{
"width" : 1600,
"height" : 1200
}
],
"bind" : [
ObjectId("5b7af9701fbad410e10f32f7")
],
"description" : "this is secoudn demo!",
"title" : "demo 3"
}
{
"_id" : ObjectId("5b7b00351fbad441494a663d"),
"file-name" : "paris2.jpg",
"options" : [
ObjectId("5b6fb855222d4c70b8041093")
],
"type" : "images",
"files" : [
{
"width" : 70,
"height" : 53
},
{
"width" : 400,
"height" : 300
},
{
"width" : 800,
"height" : 600
},
{
"width" : 1600,
"height" : 1200
}
],
"bind" : [
ObjectId("5b7af9701fbad410e10f32f7")
],
"description" : "this is a demo1 :)",
"title" : "demo"
}
You can filter out the element from the array where the primary field exists using $filter aggregation and then easily $group with the media._id field and get the $first document value.
Finally your query will be
db.getCollection("travel_sights").aggregate([
{ "$match": {
"preview.photo" : { "$exists":true },
"_id": { "$in" : [ ObjectId("5b7af9701fbad410e10f32f7") ] }
}},
{ "$addFields": {
"preview.photo": {
"$arrayElemAt": [
{ "$filter": {
"input": "$preview.photo",
"as": "photo",
"cond": { "$ne": [ "$$photo.primary", undefined ] }
}}, 0
]
}
}},
{ "$lookup":{
"from" : "media_data",
"localField" : "_id",
"foreignField": "bind",
"as": "media"
}},
{ "$unwind":"$media" },
{ "$project" : { "preview" : 1, "media": 1, }},
{ "$group": {
"_id": "$media._id",
"primary": { "$first": "$preview" }
}}
])

Update Documents From Each Value of an Input Array

I have a JSON with two keys: id and name. I need a way to insert that id on my collection (querys.project.name) when the name on it and on the JSON matches.
Example of my JSON:
var projectsMysql = [
{
"id" : 1,
"name" : "Something"
},
{
"id" : 5,
"name" : "Something else"
},
{
"id" : 50,
"name" : "Some name"
}]
and in my collection about 60 documents like this one:
{
"_id" : ObjectId("58e42bf30a34d641be6c25c2"),
"folio" : "R-666-69",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Something else"
// "projectsMysql_id" : 5 THIS SHOULD BE PLACED HERE
},
}
I wrote a function for Node.js but now I need to run this directly on the mongo shell, I read that I need to use cursor.forEach() method but I couldn't find a way to do it, this is my function:
projectsMysql
.forEach(function(project){
var query = {
name: project.name
}
db.getCollection('requests')
.find(query)
.exec(function(err, response){
if (err) {
return
}
if (response) {
db.getCollection('requests')
.update({id: response.id}, {$set: {
'project.projectsMysql_id': project.id
console.log("element inserted");
}})
.exec(function(err){
});
}
});
});
Can you point me in to the right direction?
What you want here is bulkWrite(), where instead of actually executing an update() for each document within the projectsMysql array you actually just construct a "single" statement made of "multiple" updates which sends to the server and updates the appropriate documents:
db.getCollection('requests').bulkWrite(
projectsMysql.map(({ id, name }) =>
({ "updateOne": {
"filter": { "project.name": name },
"update": { "$set": { "project.id": id } }
}})
)
)
If you expect "multiple documents" to match the condition, then simply switch to updateMany:
db.getCollection('requests').bulkWrite(
projectsMysql.map(({ id, name }) =>
({ "updateMany": {
"filter": { "project.name": name },
"update": { "$set": { "project.id": id } }
}})
)
)
Your array is already in memory so there is not really any point to doing any other kind of "iteration" and you can simply .map() the properties onto the updateOne statements ( or updateMany ) and issue them all in one statement. Updates are only actually processed where there is a "match" and indeed where there is something to actually update, as existing values will be left alone using $set.
To Demonstrate, considering these documents:
{
"folio" : "R-666-69",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Something else"
}
},
{
"folio" : "R-666-67",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Some name"
}
},
{
"folio" : "R-666-68",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Some name"
}
}
{
"folio" : "R-666-64",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Something"
}
},
{
"folio" : "R-666-65",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Different"
}
}
The the following update:
var projectsMysql = [
{
"id" : 1,
"name" : "Something"
},
{
"id" : 5,
"name" : "Something else"
},
{
"id" : 50,
"name" : "Some name"
}]
db.getCollection('requests').bulkWrite(
projectsMysql.map(({ id, name }) =>
({ "updateMany": {
"filter": { "project.name": name },
"update": { "$set": { "project.id": id } }
}})
)
)
Returns the response:
{
"acknowledged" : true,
"deletedCount" : 0,
"insertedCount" : 0,
"matchedCount" : 4,
"upsertedCount" : 0,
"insertedIds" : {
},
"upsertedIds" : {
}
}
And alters the matched documents accordingly:
{
"_id" : ObjectId("5b206a48f7fa0c655d90157a"),
"folio" : "R-666-69",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Something else",
"id" : 5
}
}
{
"_id" : ObjectId("5b206a48f7fa0c655d90157b"),
"folio" : "R-666-67",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Some name",
"id" : 50
}
}
{
"_id" : ObjectId("5b206a48f7fa0c655d90157c"),
"folio" : "R-666-68",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Some name",
"id" : 50
}
}
{
"_id" : ObjectId("5b206a48f7fa0c655d90157d"),
"folio" : "R-666-64",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Something",
"id" : 1
}
}
{
"_id" : ObjectId("5b206a48f7fa0c655d90157e"),
"folio" : "R-666-65",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Different"
}
}

need to filter query to get selected object in mongodb

I have the below document.
I want to return "employeedata" containing only those array objects (family, academic etc.) for which status is "current"
{
"_id" : ObjectId("5a1fe7ed1e9fdd17285ac13f"),
"createdby" : "admin",
"details" : [
{
"_id" : ObjectId("5a1fe81f1e9fdd17285ac14a"),
"employeedata" : {
"createddate" : "2017-11-30T11:13:57.290Z",
"family" : [
{
"status" : "current",
"_id" : ObjectId("5a1fe81f1e9fdd17285ac153")
}
],
"academic" : [
{
"status" : "archive",
"_id" : ObjectId("5a1fe81f1e9fdd17285ac159")
},
{
"status" : "current",
"_id" : ObjectId("5a1fe8a71e9fdd17285ac15d")
}
],
"company" : [
{
"status" : "archive",
"_id" : ObjectId("5a1fe81f1e9fdd17285ac150")
},
{
"status" : "current",
"_id" : ObjectId("5a1fe88e1e9fdd17285ac15c")
}
],
"other" : [
{
"status" : "current",
"_id" : ObjectId("5a1fe81f1e9fdd17285ac154")
}
]
}
}
]
"confirmed" : true,
}
You can try following to filter the object as it is an complex object we will have to loop through the complete object and modify it at run time.
var data = JSON.parse('{"_id":"5a1fe7ed1e9fdd17285ac13f","createdby":"admin","details":[{"_id":"5a1fe81f1e9fdd17285ac14a","employeedata":{"createddate":"2017-11-30T11:13:57.290Z","family":[{"status":"current","_id":"5a1fe81f1e9fdd17285ac153"}],"academic":[{"status":"archive","_id":"5a1fe81f1e9fdd17285ac159"},{"status":"current","_id":"5a1fe8a71e9fdd17285ac15d"}],"company":[{"status":"archive","_id":"5a1fe81f1e9fdd17285ac150"},{"status":"current","_id":"5a1fe88e1e9fdd17285ac15c"}],"other":[{"status":"current","_id":"5a1fe81f1e9fdd17285ac154"}]}}]}');
function createObject() {
for (var obj in data.details[0].employeedata) {
if (Array.isArray(data.details[0].employeedata[obj])) {
data.details[0].employeedata[obj].filter(function (value, index) {
if (value.status !== 'current') {
//return value;
data.details[0].employeedata[obj].splice(index, 1);
}
})
}
}
}

Mongodb : get whether a document is the latest with a field value and filter on the result

I am trying to port an existing SQL schema into Mongo.
We have document tables, with sometimes several times the same document, with a different revision but the same reference. I want to get only the latest revisions of the documents.
A sample input data:
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-A",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:23:18.807Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-A",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:30:35.757Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X"
}
Given this data, I want this result set (sometimes I want only the last revision, sometimes I want all revisions with an attribute telling me whether it's the latest):
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X",
"lastrev" : true
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X",
"lastrev" : true
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X",
"lastrev" : true
}
I already have a bunch of filters, sorting, and skip/limit (for pagination of data), so the final result set should be mindful of these constraints.
The current "find" query (built with the .Net driver), which filters fine but gives me all revisions of each document:
coll.find(
{ "$and" : [
{ "$or" : [
{ "deletedid" : { "$exists" : false } },
{ "deletedid" : null }
] },
{ "$or" : [
{ "taskid" : { "$exists" : false } },
{ "taskid" : null }
] },
{ "objecttypeuid" : { "$in" : ["xxxxx"] } }
] },
{ "_id" : 0, "Uid" : 1, "lastrev" : 1, "title" : 1, "code" : 1, "creator" : 1, "owner" : 1, "modificator" : 1, "status" : 1, "reference": 1, "creationdate": 1 }
).sort({ "creationdate" : 1 }).skip(0).limit(10);
Using another question, I have been able to build this aggregation, which gives me the latest revision of each document, but with not enough attributes in the result:
coll.aggregate([
{ $sort: { "creationdate": 1 } },
{
$group: {
"_id": "$reference",
result: { $last: "$creationdate" },
creationdate: { $last: "$creationdate" }
}
}
]);
I would like to integrating the aggregate with the find query.
I have found the way to mix aggregation and filtering:
coll.aggregate(
[
{ $match: {
"$and" : [
{ "$or" : [
{ "deletedid" : { "$exists" : false } },
{ "deletedid" : null }
] },
{ "$or" : [
{ "taskid" : { "$exists" : false } },
{ "taskid" : null }
] },
{ "objecttypeuid" : { "$in" : ["xxx"] } }
]
}
},
{ $sort: { "creationdate": 1 } },
{ $group: {
"_id": "$reference",
"doc": { "$last": "$$ROOT" }
}
},
{ $sort: { "doc.creationdate": 1 } },
{ $skip: skip },
{ $limit: limit }
],
{ allowDiskUse: true }
);
For each result node, this gives me a "doc" node with the document data. It has too much data still (it's missing projections), but it's a start.
Translated in .Net:
FilterDefinitionBuilder<BsonDocument> filterBuilder = Builders<BsonDocument>.Filter;
FilterDefinition<BsonDocument> filters = filterBuilder.Empty;
filters = filters & (filterBuilder.Not(filterBuilder.Exists("deletedid")) | filterBuilder.Eq("deletedid", BsonNull.Value));
filters = filters & (filterBuilder.Not(filterBuilder.Exists("taskid")) | filterBuilder.Eq("taskid", BsonNull.Value));
foreach (var f in fieldFilters) {
filters = filters & filterBuilder.In(f.Key, f.Value);
}
var sort = Builders<BsonDocument>.Sort.Ascending(orderby);
var group = new BsonDocument {
{ "_id", "$reference" },
{ "doc", new BsonDocument("$last", "$$ROOT") }
};
var aggregate = coll.Aggregate(new AggregateOptions { AllowDiskUse = true })
.Match(filters)
.Sort(sort)
.Group(group)
.Sort(sort)
.Skip(skip)
.Limit(rows);
return aggregate.ToList();
I'm pretty sure there are better ways to do this, though.
You answer is pretty close. Instead of $last, $max is better.
About $last operator:
Returns the value that results from applying an expression to the last document in a group of documents that share the same group by a field. Only meaningful when documents are in a defined order.
Get the last revision in each group, see code below in mongo shell:
db.collection.aggregate([
{
$group: {
_id: '$reference',
doc: {
$max: {
"creationdate" : "$creationdate",
"code" : "$code",
"Uid" : "$Uid",
"status" : "$status",
"title" : "$title",
"creator" : "$creator"
}
}
}
},
{
$project: {
_id: 0,
Uid: "$doc.Uid",
status: "$doc.status",
reference: "$_id",
code: "$doc.code",
title: "$doc.title",
creationdate: "$doc.creationdate",
creator: "$doc.creator"
}
}
]).pretty()
The output as your expect:
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X"
}
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X"
}
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X"
}

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.