How to remove objects from an array within a mongoose model - node.js

I am trying to remove multiple objects that are in an array in mongoose. My Workout model look like this:
{
_id: 5e04068491a2d433007026cd,
exercises: [
{ _id: 5e0401b9dda7ea28a70e99ed, reps: '1', sets: '3' },
{ _id: 5e0401cadda7ea28a70e99ee, reps: '1', sets: '3' },
{ _id: 5e0401dbdda7ea28a70e99ef, reps: '1', sets: '3' }
]
}
I have an array of id's, named deletedExercises, these are the ids of the objects that I want removed from the exercise list. I am trying to loop through deletedExercise and remove any exercises that match the id of the deletedExercise item.
router.put("/:workoutId", (req, res)=>{
deletedOnes = req.body.exerciseId
deletedExercises = []
if(typeof deletedOnes === 'object'){
deletedOnes.forEach(item => {
deletedExercises.push(item)
})
} else {
deletedExercises.push(deletedOnes)
}
deletedExercises.forEach(item => {
Workout.findByIdAndUpdate( req.params.workoutId,
{ $pull: { exercises: { _id: item} } } )
});

You can simply delete exercises using the $in operator inside $pull like this:
router.put("/:workoutId", (req, res) => {
console.log(req.body.exerciseId); //[ '5e05c5306e964f0a549469b8', '5e05c5306e964f0a549469b6' ]
Workout.findByIdAndUpdate(
req.params.workoutId,
{
$pull: {
exercises: {
_id: {$in: req.body.exerciseId}
}
}
},
{ new: true }
)
.then(doc => {
res.send(doc);
})
.catch(err => {
console.log(err);
res.status(500).send("Error");
});
});
Let's say we have this workout with 3 exercises:
{
"_id": "5e05c5306e964f0a549469b5",
"exercises": [
{
"_id": "5e05c5306e964f0a549469b8",
"reps": 8,
"sets": 4
},
{
"_id": "5e05c5306e964f0a549469b7",
"reps": 10,
"sets": 3
},
{
"_id": "5e05c5306e964f0a549469b6",
"reps": 12,
"sets": 2
}
],
}
If we want to remove the exercises 5e05c5306e964f0a549469b8 and 5e05c5306e964f0a549469b6 for this 5e05c5306e964f0a549469b5 workout, we can send a PUT request with this body: (url must end something like this http://.../5e05c5306e964f0a549469b5)
{
"exerciseId": [
"5e05c5306e964f0a549469b8",
"5e05c5306e964f0a549469b6"
]
}
The response will be:
{
"_id": "5e05c5306e964f0a549469b5",
"exercises": [
{
"_id": "5e05c5306e964f0a549469b7",
"reps": 10,
"sets": 3
}
]
}

Hard to tell considering you're not saying what error you are getting, but my guess from looking at it is that you are comparing an ObjectId with a String, try to replace this line:
{ $pull: { exercises: { _id: item} } } )
with this:
{ $pull: { exercises: { _id: new ObjectId(item)} } } )
** EDIT **
you probably need to also convert the main ID you are searching for to an ObjectId:
Workout.findByIdAndUpdate( new ObjectId(req.params.workoutId),
{ $pull: { exercises: { _id: new ObjectId(item)} } } )

Related

mongoose divide two fields in put request

Can I update a field of a document with a division of two fields? Using Node and MongoDB, I'm trying to create a rating function, and I have to make a division, but nothing seems to work. I want the new value of rating to be, the current one divided by the number of votes.
router.put("/:id/:rating", async (req, res) => {
const movie_rating = parseInt(req.params.rating);
try {
const updatedMovie = await Movie.findByIdAndUpdate(
req.params.id,
{
$inc: { noVotes: 1 },
$inc: { rating: movie_rating },
$divide: { rating: [rating, noVotes] },
// rating: { $divide: [rating, noVotes] }
},
{ new: true }
);
res.status(200).json(updatedMovie);
} catch (err) {
res.status(500).json(err);
}
});
You need to change few things
Sample
db.collection.update({},
[
{
"$set": {
"key2": {
$add: [
"$key2",
1
]
},
key3: {
"$divide": [
{
$add: [
"$key2",
1
]
},
"$key"
]
},
}
}
],
{
"multi": true,
"upsert": false
})
You need aggregate update as you need divide
You cannot use the updated value in the same operation
You cannot combine $inc, $set in aggregate update
Alternatively, you can use $add instead $inc
you can reperform the operation for the divide operation than making another update call
This can be done with $set,
It will look like this:
router.put("/:id/:rating", async (req, res) => {
const movie_rating = parseInt(req.params.rating);
try {
const updatedMovie = await Movie.findByIdAndUpdate(
req.params.id,
[
{
$set: {
noVotes: { $sum: ["$noVotes", 1] },
rating: { $sum: ["$rating", movie_rating] },
averageRating: { $divide: ["$rating", "$noVotes"] },
},
},
],
{ new: true }
);
res.status(200).json(updatedMovie);
} catch (err) {
res.status(500).json(err);
}
});

MongoDB: pullAll / pull whole nested array object by value using mongoose

I want to pull the whole nested array object if the object contains a specified string for a specific key. I'm using mongoose with nodejs:
DB before deletion:
{ _id : 1234
fallBackData: {
nestedKey: [ { arrayKey: "theValue" }, { arrayKey: "anotherValue" } ]
}
}
DB after deletion:
{ _id : 1234
fallBackData: {
nestedKey: [ { arrayKey: "anotherValue" } ]
}
}
I took a look at How can I pull nested object value in mongodb and $pullAll Mongo DB docs ,tried the following, but none worked:
const ad = await Ad.updateOne(
{ _id: 1234 },
{
$pullAll: {
fallbackData: { nestedKey: [{ arrayKey: "theValue"}] },
},
}
);
const ad = await Ad.updateOne(
{ _id: 1234 },
{
$pullAll: {
"fallbackData.$.nestedKey" : { arrayKey: "theValue" },
},
}
);
const ad = await Ad.updateOne(
{ _id: 1234 },
{
$pullAll: {
"fallbackData.$.nestedKey" : [{ arrayKey: "theValue"}],
},
}
);
The query return value is the following, but the object in the array is not deleted:
{
acknowledged: true,
modifiedCount: 1,
upsertedId: null,
upsertedCount: 0,
matchedCount: 1
}
You can achieve this by changing a little
playground
db.collection.update({
_id: 1234,
"fallBackData.nestedKey": {
$elemMatch: {
"arrayKey": "theValue"
}
}
},
{
"$unset": {
"fallBackData": "nestedKey"
}
})
You cannot add matching conditions with $pullAll related to Array
$pullAll expects an array of matching values to be removed
You can do $pull instead of $unset but $pull results empty array
The answer from #Gibbs removes the array completely, while you asked only to pull the specific object from it.
Here is the simple solution with $pull command:
db.collection.update({
_id: 1234,
"fallBackData.nestedKey": {
$elemMatch: {
"arrayKey": "theValue"
}
}
},
{
$pull: {
"fallBackData.nestedKey": {
arrayKey: "theValue"
}
}
})
Mongo Playground link
pullAll matches the entire object (or objects) to be pulled. In other words, if the input was:
[
{
_id: 1234,
fallBackData: {
nestedKey: [
{
arrayKey: "theValue",
foo: "bar"
},
{
arrayKey: "anotherValue",
foo: "baz"
}
]
}
}
]
Then you need to do:
db.collection.update({
_id: 1234
},
{
$pullAll: {
"fallBackData.nestedKey": [
{
arrayKey: "theValue",
foo: "bar"
}
]
}
})
See https://mongoplayground.net/p/iJkfqIWK0JO.
On the other hand, $pull can match objects based on a condition, so you can pull from the array based on the specific key you want to match. So, given the same input as above, you would simply do:
db.collection.update({
_id: 1234
},
{
$pull: {
"fallBackData.nestedKey": {
arrayKey: "theValue"
}
}
})
See https://mongoplayground.net/p/MOuSmh7Ir7b.
The conditions can be more complex than a simple field value match. For example to match and pull multiple keys:
db.collection.update({
_id: 1234
},
{
$pull: {
"fallBackData.nestedKey": {
"arrayKey": {
$in: [
"theValue",
"anotherValue"
]
}
}
}
})
See https://mongoplayground.net/p/iSMVxp7a9TX.

Remove one field from mongoDB collection

I have a mongoDB collection which I use with a mongoose Schema :
const balanceSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId, ref: 'user'
},
incomes: { Number },
fees: { Number },
},
{ strict: false })
I use the strict mode to false, so I can push any 'key' I want with its value.
I would like to delete just one of the "incomes" category, but I can't specify the line because there is no 'defined key'.
Here is an exemple of the data inside :
{
"_id": {
"$oid": "60c763df3d260204865d2069"
},
"incomes": {
"income1": 1300,
"anyKeyNameIWant": 400
},
"fees": {
"charge1": 29,
"charge2": 29,
"chargetest": 29,
"charge7": 29
},
"__v": 0,
}
I tried this, but no success :
module.exports.deleteOneBalance = (req, res) => {
let data = req.body
if (!ObjectID.isValid(req.params.id))
return res.status(400).send('ID unknown : ' + req.params.id);
BalanceModel.update(
{ _id: req.params.id },
{
$unset: { "incomes.salairetest": "400" }
}), (err, docs) => {
if (!err) res.send('Deleted. ' + data)
else console.log('Error : ' + err)
}
}
Any idea ?
There are several ways to delete fields with dynamic field names.
One solution is this one:
var unset = {};
unset["incomes." + "anyKeyNameIWant"] = null;
db.balanceModel.updateOne({ _id: req.params.id }, { $unset: unset })
Or you can use an aggregation pipelinie like this:
db.balanceModel.updateOne(
{ _id: req.params.id },
[
{ $set: { incomes: { $objectToArray: "$incomes" } } },
{ $set: { incomes: { $filter: { input: "$incomes", cond: { $ne: ["$$this.k", "anyKeyNameIWant"] } } } } },
{ $set: { incomes: { $arrayToObject: "$incomes" } } }
]
)
If you want to remove/unset specific value/(s) from the documents then you have to provide the complete path of that key.
Let's take an example if you want to remove anyKeyNameIWant then your path will be incomes.anyKeyNameIWant and the update query will be like this
db.sample.update(
{
_id: ObjectId("60c763df3d260204865d2069")},
{
$unset: {"incomes.anyKeyNameIWant":""}
})
In your code, you are passing an object having the key incomes in $unset which will remove the complete incomes key from the document
Here is the link to the official document in case you want more details $unset

