how can I get one document in mongoose(mongoDB)? - node.js

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

Related

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

Transform a mongoDB cursor to JSON

I have a mongoDB database set up with a express server.
I try to access a object which is inside an array in a document.
I have this route :
app.get("/api/" + BLOGS_COLLECTION + "/:blogId" + "/post" + "/:postId", function (req, res) {
db.collection(BLOGS_COLLECTION).aggregate([
{ $match: { _id: req.params.postId } }, {
$project: {
posts: {
$filter: {
input: "$posts",
as: "posts",
cond: { $eq: ["$$posts._id", req.params.postId] }
}
}
}
}].toArray((err, doc) => {
console.log(doc)
if (err) {
handleError(res, err.message, "Failed to get post");
} else {
res.status(200).send(doc);
}
}));
});
But it returns an error :
[{(intermediate value)},{(intermediate value)}].toArray is not a function
If I do not use the toArray but a plain function, it can't construct to json to send it to the front.
I do not use Mongoose.
If the code above can't work, how can I query only the object I want inside the array ?
P.S: this is how my database is made :
_id:60336bcc961785586471938b
title:"<p>ttttttttttttttttttttttttttt</p>"
description:"<p>tttttttttttttttttttttttttt</p>"
authorId:"60336bb5961785586471938a"
createDate:2021-02-22T08:31:08.889+00:00
posts:
0:
_id:"53394732-d60b-c869-1fed-1fb82c03780f"
title:"<p>iiiiiiiiiiiiiiiiiiiiiiiiiiiii</p>"
content:"<p>iiiiiiiiiiiiiiiiiiii</p>"
date:"2021-02-22T08:31:14.911Z"
You need to call toArray on the cursor reference you get back from the aggregation and not on the aggregation array:
db.collection(BLOGS_COLLECTION).aggregate([
{ $match: { _id: req.params.postId } },
{
$project: {
posts: {
$filter: {
input: "$posts",
as: "posts",
cond: { $eq: ["$$posts._id", req.params.postId] }
}
}
}
}
],
(err, cursor) => {
if (err) {
handleError(res, err.message, "Failed to get post");
} else {
cursor.toArray((error, documents) => {
if (error) { return handleError(res, error.message, "Failed to get post"); }
console.log(documents)
res.status(200).send(documents);
});
}
});
MongoDB Aggregation Pipeline
Your stages between square prackets should look like this:
[
{ $match: { _id: ObjectId( req.params.blogId) } },
{
$project: {
posts: {
$filter: {
input: "$posts",
as: "posts",
cond: { $eq: ["$$posts._id", ObjectId(req.params.postId)] }
}
}
}
},
{$unwind : "$posts" },
{$replaceRoot :{ newRoot:"$posts"}
]
After filtering and projecting, unwind array objects and then simply replace the root. And of course, follow the instructions from the previous answer about the usage of toArray on aggregate cursor result.

How to remove objects from an array within a mongoose model

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)} } } )

Delete id within nested array

So far I can only manage to delete the first id (in this case the id with "12345").
Im trying to delete the row with id 2 within books-array
Libary Table:
{
"_id": {
"$oid": "12345"
},
"libaryName": "A random libary",
"Books": [
{
"_id": {
"$oid": "1"
}
"bookTitle": "Example",
"TotalPages": "500"
},
{
"_id": {
"$oid": "2"
}
"bookTitle": "Delete Me",
"TotalPages": "400"
}
]
}
My delete code:
router.delete('/:id', (req, res) => {
Libary.remove({ _id: req.params.id })
.then(() => {
//redirect
});
});
How can I reach and delete the book row where the id is 2?
You need to use $pull opertator
router.delete('/:id', (req, res) => {
Libary.update({ _id: req.params.id }, //This is the Id of library Document
{ $pull: { "Books": {"_id":2) } } }) // This will be the Id of book to be deleted
.then(() => {
//redirect
});
});
Hope it helps.
You need to use $pull:
Library.update(
{ },
{ $pull: { Books: { _id: 2 } } }
)

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