How would I push an object to this nested file in mongodb - mongodb

My file has the following structure and I want to pass an object into the responses array, but it needs to go into the right comment responses array based on the post _id matching and then the comment_id matching.
{
post1: {
post: 'anything',
comments: [
{comment: 'anything', comment_id: RANDOM ID, responses: []},
{comment: 'something else', comment_id: ANOTHER RANDOM ID, responses: []},
],
_id: RANDOM ID
}
}
How would I add this object to the mongodb database, to add comments I used
Post.findOneAndUpdate({_id: req.body.post}, {$push: {comments: newComment}})
But I'm not sure how this works for adding responses because there's essentially 2 layers that need to be authenticated before it's pushed to the array

Try this,
Post.findOneAndUpdate({_id: req.body.post, 'comments.comment_id': req.body.commentId}, {$push: { comments: newComment}})

Considering this data:
{
"_id" : "abc",
"post1" : {
"_id" : "post_1",
"post" : "anything",
"comments" : [
{
"comment" : "anything",
"comment_id" : "comment_1",
"responses" : []
},
{
"comment" : "something else",
"comment_id" : "comment_2",
"responses" : []
}
]
}
}
The correct way to do it is like this:
Post.findOneAndUpdate(
{
_id: 'abc',
'post1._id': 'post_1',
'post1.comments.comment_id': 'comment_1',
},
{ $push: { 'post1.comments.$.responses': 'bla' } }
)
Note the $ in the path of the update query which is selected by { 'post1.comments.comment_id': 'comment_1' } in your conditional query.

Related

MongoDB - Update a field in an object of an array with multiple filter conditions

If I have the following document in my database :
{
"_id" : MainId,
"subdoc" : [
{
"_id" : SubdocId,
"userid" : "someid",
"toupdate": false,
},
{
"_id" : SubdocId2,
"userid" : "someid2",
"toupdate": false,
}
],
"extra" : [
"extraid1",
"extraid2"
]
}
How can I update the subdocument SubdocId2 where the id (SubdocId2) must match and either SubdocId2's userid is "someid2" OR value "extraid1" exists in "extra"?
The farthest I got is:
db.coll.update({
"subdoc._id":"SubdocId2", {
$or: ["extra":{$in:["extraid1"]}, "subdoc.userid":"someid2"]
}
}, {"subdoc.$.toupdate":true})
Maybe I forgot to quote up something, but I get an error (SyntaxError: invalid property id)
Please try this :
db.coll.update(
{
$and: [{ "subdoc._id": SubdocId2 }, {
$or: [{ "extra": { $in: ["extraid1"] } },
{ "subdoc.userid": "someid2" }]
}] // subdoc._id && (extra || subdoc.userid)
}, { $set: { "subdoc.$.toupdate": true } })
In your query there are couple of syntax issues & also if you don't use $set in your update part - it replace the entire document with "subdoc.$.toupdate": true. Of course if you're using mongoose that's different scenario as mongoose will internally does add $set but when executing in shell or any client you need to specify $set. Also if SubdocId2 is ObjectId() you need to convert string to ObjectId() in code before querying database.

Project values of different columns into one field

