Mongoose query via populate - node.js

I have 2 models.
Model 1:
const userSchema = new mongoose.Schema({
email: { type: String, unique: true, required: true },
password: { type: String, required: true },
passwordResetToken: String,
passwordResetExpires: Date,
facebook: String,
twitter: String,
tokens: Array,
profile: {
name: String,
gender: String,
location: String,
website: String,
picture: String
}
}, { timestamps: true });
Model 2:
const reviveSchema = new mongoose.Schema({
reviveShowName: {type: String, required: true},
reviveTitle: {type: String, required: true},
reviveCategory: {type: String, required: true},
reviveGoal: {type: Number, required: true},
revivePhoto: {type: String, required: true},
reviveVideo: {type: String},
reviveStory: {type: String},
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
}
}, { timestamps: true });
How can I pass the author's name to the show view of a particular revive?
This is how I was getting to the show view before I realized that I needed the author's data to be passed to the view as well:
exports.showRevive = (req, res, next) => {
Revive.findById(req.params.id, (err, foundRevive) => {
if(err) {
console.log(err);
} else {
res.render('revive/show_revive', {revive: foundRevive});
}
});
};
That works just fine but then to get the author's data in the revive show view as well I tried this:
exports.showRevive = (req, res, next) => {
Revive.findById(req.params.id)
.populate('author')
.exec(function(err, foundRevive) {
if(err) {
console.log(err);
} else {
res.render('revive/show_revive', {revive: foundRevive});
}
});
};
That did not work... Can you guys please point me in the right direction? Thanks!

Related

NodeJS - Mongoose Populate Nested Items

I am trying to populate a GET request.
This my model/schema:
const turnosSchema = mongoose.Schema({
turno: { type: Number, required: true},
nombre: { type: String, required: true},
hora_inicio: { type: String, required: true},
hora_fin: { type: String, required: true},
menus: { type: Array, required: true},
cod_vestir: { type: mongoose.Schema.Types.ObjectId, ref: 'cod_vestir', required: true},**//This is the item I want to populate with another collection called "codsvestirs"**
})
const LandingAyBSchema = mongoose.Schema({
titulo: { type: String, required: true},
especialidad: { type: String, required: true},
descripcion: { type: String, required: true},
observaciones: { type: String, required: true},
turnos: [turnosSchema],**//This is an array with a "turnosSchema"**
orden: { type: Number, required: true},
activo: { type: Number, required: true},
imagen: {type: String, required: true},
lang: { type: String, required: true, maxLength: 2},
})
Heres is a pic of the data hierarchy.
Here is the code I have so far. It doesn't have the actual populate code cause I have not been able to make it work. I've tried the solution from this thread which I think better suits my scenario but it didn't work Populate nested array in mongoose
exports.getAllLandingAyB = async (req, res, next) => {
const query_result = await LandingAyB.find({activo: 1}).sort({orden: 1});
if (!query_result) {
res.status(500).json({success: false})
}
res.status(200).json({
success: true,
cuantosLandings: query_result.length,
data: query_result
});
}
You need to set the type as a mongoId and then refer to the selected collection. For example on the turnos property
turnos: [{ type: Schema.Types.ObjectId, ref: 'Turnos' }]
I'm asumming the Turnos is the name of the collection, and the query shoud look like this
const query_result = await LandingAyB.find({activo: 1})
.populate('turnos')
.sort({orden: 1});.
you can find a good example on the populate documentation

Mongoose How to sort .populate field

I'm work with an user/articles profile system. I have been using the .populate() to render the posts but I cannot get the articles sorted by the date they were created.
I am using the createdAt variable as the main way of ordering the posts displayed.
For reference:
router.get('/:id', async (req, res) => {
const user = await User.findById(req.params.id, function(error) {
if(error) {
req.flash("error", "something went wrong")
res.redirect("/");
}
}).populate('articles')
res.render('users/show',{
user: user
});
and the article.js:
const ArticleSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
author: {
type: String
},
markdown: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
},
slug: {
type: String,
required: true,
unique: true
},
sanitizedHtml: {
type: String,
required: true
},
img: {
type: String
},
type:{
type: String
},
user : { type: Schema.Types.ObjectId, ref: 'User' },
}, {timestamps: true});
In advance thank you all for the help.
There is a property called options in populate,
.populate({
path: 'articles',
options: { sort: { createdAt: -1 } }
})

How to filter documents on mongodb based on reference populated subdocuments?

