Delete entire document if array has no element after pull - mongodb

Sample
db.collection.update({},
{
"$pull": {
"a": {
x: {
$lt: ISODate("2022-01-01T00:00:00Z")
}
}
}
},
{
multi: true
})
How do I delete the document if pull results empty array? I don't want to shoot two queries. Thought of using aggregate update using cond. But as per documentation, pull is not supported.
Any suggestions please?

Related

MongoDB: Add field to all objects in array, based on other fields on same object?

I am fairly new to MongoDB and cant seem to find a solution to this problem.
I have a database of documents that has this structure:
{
id: 1
elements: [ {elementId: 1, nr1: 1, nr2: 3}, {elementId:2, nr1:5, nr2: 10} ]
}
I am looking for a query that can add a value nr3 which is for example nr2/nr1 to all the objects in the elements array, so that the resulting document would look like this:
{
id: 1
elements: [ {elementId: 1, nr1: 1, nr2: 3, nr3:3}, {elementId:2, nr1:5, nr2: 10, nr3: 2} ]
}
So I imagine a query along the lines of this:
db.collection.updateOne({id:1}, {$set:{"elements.$[].nr3": nr2/nr1}})
But I cant find how to get the value of nr2 and nr1 of the same object in the array.
I found some similar questions on stackoverflow stating this is not possible, but they were 5+ years old, so I thought maybe they have added support for something like this.
I realize I can achieve this with first querying the document and iterate over the elements-array doing updates along the way, but for the purpose of learning I would love to see if its possible to do this in one query.
You can use update with aggregation pipeline starting from MongoDB v4.2,
$map to iterate loop of elements
divide nr2 with nr1 using $divide
merge current object and new field nr3 using $mergeObjects
db.collection.updateOne(
{ id: 1 },
[{
$set: {
elements: {
$map: {
input: "$elements",
in: {
$mergeObjects: [
"$$this",
{ nr3: { $divide: ["$$this.nr2", "$$this.nr1"] } }
]
}
}
}
}
}]
)
Playground
db.collection.update(
{ id:1},
{ "$set": { "elements.$[elem].nr3":elements.$[elem].nr2/elements.$[elem].nr1} },
{ "multi": true }
);
I guess this should work

How to push data to an two-level nested array in mongo db based on nested array id?

I am working with applications using MongoDB, nodeJs. I have a document with 2 level nested data on it. I want to push data of a 2-level nested array based on a 1-level "uuid". I will attach my document picture and you will know exactly what I wanted.
Node code:
await collection.update(
{
uuid: req.query.sub_uuid
},
{
$push: { subtests.uuid: req.body }
});
await test.save();
Thanks in Advance.
You have to use $arrayFilters that you can put some constraint on your update element inside the array.
db.collections.updateMany({
"subtests.uuid": 'your_uuid'
},
{
$set: {
{ $push: { "subtests.$[el].subtrests": { /*item you want to push in the array */ } } }
}
},
{
arrayFilters: [
{
"el.uuid": 'your_uuid'
}
]
})
with this approach, you can update only the element who have your uuid.

How to upsert an element in mongodb array?

Suppose our document looks like this
{
a:1,
b:[
{c:120,d:100},
{c:121,d:110}
]
}
Now how could I upsert new objects in this array?
Suppose I want to perform update on the above document and add {c:200,d:120} to b so my expected result looks like this
{
a:1,
b:[
{c:120,d:100},
{c:121,d:110},
{c:200,d:120}
]
}
Also the update will of $inc, meaning suppose I want to increment d by 200 if c is present(lets say c is 200 and it is already present in the above document), if not present then I want to upsert the document itself.
Any help would be much appreciated.
It can be achieved using the $push update command.
db.<Collection-Name>.updateOne({"a": 1}, {"$push": {"b": {"c":200, "d":120}}})
Note: Use $addToSet if you don't want duplicates elements inside the array
UPDATE:
I don't think your precise requirement can't be achieved by a single command.
Your exact requirement can be achieved by the below script:
if (db.test9.findOne({"_id": ObjectId("5f19402abbc59a3864783fc7"), "b.c": 200}, {"_id": 1}) != null) {
db.test9.updateOne({
"_id": ObjectId("5f19402abbc59a3864783fc7"),
"b.c": 200,
}, {
"$set": {
"b.$.d": 120
}
})
} else {
db.test9.updateOne({
"_id": ObjectId("5f19402abbc59a3864783fc7"),
}, {
"$push": {
"b": {
"c":200, "d":120
}
}
});
}