how can I get one document in mongoose(mongoDB)?

I want to return one index's object of the array,
but when I query, It returns to me that all of the documents.
This is my Schema(userTb)
const userTbSchema = new Schema({
_id: mongoose.Schema.Types.ObjectId,
userId: String,
folders: [
{
folderTitle: String,
}
]
}
and this is the result of the query of my Schema(userTb).
{
"_id": "5fc4c13f32ab3174acb08540",
"userId": "go05111",
"folders": [
{
"_id": "5fb7b0473fddab615456b166",
"folderTitle": "first-go"
},
{
"_id": "5fb7b0473fddab615456b16b",
"folderTitle": "second-go"
}
]
}
I want to get the only { "folderTitle" : "first-go" } folder's object, like...
{
"_id": "5fb7b0473fddab615456b166",
"folderTitle": "first-go"
}
so I query like this
router.get('/folder/:folderId', (req, res, next) => {
UserTb.find({ folders : { "$elemMatch" : { _id : req.params.folderId} } })
.exec()
.then(docs => {
res.status(200).json({
docs
});
})
.catch(err => {
res.status(500).json({
error: err
});
});
});
but the result is nothing changed.
I tried a few different ways, but it didn't work out.
how can I fix it?
please help me...
Try this (live version):
UserTb.aggregate({
$match: {
"folders._id": req.params.folderId }
},
{
$project: {
folders: {
$filter: {
input: "$folders",
as: "f",
cond: {
$eq: [
"$$f.folderTitle",
"first-go"
]
}
}
},
_id: 0
}
})
It will retrieve folders:[{...}] this will be easy to tackle using JS, and quicker.
Mechanism
Match only documents containing _id:folderId
project only the inner document

Remove _Id from mongoose Aggregate response

I'm trying to remove the _Id from the returned documents, this is my code:
module.exports = function(app) {
// Module dependencies.
var mongoose = require('mongoose'),
Contacts = mongoose.models.Contacts,
api = {},
limit = 10;
api.contacts = function(req, res) {
Contacts.aggregate([{
$group: {
"_id": {
name: "$name",
city: "$city",
state: "$state"
}
}
}, {
$sort: {
AgencyTranslation: 1
}
}, {
$limit: req.query.limit | limit
}],
function(err, contacts) {
if (err) {
res.json(500, err);
} else {
res.json({
contacts: contacts
})
}
})
};
app.get('/api/contacts', api.contacts);
};
the current result-set looks like this:
{
"contacts":[
{"_id":{"name":"Joe","city":"ankorage","state":"AL"}},
{"_id":{"name":"Mark","city":"washington","state":"DC"}}
...
]
}
I tried to replace "_Id" with "$project", or $project, and adding "_Id": 0 to the object, as some have suggested elsewhere, but was not successful.
I also tried res.send(contacts), but that only stripped the super-object ('contacts').
Any suggestions are appreciated.
Like this
Contacts.aggregate( [
{ $group: { "_id": { name: "$name", city: "$city", state: "$state" } } },
{ $project: {_id: 0, name: '$_id.name', city: '$_id.city', state: '$_id.state'} },
{ $sort: { AgencyTranslation: 1 } },
{ $limit: req.query.limit | limit }
], function () {
});
Bunch of time but, here is the answer:
After making $group or $project, do this:
{ $unset: ["_id"] }

Resources