I am trying to grab documents based on populated subdocuments.
Here are my models
// User model
var UserSchema = new mongoose.Schema({
username: {type: String, required: true, trim: true},
firstName: {type: String, required: true, lowercase: true},
lastName: {type: String, required: true, lowercase: true},
phone: {type: String, required: false},
email: {type: String, required: true},
password: {type: String, required: true},
blogs: {type: mongoose.Schema.Types.ObjectId, ref: 'Blogs'}
}, {timestamps: true});
// Blog Model
var BlogSchema = new mongoose.Schema({
description: String,
tags: [String],
other: [Object],
}, {timestamps: true});
This is how I am grabbing documents
fetchAllByFilter: async function(req, res) {
try {
let result = await Users.find({}).populate('blog');
return res.status(200).send(result);
} catch (err) {
return res.status(200).send({error: err});
}
},
Now my main question is, how would I grab Users based on their Blogs referenced documents?
For example, Find Users with Blogs that has Blog.tags of "food", "cars", "movies" and/or Blog.other of [{...SomeObject}, {...SomeOtherObject}]
looking at mongo docs match an array, you could make a utility function somewhat like this...
async function findByTag(tag) {
const blogIds = await Blog.find({ tags: tag }).select("_id");
const users = await User.find({
blogs: { $in: blogIds.map((blog) => blog._id) }
}).populate("blog");
}

Mongodb/Mongoose: one field will not updated by empty string

i am new in MongoDB/Mongoose and have the following problem:
my schema is nested with:
profile: {
school: {type: String, required: false},
subject: {type: String, required: false},
graduation: {type: String, required: false}
}
now i want to update school, subject and graduation with:
userRouter.put('/:id', function(req, res, next){
var updateObject = req.body;
User.findByIdAndUpdate(req.params['id'], updateObject,{upsert: true}, (err, success)=> {
if(err)
res.status(404).send(err);
res.status(200).send();
});
});
i know the put method replace the object, but the patch method doesn't run in my code. Now, when i sent:
{"school": "", "subject": "c", "graduation": ""}
subject and graduation will be overwritten, but school will not be empty - it contains the old stuff.
Do you have a solution? Thanks.
Tested like this school and graduation were modified...
let updateObject = {
profile: {school: "", subject: "c", graduation: ""}
};
user.findByIdAndUpdate(
req.params['id'],
updateObject,
{upsert: true},
function(err, success) {
if (err)
res.status(404).send(err);
res.status(200).send();
}
);
Used this testing schema:
new mongoose.Schema({
created: {type: Date, required: true, default: Date.now},
modified: {type: Date, required: true, default: Date.now},
profile: {
school: {type: String, required: false},
subject: {type: String, required: false},
graduation: {type: String, required: false}
}
},
{
collection: 'db_test',
strict: 'throw',
toJSON: {getters: true},
toObject: {getters: true}
}
);

Using SchemaTypes other than ObjectId for Population (Mongoose)

I am relatively new to node.js and mongoose, so I've been trying to get population working for mongoose. According to the official mongoose documentation, Strings can be used as refs:
Note: ObjectId, Number, String, and Buffer are valid for use as refs.
Based on this, I've been trying to populate a field that is of type String, rather than ObjectId. However, every time I try to do so, I get a CastError:
CastError: Cast to ObjectId failed for value "testuser" at path "_id"
If it helps, here's my code:
/=============== In user.js ===============/
var UserSchema = new mongoose.Schema({
_username: { type: String, required: true, unique: true },
password: { type: String, required: true },
date_created: { type: Date, required: true, default: Date.now() },
last_accessed: { type: Date, required: true },
admin_access: { type: Boolean, required: true }
});
UserSchema.post('save', function(user) {
// Settings Model
var Settings = require('./settings');
var setting = new Settings.SettingsModel({
_user: user._username
});
setting.save(function(err) {
if (err) throw err;
console.log('Successfully created settings for user: ' + setting);
Settings.SettingsModel.findOne({ _user: user._username }, function(err, setting) {
Settings.SettingsModel.populate(setting, { path: '_user' }, function(err, setting) {
if (err) throw err;
console.log('User of setting is ' + setting._user);
});
});
});
});
/=============== In settings.js ===============/
var SettingsSchema = new mongoose.Schema({
_user: { type: String, ref: 'User', unique: true },
defaultCurrency: { type: String, default: 'USD', required: true },
secondaryCurrency: { type: String }
});
Basically, settings for a user is created after a user has been created.
Any help is appreciated. Thanks!
While you can use types other than ObjectId for refs, the refs can't reference any field besides _id. So to do this you'd have to declare _id of UserSchema as a String and set its value to the same value you give username:
var UserSchema = new mongoose.Schema({
_id: { type: String },
_username: { type: String, required: true, unique: true },
password: { type: String, required: true },
date_created: { type: Date, required: true, default: Date.now() },
last_accessed: { type: Date, required: true },
admin_access: { type: Boolean, required: true }
});

Resources