{
"_id" : ObjectId("5ae84dd87f5b72618ba7a669"),
"main_sub" : "MATHS",
"reporting" : [
{
"teacher" : "ABC"
}
],
"subs" : [
{
"sub" : "GEOMETRIC",
"teacher" : "XYZ",
}
]
}
{
"_id" : ObjectId("5ae84dd87f5b72618ba7a669"),
"main_sub" : "SOCIAL SCIENCE",
"reporting" : [
{
"teacher" : "XYZ"
}
],
"subs" : [
{
"sub" : "CIVIL",
"teacher" : "ABC",
}
]
}
I have simplified the structure of the documents that i have.
The basic structure is that I have a parent subject with an array of reporting teachers and an array of sub-subjects(each having a teacher)
I now want to extract all the subject(parent/sub-subjects) along with the condition if they are sub-subjects or not which are taught by a particular teacher.
For eg:
for teacher ABC i want the following structure:
[{'subject':'MATHS', 'is_parent':'True'}, {'subject':'CIVIL', 'is_parent':'FALSE'}]
-- What is the most efficient query possible ..? I have tried $project with $cond and $switch but in both the cases I have had to repeat the conditional statement for 'subject' and 'is_parent'
-- Is it advised to do the computation in a query or should I get the data dump and then modify the structure in the server code? AS in, I could $unwind and get a mapping of the parent subjects with each sub-subject and then do a for loop.
I have tried
db.collection.aggregate(
{$unwind:'$reporting'},
{$project:{
'result':{$cond:[
{$eq:['ABC', '$reporting.teacher']},
"$main_sub",
"$subs.sub"]}
}}
)
then I realised that even if i transform the else part into another query for the sub-subjects I will have to write the exact same thing for the property of is_parent
You have 2 arrays, so you need to unwind both - the reporting and the subs.
After that stage each document will have at most 1 parent teacher-subj and at most 1 sub teacher-subj pairs.
You need to unwind them again to have a single teacher-subj per document, and it's where you define whether it is parent or not.
Then you can group by teacher. No need for $conds, $filters, or $facets. E.g.:
db.collection.aggregate([
{ $unwind: "$reporting" },
{ $unwind: "$subs" },
{ $project: {
teachers: [
{ teacher: "$reporting.teacher", sub: "$main_sub", is_parent: true },
{ teacher: "$subs.teacher", sub: "$subs.sub", is_parent: false }
]
} },
{ $unwind: "$teachers" },
{ $group: {
_id: "$teachers.teacher",
subs: { $push: {
subject: "$teachers.sub",
is_parent: "$teachers.is_parent"
} }
} }
])

MongoDB, how to query a document but limit an array in that document

I have the following document:
{
_id: asdfasdf,
title: "ParentA",
children: [
{
_id: abcd <-- using new ObjectId() to generate these on creation
title: "ChildA",
},
{
_id: efgh,
title: "ChildB"
}
]
}
What I want to do is use findOne but I only want the returned document to contain a single child in its array.
Sudo logic
Categories.findOne({ _id: "asdfasdf" }, { children: _Id: "abcd" });
I want the returned document to look like this:
{
_id: asdfasdf,
title: "ParentA",
children: [
{
_id: abcd <-- using new ObjectId() to generate these on creation
title: "ChildA",
}
]
}
The purpose of this is so I can pass the information into an edit form, and then update that single child object in the array on save.
I'm getting confused as to how to limit the result set.
Thank you very much!
---- Edit ----
After attempting to use the suggested duplicate question as a reference, I'm getting undefined in my results. I really want to use findOne instead of find() as well. On the client, a collection object, even though it contains one item, is treated differently than a single (findOne) object that is returned.
Here is what I've tried.
db.Category.findOne({
"_id": parentid,
"children._id": childid
},{
"_id": childid,
"children": {
"$elemMatch": {
"_id": childid
}
}
});
db.Category.findOne({
"_id": parentid
},{
"children": {
"$elemMatch": {
"_id": childid
}
}
});
I've tried several more variations like the above.
---- Edit 2 ----
Based on a comment, here is the output of the following query:
db.category.findOne({ "_id" : "9dYgKFczgiRcNouij"});
{
"title" : "Appliances",
"active" : true,
"children" : [
{
"_id" : ObjectId("680d55c6995ef6f0748278c2"),
"title" : "Laundry",
"active" : true
},
{
"_id" : ObjectId("2b4469c1a4c8e086942a1233"),
"title" : "Kitchen"
"active" : true
},
{
"_id" : ObjectId("4f5562ef7668839704c851d6"),
"title" : "Other"
"active" : true
}
],
"_id" : "9dYgKFczgiRcNouij"
}
So I think perhaps my problem is how I created the children._id in the array. I used new ObjectId() to generate the _id.
--- Edit 3 ---
db.category.findOne({
"_id": "9dYgKFczgiRcNouij"
},{
"children": {
"$elemMatch": {
"_id": ObjectId("4f5562ef7668839704c851d6")
}
}
});
This returns ObjectID is not defined.

Mongoose aggregate issue with grouping by nested field

