Pull item by value from embedded array in Mongo - arrays

I want to pull a specific item from an embedded array...assume the following mongo document....
db.test.find()
{
id:1,
comments :
[
{ cid: 1 },
{ cid: 2 },
{ cid: 3 },
{ cid: 4 },
{ cid: 5 }
]
}
I want to remove an item from the comments array by cid, not by position. I've tried all of these but none of them appear to work. I've tried using the dot notation, but that does not seem to have any effect. I tried the last post suggestion from How to Delete-nth element from array, but no luck...
db.test.update({ 'comments.cid' : 5}, {"$pull" :{"comments":{"cid":"3"}}} )
db.test.update( {id: 1}, {"$pull" : {"comments" : { "cid" : "3"}}},false,false)
db.test.update( {id: 1}, {"$pull" :{"comments.cid" :3}})

this should work:
db.test.update( {id: 1}, {$pull :{comments: {cid :3}}})
also, in your document, you have:
id: 1 without comma at the end, it shold be:
id:1,

These worked too...
db.test.update({comments:{cid:4} },
{$pull:{comments:{cid:4}},
$inc:{commentCount: -1}})
db.test.update({"comments.cid" : 17},
{$pull:{ comments:{cid: 17}},
$inc:{commentCount:-1}})

Just wanted to modify the answer so that it can delete multiple objects from an array.
db.test.update( {id: 1}, {"$pullAll" : {"comments" : [{ "cid" : "3"},{ "cid" : "2"}]}})
This answer has been updated and it works with mongoose too

Related

MongoDB - Delete item in nested array

I saw a lot of similar questions about this but couldn't make it work in my collection.
How can I delete a specific comment (you can assume that I know the userId).
For example, I want to remove comment with commentId=3 and I know that it's under userId=1.
[
{
userId: "1",
posts: [
{
postId: "2",
comments: [
{
commentId: "3",
...
},
...
]
},
...
]
},
...
]
Thanks!
This works with Mongodb 4.2 and will remove first array entry from "comments" if it matches the "commentId" 3
db.posts.update(
{"userId" : "1"},
{$pull : {"posts.$[].comments" : {"commentId":"3"}}}
)
If you would like to remove all array entries use:
db.posts.update(
{"userId" : "1"},
{$pull : {"posts.$[].comments" : {"commentId":"3"}}},
{"multi": true}
)
Have you tried using the deleteOne()function ? try and have a look on MongoDb documentation. MongoDbDocumentation

MongoError: Found multiple array filters with the same top-level field name

