How to rename path in response for populate - node.js

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

Related

Mongoose on update saves virtual populate to document

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

How to display all the posts referenced to logged in user?

I am trying to understand Mongoose's populate function and am so far stumped by available resources and the docs.
Essentially I have a schema arrangement as below, and I am trying to display all the posts referenced to a user on one page rendered with ejs.
var UserSchema = new Schema ({
username: String,
password: String,
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Post"
}
]
});
var PostSchema = new Schema ({
Title: String,
Content: String,
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
}
});
I have got passport/sessions setup, so I am able to get the id of the active user. Using this id I would like to display all of the user's posts. I have been stuck on this for hours and tried following various tutorials and different approaches, please someone put me out of my misery! How should I approach this?
Try to match the users first with the ids list (activeUserIds) then populate the posts and author fields in fetched records.
await User.find({'_id': {$in: [activeUserIds]}})
.populate({
path: 'posts',
model: 'Post',
populate: ({
path: 'author',
model: 'User'
})
});

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 populate nested array in Mongoose

I got stuck at populating my array... this is what i get from my Terminal.find() function:
{ _id: 5a7300046f18140d5081be83,
type: 5a71a22b0d4a2d0ec3b91edc,
serial: '213',
macAddress: 'E1:12:BE:82:12',
barcode: '1243w464563',
dateArrival: 2018-02-01T11:54:15.870Z,
firmware: 5a71a2030d4a2d0ec3b91edb,
installedAt: 5a61e547922c6d28e324d775,
installedAtBranch: 5a7054713fe20c0c5a94fb11,
__v: 0,
deleted: false,
activated: true }
So i want to get the correct information, when a user searches for the terminal. This is why i have to use populate(). Difficult for me now is to populate things deeper inside the array. So the first Object type has a readMethod, a layout and interfaces - i have to populate those three, but i don't know how. This is aggravated by the fact that interfaces is an Object inside my Database. So my type looks like this:
I think if someone can show me how to populate type with all of its interfaces i can achive the rest on my own.
Thanks in advance!
I found the answer on my own by searching deeper inside google:
Terminal.findOne({'barcode': barcode})
.populate({
path: 'type',
populate: [{
path: 'user',
model: 'User'
},
{
path: 'method',
model: 'Method',
},
{
path: 'layout',
model: 'Layout'
},
{
path: 'interfaces',
model: 'Interface'
}]
})
.populate('firmware')
.populate('customerId')
.populate('branchId')
.exec( function (err, terminal) {
if (err) {
res.status(500).send(err);
}
res.json({ terminal });
});
As you are using mongoose then I think your model should look like following or it must be like following
Terminal Type Schema
var terminalTypeSchema = new Schema({
articlenumber: { type: Number, default: something },
readMethod: Schema.ObjectId,
layout: Schema.ObjectId,
interfaces: [{ type: Schema.ObjectId, ref: 'User' }]
});
mongoose.model('TerminalType', terminalTypeSchema);
Interface Schema
var interfaceSchema = new Schema({
// some vars
});
mongoose.model('Interface', interfaceSchema);
Now run following query
TerminalType.findOne({ _id: terminalTypeId})
.populate('interfaces') // <==
.exec(function(err, terminalTypes){
// You can achieve the rest on your own.
});

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