How to pull items from reference array based on condition in array? - javascript

I am working in creating a todo list and one of the action I want users to do is delete all completed todos in one click. I have my models here, and the code I have, I have been reading trying to figure it out, but can't find it anywhere. Thanks in advance.
User Model
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
todos: [{
type: mongoose.Types.ObjectId,
ref: 'Todo'
}]
});
Todo model:
const TodoSchema = new mongoose.Schema({
creator: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
content: {
type: String,
required: true,
},
completed: {
type: Boolean,
default: false
},
});
This is what I have now. I get a "Cast to ObjectId failed for value true at path todos.
router.delete('/delete/completed', auth, async (req, res) => {
try {
const userTodos = await User.update(
{'_id':req.user.id},
{ $pull: { 'todos': { 'completed': true } } },
{ multi: true }
);
if (!userTodos) {
return res.status(400).send('Server error');
}
return res.json({ userTodos });
//return res.json({msg: 'All completed removed'})
} catch (err) {
console.error(err.message);
return res.status(404).json({ msg: 'Something went wrong, try again' });
}
});

If you are (as it seems from your code) using mongoose, you could use mongoose's populate feature:
const userTodos = await User.find(
{'_id':req.user.id}).populate('todos', {
match: {completed: true}
});
please note, however, that you'll need to delete both the documents in the todos collection, AND the todo reference in the user's todos array. You may consider to remove one side of the reference, see the pros and cons of two-way referencing here

Related

Mongoose does not populate

I am trying to populate one of my models but it does not work.
This is my Card schema:
const CardSchema = new mongoose.Schema({
text: {
type: String,
},
wrap: {
type: String,
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
}
});
module.exports = mongoose.model('Card', CardSchema);
This is my controller:
exports.getCards = asyncHandler(async (req, res, next) => {
const cards = await Card.find({}).populate('user').exec();
res.status(200).json({
success: true,
count: cards.length,
data: cards,
});
});
It does return the cards but without any user field.
The user schema is exported as "User"
You have made a small mistake in defining the models while you referring the User Collections, remove the single quotes
The model definition should be as follows
const CardSchema = new mongoose.Schema({
text: {
type: String,
},
wrap: {
type: String,
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: User, // Replace 'User' with User
}
});
module.exports = mongoose.model('Card', CardSchema);

How do I reference mongoose model to another model?

I have a mongoose schema for stories that looks like this:
{
id: {
type: Number,
default: 0
},
title: {
type: String,
maxLength: 60
},
author: {
userid: {
type: Number
},
username: {
type: String
}
}
chapters: [chapter],
numchapters: {
type: Number,
default: 1
},
favs: {
type: Number,
default: 0
},
completed: {
type: Boolean,
default: false
}
}
What I'm trying to do is reference a document in a separate collection (users), and use the values of its userid and username fields in the author field.
how do I do this?
current code:
storyobj.populate('author', {path: 'author', model: 'users', select: 'userid username'}, (err) => {
if (err) {
console.log(err)
}
})
just in case it's relevant, the structure of the users collection looks like this:
{
username: {
type: String,
},
email: {
type: String,
},
password: {
type: String,
},
userid: {
type: Number
},
isAdmin: {
type: Boolean,
default: false
},
banned: {
type: Boolean,
default: false
}
}
EDIT:
I've changed the author field in the Stories model to look like this:
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
This is so I tell Mongoose, "Hey, I want this field to reference a user in the User collection".
Here are some more details that I hope will be of help.
Full code:
var storydb = require('../models/stories/story');
var chapterdb = require('../models/stories/chapter');
var userdb = require('../models/user');
const file = JSON.parse(fs.readFileSync('test.json')); // this is a file with the data for the stories I am trying to insert into my database
for (const d in file) {
var storyobj = new storydb({
id: d,
chapters: []
});
for (let e = 0; e < file[d].length; e++) {
var abc = file[d][e];
var updatey = {
chaptertitle: abc.chapter,
chapterid: parseInt(abc.recid),
words: abc.wordcount,
notes: abc.notes,
genre: abc.gid.split(', '),
summary: abc.summary,
hidden: undefined,
loggedinOnly: undefined,
posted: new Date(Date.parse(abc.date)),
bands: abc.bandnames.split(', ')
};
var kbv = getKeyByValue(userlookup, abc.uid);
storyobj.title = abc.title;
storyobj.numchapters = file[d].length;
storyobj.favs = file[d][0].numfavs;
updatey.characters = abc.charid.split(/, |,/g);
storyobj.chapters.push(updatey)
}
storyobj.save();
}
In file, there's a unique ID representing the author of each story. kbv returns the userid associated with that unique ID (note that they're NOT the same).
Now, here's where I'm having trouble:
What I want to do is find a user matching the userid in kbv, and make that the author property in the story model.
The code I'm currently using to try and achieve that:
storydb.findOne({storyobj}, 'author').populate("author", (f) => console.log(f));
const Stories = require("./path/to/model");
Stories
.find({ /* query */ }, { /* projection */ })
.populate("author.username", ["userid", "username"])
.then(/* handle resolve */)
.catch(/* handle rejection */)
For this to work, you have to add a ref key to the userid key in your model, where the ref value is the name of the model it's referencing.
Story.model.js
const StorySchema = new Schema({
author: {
userid: { type: Schema.Types.ObjectId, ref: "users", required: true },
/* other props */
}
/* other props */
});