folks. I'm working in Node.js with a MongoDB collection that has a field that is an array of objects, like so:
{_id: 'someIdNumber',
text: 'some text',
replies: [{_id: 'someReplyId', replyText: 'some reply text', password: 'somePassword'}, {...}]
I'm trying to update the replyText field of the replies array using the $[<identifier>] array update operator as shown in the MongoDB documentation: https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/ What I'm trying to do is as follows:
db.collection('collectionName').updateOne(
{ _id: ObjectID('whateverId') },
{ $set: { "replies.$[elem].replyText": "new text" } },
{
arrayFilters: [{ "elem._id": ObjectID(req.body.reply_id)}, {"elem.password": 'whateverPassword}]
},
(err, data) => {console.log('hooray, it worked')}
This throws an error, MongoError: Found multiple array filters with the same top-level field name elem. If I get rid of one of my arrayFilters, this fixes the error, but obviously at the expense of my filtering conditions.
The MongoDB documentation's example of this process, (I've shortened the collection students2 to a single document,) is as follows:
{
"_id" : 1,
"grades" : [
{ "grade" : 80, "mean" : 75, "std" : 6 },
{ "grade" : 85, "mean" : 100, "std" : 4 },
{ "grade" : 85, "mean" : 100, "std" : 6 }
]
}
db.students2.update(
{ },
{ $inc: { "grades.$[elem].std" : -1 } },
{ arrayFilters: [ { "elem.grade": { $gte: 80 }, "elem.std": { $gt: 5 } } ], multi: true }
)
The syntax is a tiny bit different because the documentation is using the Mongo shell method, not Node.js, but otherwise, it looks to me like I'm doing what the documentation says to do. I'm using updateOne and not update because I only want to update one document, and I'm not using multi for the same reason.
Any insight on how I could get this to work with both arrayFilters intact would be much appreciated.
Thanks for your time!
Got it! Unfortunately I cannot upvote the question/comment hence adding here for others to benefit. Thanks #LuosRestil and #typesafe for getting me to the fix. I had the same syntax error where I tried to add multiple expressions for the same field in the array for the arrayFilters. It should be once expression per field.
WRONG:
arrayFilters: [
{ "elemA.<fieldName1>": "value1" },
{ "elemA.<fieldName2>": "value2" },
{ "elemA.<fieldName3>": "value3" }
];
CORRECT:
arrayFilters: [
{
"elemA.<fieldName1>": "value1",
"elemA.<fieldName2>": "value2",
"elemA.<fieldName3>": "value3"
}
];

MongoDB $and Query only if the $and is true within the same array index of a document - especially for a doubly nested array [duplicate]

This question already has answers here:
How to search in array of object in mongodb
(5 answers)
Closed 3 years ago.
1. Finding an $and match only if it occurs within the same array element
Let us assume we have these two documents in a mongoDB:
{_id: 1, people: [{height: 10, age: 10}, {height: 5, age: 5}]}
{_id: 2, people: [{height: 10, age: 5}, {height: 5, age: 10}]}
And we want to match every document where there is at least 1 person whose height and age are both >= 10.
My current query is similar to
{$and: [{people.height: {$gte, 10}}, {people.age: {$gte: 10}}]}
However, this is returning both ID 1 and 2, when only id 1 contains a single person who is >=10 for both.
2. Same problem as above, but that list is nested within a second list. Only ID's should be returned if there is at least one person who is both >= 10 age and height.
Lets assume we have these two documents in a mongoDB:
{_id: 1, families: [people: [{height: 10, age: 10}, {height: 5, age: 5}], people: [{height: 0, age: 0}, {height: 0, age: 0}]]}
{_id: 2, families: [people: [{height: 10, age: 5}, {height: 0, age: 0}], people: [{height: 5, age: 10}, {height: 0, age: 0}]]}
How could I construct a query to only match id 1?
3. Building the correct index for this type of query
Currently I have a compound index equivalent to {families.people.height, families.people.age} but I believe this will not be optimized for the query I should be using. Should the index instead be {families.people}?
Note: hypothetically, this query is being run on a mongoDB with ~700,000,000 documents
So, you did not clarify the requirements for data output format. Assuming the data schema returned from the aggregation does not need to be identical to the documents in the collection you could use $unwind.
Example:
db.coll.aggregate([
{ $unwind: "$people" },
{ $addFields: {
isHeightMatch: { $gte: [ "$people.height", 10 ] },
isAgeMatch: { $gte: [ "$people.age", 10 ] }
}
},
{ $match: {
$and: [
{ isHeightMatch: true },
{ isAgeMatch: true }
]
}
},
{ $project: {
"isHeightMatch": 0,
"isAgeMatch": 0
}
}
])
Output:
{ "_id" : 1, "people" : { "height" : 10, "age" : 10 } }
You can use MongoDB $elemMatch to achieve more specific array element matches.
In order to get at least 1 person whose height and age are both >= 10, you can use this query:
{ people: { $elemMatch: { $and: [ {height: {$gte: 10} }, { age: {$gte: 10} } ] } } }
The same thing applies to nested arrays. If the people array is nested in a families array, you can use this query:
{ 'families.people': { $elemMatch: { $and: [ {height: {$gte: 10} }, { age: {$gte: 10} } ] } } }
I hope that helps.

MongoDB - Can't push an item to an array inside an object inside an array

I have this object in MongoDB:
{
_id: ObjectId("1"),
data: {
city: "ccp",
universities: [
{
_id: "2"
name: "universityOne"
students: []
},
{
_id: "3"
name: "universityTwo",
students: []
}
]
}
}
I need to push a Student object inside the students array inside the universityOne object inside the universities array, inside the global object.
I tried documentation and come up with this queries.
Mongo shell returns { "acknowledged" : true, "matchedCount" : 0, "modifiedCount" : 0 }. And nothing changes.
Here are the queries formatted/unformatted, so you can see them:
db.pautas.updateOne({_id: ObjectId("1")}, {$push: {"data.universities.$[id].students": {name: "aStudentName", age: 22}}}, {arrayFilters: [{"id._id": ObjectId("2")}]})
db.pautas.updateOne({_id: ObjectId("1")}, {$push: {"data.universities.$[id].students": {name: "aStudentName", age: 22}}}, {arrayFilters: [{"id._id": ObjectId("2")}]})
This second query is with the name of the university on the mongo [<identifier>]. But doesn't work either.
db.pautas.updateOne({_id: ObjectId("1")}, {$push: {"data.universities.$[name].students": {name: "aStudentName", age: 22}}}, {arrayFilters: [{"name.name": "universityOne"}]})
db.pautas.updateOne({_id: ObjectId("1")}, {$push: {"data.universities.$[name].students": {name: "aStudentName", age: 22}}}, {arrayFilters: [{"name.name": "universityOne"}]})
Regards.
UPDATE
Real object:
{
_id: ObjectId("5c6aef9bfc1a2693588827d9"),
datosAcademicos: {
internados: [
{
_id: ObjectId("5c6bfae98857df9f668ff2eb"),
pautas: []
},
{
_id: ObjectId("5c6c140f8857df9f668ff2ec"),
pautas: []
}
]
}
}
I need to add a Pauta to the pautas array. I've set pautas to an array of strings for debugging purpose, so just need to push a "hello world" or whatever string.
I tried this with the answers I've been given:
db.pautas.updateOne({"_id":ObjectId("5c6aef9bfc1a2693588827d9"), "datosAcademicos.internados._id": ObjectId("5c6bfae98857df9f668ff2eb")}, { $push: {"datosAcademicos.internados.$.pautas": "hi"}})
db.pautas.updateOne({"_id":ObjectId("5c6aef9bfc1a2693588827d9"), "datosAcademicos.internados._id": ObjectId("5c6bfae98857df9f668ff2eb")}, { $push: {"datosAcademicos.internados.$.pautas": "hi"}})
Update 2:
Mongo version: v4.0.2
Using Robo 3T.
I created a test database
And tried this command
Still not working.
There are 3 issues with your statement. First, the root ID field is "id" and you're querying the "_ID".
Second, you should put the match fields altogether. This update works as expected.
Third, you should use "$" to select the nested array position, not "$[id]".
db.pautas.updateOne(
{ "id": ObjectId("1"), "data.universities._id": "2"},
{$push:
{"data.universities.$.students": {name: "aStudentName", age: 22}}
})
Answer to the question UPDATE:
The update statement worked just fine.
Update statement
Record after update - I ran my update with your data and then the update code you posted, both worked just fine.

Move an element from one array to another within same document MongoDB

I have data that looks like this:
{
"_id": ObjectId("4d525ab2924f0000000022ad"),
"array": [
{ id: 1, other: 23 },
{ id: 2, other: 21 },
{ id: 0, other: 235 },
{ id: 3, other: 765 }
],
"zeroes": []
}
I'm would like to to $pull an element from one array and $push it to a second array within the same document to result in something that looks like this:
{
"_id": ObjectId("id"),
"array": [
{ id: 1, other: 23 },
{ id: 2, other: 21 },
{ id: 3, other: 765 }
],
"zeroes": [
{ id: 0, other: 235 }
]
}
I realize that I can do this by doing a find and then an update, i.e.
db.foo.findOne({"_id": param._id})
.then((doc)=>{
db.foo.update(
{
"_id": param._id
},
{
"$pull": {"array": {id: 0}},
"$push": {"zeroes": {doc.array[2]} }
}
)
})
I was wondering if there's an atomic function that I can do this with.
Something like,
db.foo.update({"_id": param._id}, {"$move": [{"array": {id: 0}}, {"zeroes": 1}]}
Found this post that generously provided the data I used, but the question remains unsolved after 4 years. Has a solution to this been crafted in the past 4 years?
Move elements from $pull to another array
There is no $move in MongoDB. That being said, the easiest solution is a 2 phase approach:
Query the document
Craft the update with a $pull and $push/$addToSet
The important part here, to make sure everything is idempotent, is to include the original array document in the query for the update.
Given a document of the following form:
{
_id: "foo",
arrayField: [
{
a: 1,
b: 1
},
{
a: 2,
b: 1
}
]
}
Lets say you want to move { a: 1, b: 1 } to a different field, maybe called someOtherArrayField, you would want to do something like.
var doc = db.col.findOne({_id: "foo"});
var arrayDocToMove = doc.arrayField[0];
db.col.update({_id: "foo", arrayField: { $elemMatch: arrayDocToMove} }, { $pull: { arrayField: arrayDocToMove }, $addToSet: { someOtherArrayField: arrayDocToMove } })
The reason we use the $elemMatch is to be sure the field we are about to remove from the array hasn't changed since we first queried the document. When coupled with a $pull it also isn't strictly necessary, but I am typically overly cautious in these situations. If there is no parallelism in your application, and you only have one application instance, it isn't strictly necessary.
Now when we check the resulting document, we get:
db.col.findOne()
{
"_id" : "foo",
"arrayField" : [
{
"a" : 2,
"b" : 1
}
],
"someOtherArrayField" : [
{
"a" : 1,
"b" : 1
}
]
}

Resources