Mongoose on update saves virtual populate to document - node.js

For some reason mongoose is not updating correctly. It is adding into my Document the virtuals.
const BlogSchema: Schema = new Schema<Blog>({
clientId: {
type: Schema.Types.ObjectId
}
//...
})
ClientSchema.virtual('client', {
ref: 'clients',
localField: 'clientId',
foreignField: '_id',
justOne: true,
});
//...
return this.clientScreeningsModel
.findById(id)
.populate('client', 'email username')
.lean(true);
This is working fine.
But on the client side I say I want to update or create a new Document and I send the entire Object
eg:
{
_id: 'something',
clientId: 'previousId',
client: {
email: 'foo#bar.com',
username: 'JohnDoe'
}
}
It will save the client object as well into my Document, Does mongoose not know that it is a virtual?
Thanks in advance for any help :)

Related

How to populate an object inside an object in an array?

I am currently working on a project with mongoose, and I have been stuck in a problem with the mongoose populate() method.
The problem is that I cannot populate an object inside an object in an array.
// * I simplified the code below.
// Profile Document
Profile = model('Profile', new Schema({
_user: {
type: Schema.Types.ObjectId,
ref: 'User',
},
posts: [
{
type: Schema.Types.ObjectId,
ref: 'Post',
// Post = {
// _id: Number,
// _poster: {
// type: Schema.Types.ObjectId,
// ref: 'User',
// }
// };
},
],
}));
// app.js
const profile = await Profile.findOne({ _user: userId })
.populate('_user') // it works
.populate('posts') // it works too
.populate('posts._poster'); // it doesn't work
Is there any way to populate a nested object in an array?
It'd be great if you could answer my question. Thank you in advance.
I tried accessing the notation properly to populate *_poster*, however, it didn't still work. The code is below.
const profile = await Profile.findOne({ _user: userId })
.populate('_user')
.populate('posts');
await profile.toObject();
profile.posts.forEach((post) => {
profile.populate('posts.post._poster');
})
I have tested your schema this works great, could you test and let me know
const profile = await Profile.findOne({
_id: '5f108ed7ecc4881a6c35e27b',
})
.populate('_user')
.populate({
path: 'posts',
populate: {
path: '_poster',
model: 'User',
},
})
.exec();
console.log(profile);

How to get user name from user id in React.js

I have created this schema with mongoose
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const QuestionSchema = new mongoose.Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
question:{
type:String
},
name:{
type:String,
},
answerd:[
{
user:{
type:mongoose.Schema.Types.ObjectId,
ref:'users'
}
}
]
})
module.exports = Question = mongoose.model('question',QuestionSchema);
In my global state (REDUX) I have the state
const initialState = {
questions:[],
question:null,
loading:true,
error:{}
};
The element questions store question object which contains the name of the user who made the question, the question itself, and people who have answered.
Some where in a .js file I can get the id of users who have answered by simply
question.buzzed.map(user=> <h1> {user._id} </h1>),
but how is possible to get this user name, I also have a schema for user which have attributes such as name, id, ... etc
You should use the populate method. It is like join in SQL for mongoose, because it connects your answer to the user collection. Based on your code, it could look like this :
Question.find({}).populate("answerd")
or something like this:
Question.find().populate({ path: 'answerd', select: 'username' });
For more information please read the populate documentation
U can use $lookup.
var aggregate = [
{
$unwind: "$answerd"
},
{
$lookup: {
from: "users",
localField: "user",
foreignField: "_id",
as: "user"
}
}
];
Questions.aggregate(aggregate, function(err, users) {
})

Why does mongoose populate virtual field as array instead of single item?

I want to populate an object into a virtual field with mongoose as a JSON object, but it always returns an array with a single item.
Here is my scheme code (part with virtual field):
Order.virtual('client', {
type: 'ObjectId',
ref: 'User',
localField: 'clientId',
foreignField: '_id'
});
Here is how I do population:
Order.findOneAndUpdate({ internalStatus: 2}, { internalStatus: 3 })
.lean()
.populate({ path: 'client', select: 'email' })
.exec(function (err, order) {
//...
});
Here is what I receive in returned JSON:
{ _id: 5ad903d90443fe13b8c9061a,
client: [ { _id: 5b3755fe69635d1e942d00a8, email: 'user#user.com' } ] }
This is what I want to achieve:
{ _id: 5ad903d90443fe13b8c9061a,
client: { _id: 5b3755fe69635d1e942d00a8, email: 'user#user.com' } }
Thank you for any help or suggestions!
You have to add "justOne : true" to your virtual field config :
Order.virtual('client', {
type: 'ObjectId',
ref: 'User',
localField: 'clientId',
foreignField: '_id',
justOne : true
});
In mongoose mongoose#5.0.17 I am seeing they return as JSON_OBJECT but when I upgraded to mongoose#5.3.0 , it started retuning as JSON_ARRAY