How can I update an index object array by id

I have the object array's ID and I want to update the info with information I already have on my backend.
My code deletes every array and creates a new one with the new info.
I want to access the array with the variable ID and then change it's values:
const ProdcutSchema = new mongoose.Schema({
name:{
type: String,
required: true
},
productDescription:{
type: String,
required: true
},
pricePerUnit:{
type: Number,
required: true
},
productAmmount:{
type:Number,
required: true
},
/*productImagePath:{
type:String,
required: true
}*/
});
const UserSchema = new mongoose.Schema({
name:{
type: String,
},
email:{
type: String,
},
password:{
type: String,
},
date:{
type: Date,
default: Date.now
},
products:[ProdcutSchema]
});
//Update products
router.put('/dashboard/:id', (req, res)=>{
const ID = req.params.id;
const {product_name, price_PerUnit, product_Description, product_Ammount} = req.body; //Get access to ajax data using body parser
if(!product_name || !price_PerUnit || !product_Description || !product_Ammount){
res.send('Please fill out all fields with valid content');
}else{
User.products.findOneAndUpdate(
{ _id : ID },
{ $set: { products: {
name :product_name,
productDescription : product_Description,
pricePerUnit : price_PerUnit,
productAmmount : product_Ammount
} } },
(err) => {
if (err) throw console.log('found errors');
console.log('no errors');
})
}
});
If you have ObjectId of that item you want to update, code should look like this:
User.products.findOneAndUpdate(
{ _id: ID, "products._id": <here goes id for an array element> },
{
$set: {
"products.$": {
name: product_name,
productDescription: product_Description,
pricePerUnit: price_PerUnit,
productAmmount: product_Ammount
}
}
},
err => {
if (err) throw console.log("found errors");
console.log("no errors");
}
);
Also u gotta be aware that u need to supply an specific ID of an array element for this kind of situation when you want to update subdocument.

Can't remove the user id from upvotes section

I've tried almost everything splice, pop, shift, remove but I can't remove the user id which simple means downvote the post.
Here is my code:
// #type POST
// #route /api/question/upvote/:id
// #desc route for upvoting answers to questions
// #access PRIVATE
router.post('/upvote/:id', passport.authenticate('jwt', {session:false}), (req, res)=> {
Profile.findOne({user: req.user.id})
.then(profile => {
Question.findById(req.params.id)
.then(question => {
if(question.upvotes.filter(upvote => upvote.user.toString() === req.user.id.toString()).length > 0){
return res.status(400).json({noUpvote : 'User is downvoted the question'});
}
question.upvotes.unshift({user : req.user.id});
question.save()
.then(question => res.json(question))
.catch(err => console.log('Error on saving the upvote user id :' + err));
})
.catch(err => console.log('Error on getting the question : ' + err));
})
.catch(err => console.log('Error on finding the user : ' + err));
});
There are the three models in my application:
//Load the Person Model
const Person = require('../../models/Person');
//Load the Profile Model
const Profile = require('../../models/Profile');
//Load the Questions Model
const Question = require('../../models/Questions');
So the upvote is in question model.
The Person model contains the registration and login information.
The Profile model contains the Person details. The Question model contains question, answer, comment, and upvotes.
Here is the question model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const QuestionSchema = new Schema({
user : {
type: Schema.Types.ObjectId,
ref: 'myPerson'
},
textone: {
type: String,
required: true
},
texttwo: {
type: String,
required: true
},
name:{
type: String
},
upvotes: [{
user : {
type: Schema.Types.ObjectId,
ref: 'myPerson'
}
}],
answers:[{
user : {
type: Schema.Types.ObjectId,
ref: 'myPerson'
},
answer: {
type: String,
required: true
},
name:{
type: String
},
date: {
type: Date,
default: Date.now
}
}],
comments: [{
user : {
type: Schema.Types.ObjectId,
ref: 'myPerson'
},
name:{
type: String
},
text: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
}],
date: {
type:Date,
default: Date.now
}
});
module.exports = Questions = mongoose.model("myQuestion", QuestionSchema);
you can remove the id using filter.
question.upvotes = question.upvotes.filter(upvote => upvote.user.toString() !== req.user.id.toString());
question.save();
add it inside the if condition if this is where you want to remove it.

