I have a sample schema like this -
Comment.add({
text:String,
url:{type:String,unique:true},
username:String,
timestamp:{type:Date,default:Date}
});
Feed.add({
url:{type:String, unique:true },
username:String,
message:{type:String,required:'{PATH} is required!'},
comments:[Comment],
timestamp:{type:Date,default:Date}
});
Now, I don't want to expose the _id fields to the outside world that's why I am not sending it to the clients anywhere. Now, I have two important properties in my comment schema (username,url) What I want to do is update the content of the sub document that satisfies
if the comment.username
is same as my client value req.user.username
then update the comment.text
property of that record whose url was supplied by client in req.body.url
variable.
One long and time consuming approach I thought is to first find the feed with the given url and then iterating over all the subdocuments to find the document which satisfies the comment.url==req.body.url
and then check if the comment.username==req.user.username
if so, update the comment object.
But, I think there must be an easier way of doing this?
I already tried -
db.feeds.update({"username":"[email protected]","comments.username":"[email protected]","comments.url":"test"},{$set:{"comments.$.text":"updated text 2"}})
but this updates even when the comments.url
or comments.username
matches other sub documents
and I also tried
db.feeds.distinct("comments._id",{"comments.url":req.body.url})
to find the _id of document associated with the url
but it returns all the _id
in the subdocument
First off - you should not rely on _id
not being seen by the outside world in terms of security. This is a very bad idea for a multitude of reasons (primarily REST and also the fact that it's returned by default with all your queries).
Now, to address your question, what you want is the $elemMatch
operator. This says that you're looking for something where the specified sub-document within an array matches multiple queries.
E.g.
db.feeds.update({
"username":"[email protected]",
comments: {
$elemMatch: {
username: "[email protected]",
url: "test"
}
}
}, {$set: {"comments.$.text":"updated text 2"}})
If you don't use $elemMatch
you're saying that you're ok with the document if any of the comments match your query - i.e. if there is a comment by user "[email protected]", and separate comment has a url "test", the document will match unless you use $elemMatch