I have a schema that stores user attendance for events:
event:
_id: ObjectId,
...
attendances: [{
user: {
type: ObjectId,
ref: 'User'
},
answer: {
type: String,
enum: ['yes', 'no', 'maybe']
}
}]
}
Sample data:
_id: '533483aecb41af00009a94c3',
attendances: [{
user: '531770ea14d1f0d0ec42ae57',
answer: 'yes',
}, {
user: '53177a2114d1f0d0ec42ae63',
answer: 'maybe',
}],
I would like to return this data in the following format when I query for all attendances for a user:
var attendances = {
yes: ['533497dfcb41af00009a94d8'], // These are the event IDs
no: [],
maybe: ['533497dfcb41af00009a94d6', '533497dfcb41af00009a94d2']
}
I am not sure the aggegation pipeline will return it in this format? So I was thinking I could return this and modify it easily:
var attendances = [
answer: 'yes',
ids: ['533497dfcb41af00009a94d8'],
},{
answer: 'no',
ids: ['533497dfcb41af00009a94d8']
}, {
answer: 'maybe',
ids: ['533497dfcb41af00009a94d6', '533497dfcb41af00009a94d2']
}];
My attempt is not succesful however. It doesn't group it by answer:
this.aggregate({
$match: {
'attendances.user': someUserId
}
}, {
$project: {
answer: '$attendances.answer'
}
}, {
$group: {
_id: '$answer',
ids: {
$addToSet: "$_id"
}
}
}, function(e, docs) {
});
Can I return the data I need in the first desired format and if not how can I correct the above code to achieve the desired result?
On that note - perhaps the map-reduce process would be better suited?
The below query will help you get close to the answer you want. Although, it isn't exactly in the same format you are expecting, you get separate documents for each answer option and an array of event id's.
db.collection.aggregate([
// Unwind the 'attendances' array
{"$unwind" : "$attendances"},
// Match for user
{"$match" : {"attendances.user" : "53177a2114d1f0d0ec42ae63"}},
// Group by answer and push the event id's to an array
{"$group" : {_id : "$attendances.answer", eventids : {$push : "$_id"}}}
])
This produces the below output:
{
"result" : [
{
"_id" : "yes",
"eventids" : [
ObjectId("53d968a8c4840ac54443a9d6")
]
},
{
"_id" : "maybe",
"eventids" : [
ObjectId("53d968a8c4840ac54443a9d7"),
ObjectId("53d968a8c4840ac54443a9d8")
]
}
],
"ok" : 1
}

How do I replace an entire array of subdocuments in MongoDB?

Here is an example document from my collection:
Books
[
id: 1,
links:
[
{text: "ABC", "url": "www.abc.com"},
{text: "XYZ", "url": "www.xyz.com"}
]
]
I want to replace the links array in one update operation. Here is an example of how the above document should be modified:
Books
[
id: 1,
links:
[
{text: "XYZ", "url": "www.xyz.com"},
{text: "efg", "url": "www.efg.com"}, <== NEW COPY OF THE ARRAY
{text: "ijk", "url": "www.ijk.com"}
]
]
As you can see, the links array has been replaced (old data removed, and new data added).
I am having very hard time with the Update.Set() because it says it MyLinks<> cannot be mapped to a BsonValue
I've tried many different ways of achieving this, and all of them fail, including .PushAllWrapped<WebLinkRoot>("links", myDoc.WebLinks).
Everything I've tried results in the new values being appended to the array, rather than the array being replaced.
As it seems MongoDB doesn't provide a simple method to replace an array of subdocument OR a method like .ClearArray(), what is the best way for me to ensure the array is cleared before adding new elements in a single query?
I am here because I saw 5k views on this post, I'm adding some stuff may be it help other who looking for answer of above
db.collectionName.insertOne({
'links': [
{
"text" : "XYZ",
"url" : "www.xyz.com"
}
]
});
now run this query which help to replace older data
db.collectionName.update(
{
_id: ObjectId("your object Id")
},
{
$set:
{
'links':[ {
"text" : "XYZ1",
"url" : "www.xyz.com1"
} ]
}
});
I think you have to do something like this:
var newArray = new BSONArray {
new BSONDocument { { "text", "XYZ" }, { "url", "www.xyz.com" } },
new BSONDocument { { "text", "efg" }, { "url", "www.efg.com" } },
new BSONDocument { { "text", "ijk" }, { "url", "www.ijk.com" } }
};
var update = Update.Set( "links", newArray );
collection.Update( query, update );
Or whatever method you can to cast as a valid BSONValue.
So equivalent to shell:
{ "links" : [ { "text" : "abc" } ] }
> db.collection.update(
{},
{ $set:
{ links: [
{ text: "xyz", url: "something" },
{ text: "zzz", url: "else" }
]}
})
>db.collection.find({},{ _id: 0, links:1 }).pretty()
{ "links" : [
{
"text" : "xyz",
"url" : "something"
},
{
"text" : "zzz",
"url" : "else"
}
]
}
So that works.
You clearly need something else other than embedded code. But hopefully that puts you on the right track.