Usage of mongoose deep-populate unclear

I would like to use the deep-populate plugin for mongoose queries. Most certainly I am to unexperienced but, I don't success to deep-populate something.
Here's my scheme:
var mongoose = require('mongoose');
var MMAnswersSchema = new mongoose.Schema({
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
item: { type: mongoose.Schema.Types.ObjectId, ref: 'MMItem'},
answer: [
{
skalaAnswer: Number,
freitextAnswer: String,
multipleChoiceAnswer: String,
answered_at: { type: Date, required: true, default: Date.now }
}
],
last_answered: { type: Date, required: true, default: Date.now }
});
MMAnswersSchema.pre('save', function(next) {
this.last_answered = Date.now;
next();
});
mongoose.model('MMAnswer', MMAnswersSchema);
var deepPopulate = require('mongoose-deep-populate')(mongoose);
MMAnswersSchema.plugin(deepPopulate);
Did I put the deepPopulate require stuff at the right place?
I use this route now to get my stuff:
// Alle Antworten des aktuellen Users laden
// Verwendet von der Historie
.get('/mm-answers/:user_id/get-all-answers', function (req, res, next) {
Answer.find({user:req.params.user_id}, function (err, answer) {
if(err){
return next(err);
}
res.json(answer);
})
.deepPopulate('item')
})
I want that it replaces the id with the actual item and the item itself contains a 'ofBox'-field which I also want to be replaced with the actual box document (thats why I use deep-populate).
Using the query above simply doesn't give me any result. I still have the ID in the item field.
If I run the standard .populate('item') command, it replaces the item ID with the item itself but unfortunately not the ofBox ID inside the item. (thats why I need deep-populate).
Can someone help me to figure it out? I guess I made a mistake in importing/ registrating the plugin correctly...-.-
You need to put the deepPopulate call before you execute the query, something like this:
// Alle Antworten des aktuellen Users laden
// Verwendet von der Historie
.get('/mm-answers/:user_id/get-all-answers', function (req, res, next) {
Answer.find({ user: req.params.user_id }).deepPopulate('item').exec(function (err, answer) {
if (err) {
return next(err);
}
res.json(answer);
});
});
In the model you just need to add the fields you wanna populate when you add the plugin:
const mongoose = require('mongoose');
const deepPopulate = require('mongoose-deep-populate')(mongoose);
const MMAnswersSchema = new mongoose.Schema({
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
item: { type: mongoose.Schema.Types.ObjectId, ref: 'MMItem'},
answer: [
{
skalaAnswer: Number,
freitextAnswer: String,
multipleChoiceAnswer: String,
answered_at: { type: Date, required: true, default: Date.now }
}
],
last_answered: { type: Date, required: true, default: Date.now }
});
MMAnswersSchema.pre('save', function(next) {
this.last_answered = Date.now;
next();
});
MMAnswersSchema.plugin(deepPopulate, {
populate: {
user: {
select:'_id name lastName'
},
item: {
select:'_id name'
},
});
const MMAnswers = mongoose.model('answers',MMAnswersSchema);
module.exports = { MMAnswers };
Finally when you need to use the deep population JUST add whatever field you wanna populate
await MMAnswers.findOne({your search}).deepPopulate('user').exec();
or
await MMAnswers.findOne({your search}).deepPopulate('user item').exec();

Categories