How do I replace an entire array of subdocuments in MongoDB? - 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.

Related

How would I push an object to this nested file in 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.

MongoDB - Update parent using child

I have a document structure like this : (but with many markers and forms)
{
markers : [
{
id : 1,
updateDate : 1538051924574,
forms : [
{
id : 2,
sent : false
}
]
}
]
}
And I wanted to update forms.sent to true, so I made this query :
updateOne({
}, {
$set: {
"markers.$[].forms.$[b].sent": true
}
}, {
arrayFilters: [
{"b.id": "2"}
]
})
Everything is working as intended, but now I would like to update markers.udpateDate for every forms.sent updated. But I don't have access to marker.id, I can only use form.id.
My question is, can I update parent attribute using only child accessor ?
I already tried something like this :
updateOne({
}, {
$set: {
"markers.$[].forms.$[b].sent": true,
"markers.$[].updateDate": Date.now()
}
}, {
arrayFilters: [
{"b.id": "2"}
]
})
But as I would think, it does update every markers in the array..
If anyone has any idea,
Thanks
Just add another placeholder for parent, and duplicate you arrayFiter with this new placeholder :
db['01'].updateOne({
}, {
$set: {
"markers.$[].forms.$[b].sent": true,
"markers.$[dateUp].updateDate": Date.now() <=
}
}, {
arrayFilters: [
{"b.id": 2},
{"dateUp.forms.id":2} <=
]
})
Note : You used string value in your arrayFilters, but according to your schema it must be a number.

mongoDB find and update or insert

I am using mongoDB and mongoose.
I have the following scheme:
{ "group" :
"items" : [
{
"name": "aaa",
"value": "aaa_value",
"description": "some_text"
},
{
"name": "bbb",
"value": "bbb_value"
"description": "some_text2"
},
{
"name": "ccc",
"value": "ccc_value"
"description": "some_text3"
},
]
}
My function receives a name and a value.
If an item with this name is presented, I want to update the value according to the value parameter (and do not change the description).
If not, I want to add a new item to the array, with the parameter name and value, and a default description.
How can it be done?
Thank you
upsert operation is not possible on embedded array. It has be to 2 step process.
Either you first (try to) remove record and then push it. Or update the record first, and if that fails, then insert it.
First approach: (first delete it)
db.collection.update(
{ _id : ObjectId("xyz")},
{ $pull: {"items.name": "aaa"}}
)
then, insert it:
db.collection.update(
{ _id : ObjectId("xyz")},
{ $push: {"items": {
name : "ddd",
value: "new value",
description: "new description"
}}
)
Second Approach: (first update it)
var result = db.collection.update(
{
_id : ObjectId("xyz"),
"items.name": "aaa")
},
{
$set: {"items.$.name": {name: "new name"}}
}
);
And then, if nothing updates, then insert it.
if(!result.nMatched)
{
db.collection.update(
{
_id: ObjectId("xyz"),
"items.name": {$ne: ObjectId("xyz"}}
},
{
$push: {
items: {
name: "new name",
value: "new value",
description: "new description"
}
}
}
);
}

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.

MongoDB retrieve only specific data

I have collection like this:
{
"Post": {
"post": "These are following example of your station of the various stamps, and this can't be foured by the name.\n\n also I can't use this way to search string in the midle way, also what are you doing is the default factory",
"like": [
"rudi",
"tabootie",
"oknoorap",
"various",
"rusian_roulette"
],
"Comment": [
{
"comment_id": 1,
"name": "Anonymous",
"comment": "You are absolutely right dude, when you call me, you can host here",
"like": [
"rudi",
"stumble",
"upon",
"facebook"
]
"timestamp": {
"t": 9000,
"i": 1311245225
}
},
{
"comment_id": 2,
"name": "Anonymous",
"comment": "the guy is here",
"like": [
"rudi",
"stumble",
"upon",
"facebook"
]
"timestamp": {
"t": 10000,
"i": 1311245225
}
},
{
"comment_id": 2,
"name": "Oknoorap",
"comment": "the other guy is here",
"like": [
"rudi",
"stumble",
"upon",
"facebook"
]
"timestamp": {
"t": 11000,
"i": 1311245225
}
}
]
}
}
Could you help me? How to retrieve only for Post.Comment.comment_id = 2, negation for _id, post, etc
It is not possible to retrieve only parts of an array. You can limit fetching only per document/embedded document.
Java or Javascript?
Java:
//Query items
BasicDBObject query = new BasicDBObject();
query.put("Post.Comment.comment_id", 2);
//Show items
BasicDBObject showField = new BasicDBObject();
showField.put("post", 1); //show
showField.put("other", 0); //hide
//Execute query
DBCursor cursor = dbc.find(query, showField);
......
Although your document schema could probably be improved .. the Aggregation Framework in MongoDB 2.2 enables some new flexibility.
Note: you have two comments with comment_id = 2 in your example, which seems to be a mistake if you are using this to uniquely identify a comment. I'm assuming your third comment_id should actually be comment_id = 3.
Here is a commented example using aggregate():
db.posts.aggregate(
// Find specific post
{ $match : {
'_id' : 123,
}},
// Unwind the Post.Comment array into a stream of documents
{ $unwind : '$Post.Comment' },
// Match specific comment_id
{ $match : {
'Post.Comment.comment_id' : 2,
}},
// Limit results to the embedded Comment
{ $project: {
'Post.Comment': 1
}}
)
.. and the results:
{
"result" : [
{
"_id" : 123,
"Post" : {
"Comment" : {
"comment_id" : 2,
"name" : "Anonymous",
"comment" : "the guy is here",
"like" : [
"rudi",
"stumble",
"upon",
"facebook"
],
"timestamp" : {
"t" : 10000,
"i" : 1311245225
}
}
}
}
],
"ok" : 1
}