How to rename path in response for populate

I have a query like this:
galleryModel.find({_id: galleryId})
.populate({
model: 'User',
path: 'objectId',
select: 'firstName lastName'
})
End response for objectId will be like this:
objectId: {
...
}
How can I change it to user in response without changing real path?
You can do this by virtual populate, introduced in mongoose version 4.5 . For that you need to define a virtual field in mongoose schema.
var GallerySchema = new mongoose.Schema({
name: String,
objectId: {
type: mongoose.Schema.Types.ObjectId
},
});
GallerySchema.virtual('user', {
ref: 'User',
localField: 'objectId',
foreignField: '_id'
});
Ans when you run find query, just populate it with user.
Gallry.find({_id: galleryId}).populate('user','firstName lastName').exec(function(error, gallery) {
console.log(error);
console.log(gallery);;
});
Above code is not tested in program, there may be typos, You can get more details about mongoose virtual populate on below link
http://mongoosejs.com/docs/populate.html

Populate a mongoose model with a field that isn't an id

Is it possible to populate a mongoose model with a field of a reference model that isn't the _id ... e.g. a username.
so something like
var personSchema = Schema({
_id : Number,
name : String,
age : Number,
stories : { type: String, field: "username", ref: 'Story' }
});
This is supported since Mongoose 4.5, and is called virtuals population.
You have to define your foreign keys relationships after your schemas definitions and before creating models, like this:
// Schema definitions
BookSchema = new mongoose.Schema({
...,
title: String,
authorId: Number,
...
},
// schema options: Don't forget this option
// if you declare foreign keys for this schema afterwards.
{
toObject: {virtuals:true},
// use if your results might be retrieved as JSON
// see http://stackoverflow.com/q/13133911/488666
//toJSON: {virtuals:true}
});
PersonSchema = new mongoose.Schema({id: Number, ...});
// Foreign keys definitions
BookSchema.virtual('author', {
ref: 'Person',
localField: 'authorId',
foreignField: 'id',
justOne: true // for many-to-1 relationships
});
// Models creation
var Book = mongoose.model('Book', BookSchema);
var Person = mongoose.model('Person', PersonSchema);
// Querying
Book.find({...})
// if you use select() be sure to include the foreign key field !
.select({.... authorId ....})
// use the 'virtual population' name
.populate('author')
.exec(function(err, books) {...})
It seems they enforce to use _id, and maybe we can customize it in the future.
Here is the issue on Github https://github.com/LearnBoost/mongoose/issues/2562
This is an example of using the $lookup aggregate to populate a model called Invite with the respective User based on the corresponding email field:
Invite.aggregate(
{ $match: {interview: req.params.interview}},
{ $lookup: {from: 'users', localField: 'email', foreignField: 'email', as: 'user'} }
).exec( function (err, invites) {
if (err) {
next(err);
}
res.json(invites);
}
);
It's probably quite similar to what you're trying to do.
To add to Frosty's answer, if you're looking to refer to an array of documents of another collection you would make changes like so.
BookSchema = new mongoose.Schema(
{
title: String,
authorId: [Number],
},
// schema options: Don't forget this option
// if you declare foreign keys for this schema afterwards.
{
toObject: { virtuals: true },
// use if your results might be retrieved as JSON
// see http://stackoverflow.com/q/13133911/488666
toJSON: {virtuals:true}
});
PersonSchema = new mongoose.Schema({ id: Number, name: String });
BookSchema.virtual("author", {
ref: "Person",
localField: ["authorId"],
foreignField: ["id"],
// justOne: true, // Needs to be commented out in this scenario,
});
You may use the populate() API.
The API is more flexible, you don't have to specify ref and field in the Schema.
http://mongoosejs.com/docs/api.html#document_Document-populate
http://mongoosejs.com/docs/api.html#model_Model.populate
You can mix and match with find().

Resources