Most efficient way to put fields of an embedded document in its parent for an entire MongoDB collection?

I am looking for the most efficient way to modify all the documents of a collection from this structure:
{
[...]
myValues:
{
a: "any",
b: "content",
c: "can be found here"
}
[...]
}
so it becomes this:
{
[...]
a: "any",
b: "content",
c: "can be found here"
[...]
}
Basically, I want everything under the field myValues to be put in its parent document for all the documents of a collection.
I have been looking for a way to do this in a single query using dbCollection.updateMany(), put it does not seem possible to do such thing, unless the content of myValues is the same for all documents. But in my case the content of myValues changes from one document to the other. For example, I tried:
db.getCollection('myCollection').updateMany({ myValues: { $exists: true } }, { $set: '$myValues' });
thinking it would perhaps resolve the myValues object and use that object to set it in the document. But it returns an error saying it's illegal to assign a string to the $set field.
So what would be the most efficient approach for what I am trying to do? Is there a way to update all the documents of the collection as I need in a single command?
Or do I need to iterate on each document of the collection, and update them one by one?
For now, I iterate on all documents with the following code:
var documents = await myCollection.find({ myValues: { $exists: true } });
for (var document = await documents.next(); document != null; document = await documents.next())
{
await myCollection.updateOne({ _id: document._id }, { $set: document.myValues, $unset: { myValues: 1} });
}
Since my collection is very large, it takes really long to execute.
You can consider using $out as an alternative, single-command solution. It can be used to replace existing collection with the output of an aggregation. Knowing that you can write following aggregation pipeline:
db.myCollection.aggregate([
{
$replaceRoot: {
newRoot: {
$mergeObjects: [ "$$ROOT", "$myValues" ]
}
}
},
{
$project: {
myValues: 0
}
},
{
$out: "myCollection"
}
])
$replaceRoot allows you to promote an object which merges the old $$ROOT and myValues into root level.

Delete array element having object key value mongoose

Here is my collection:
{
"_id":"5b3385af20b7dc2b008ef5b9",
"name":"C",
"distances":[{"_id":"5b3460b05b2edc1bbcb0f362",
"distance":7,
"waypoint":"5b3385af20b7dc2b008ef5b9",
"status":"available"},
{"_id":"5b3460b05b2edc1bbcb0f361",
"distance":4,
"waypoint":"5b3460a15b2edc1bbcb0f360",
"status":"available"}],
"createdAt":"2018-06-27T12:40:15.457Z",
"updatedAt":"2018-06-27T12:57:50.191Z",
"__v":0
}
Let's focus on the distances array only, so which is:
"distances":[{"_id":"5b3460b05b2edc1bbcb0f362",
"distance":7,
"waypoint":"5b3385af20b7dc2b008ef5b9",
"status":"available"},
{"_id":"5b3460b05b2edc1bbcb0f361",
"distance":4,
"waypoint":"5b3460a15b2edc1bbcb0f360",
"status":"available"}]
What i want to do is, I want to delete object and update the distances array which have "waypoint":"5b3460a15b2edc1bbcb0f360"
so far I have tried:
Model.update( {'_id': model._id}, { $pullAll: {distances: [{'waypoint': req.body.id}] } });
this isn't working. Please suggest a way out.
You can use $pull of MongoDB
db.collection.update(
{ },
{ $pull: { distances: { waypoint: req.body.id} } },
)
{multi: true}: adding this in above query will delete all entries matching { waypoint: req.body.id}