MongoDB populate returning null - node.js

I am trying to populate my user schema with items but for some reason it does not populate anything in to the user schema. Could someone please take a look. I have 1 user and 1 item belonging to that user within my database but nothing is populating and I keep seeing null.
User Schema
var mongoose = require('mongoose')
var userSchema = mongoose.Schema({
name: {
type: String,
required: true
},
discordID: {
type: String,
required: true
},
discordImage: {
type: String,
required: true
},
items: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Item'
}]
})
const User = module.exports = mongoose.model('User', userSchema)
Item Schema
var mongoose = require("mongoose")
var itemSchema = mongoose.Schema({
name: {
type: String,
required: true
},
purchasedPrice: {
type: Number,
required: true
},
purchasedDate: {
type: String,
required: true
},
author: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User"
}
})
const Item = module.exports = mongoose.model("Item", itemSchema)
Populate Code
app.get("/inventory", async (req, res) => {
try {
await req.user.populate({
path: 'items'
}).execPopulate()
console.log(req.user)
} catch (error) {
console.log(error)
}
res.status(200).render("inventory.ejs", { currentUser: req.user })
})
Objects in the DB:
Item:
User:

Used virtual method on user schema to create association
userSchema.virtual("items", {
ref: "Item",
localField: "_id",
foreignField: "author"
})
Worked fine with original code

I keep seeing null.
and
no its just empty
hints there are no items added to your user. You need to have some ids you can populate.
All populate does is convert an ObjectID into a document. There is no magic that will sync itemSchema.author with userSchema.items.
Hence, it's not enough to add the author to the item. You also need to add the item to the author.
So for example, you could add an item like this:
const item = new Item({author: user._id});
await item.save();
req.user.items.push( item );
await req.user.save();
Now when you log req.user, are there any items there?
Once you see objectIds, then you can go back and add that .populate('items') into the mix and I promise you it'll work.

Related

MongoDB populate() to dynamically load/migrate data not working

I am building an app in which the user adds and deletes objects (Pic) in an array('pics') after registering, but not sure how to dynamically load or populate('pics') to userSchema to automatically render. The user registers on the app with that array originally empty ('pics' = zero), and will create or delete those objects thereafter when logged in.
Following the documentation, I used "await User.find().populate('pics');" to migrate data in index method, but did not work.
Besides, should I include 'pics' key at store method, or userSchema 'pics' should be enough?
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
pics: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Pic"
}
],
});
const picSchema = new mongoose.Schema({
thumbnail: String,
description: String,
dev: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
},
);
const User = mongoose.model('User', userSchema);
const Pic = mongoose.model('Pic', picSchema)
async index(req, res, next) {
const users = await User.find().populate('pics');
res.status(200).json(
devs
);
},
async store(req, res) {
try {
const { name } = req.body;
let user = await User.create({
name,
pics
})
// await user.populate('pics').execPopulate();
res.send({ user })
}
} catch (error) {
res.status(400).send(error);
}
},
I worked a time ago with MongoDB and NodeJS. I think that you have a problem with the definitions. Also, you can read the documentation https://mongoosejs.com/docs/populate.html
You need to define the _id for collections (Schema).
const userSchema = new mongoose.Schema({
_id: new mongoose.Types.ObjectId(),
name: {
type: String,
required: true,
trim: true
},
pics: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Pic"
}
],
});
const picSchema = new mongoose.Schema({
_id: new mongoose.Types.ObjectId(),
thumbnail: String,
description: String,
dev: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
},
);
So, when you create a new User, the _id is completed (you can generate it or it can be generated automatically) and ignore the field pics. When you create a Pic, you need to read the _id of the User and assigned as 'dev', something like:
let pic = new Pic({
thumbnail: '', description: '',
dev: yourUser._id
});
Using this way to create documents, you can use the populate function.

Cannot dump or return relation array inside mongoose object

I am trying to learn basic relations with MongoDB and mongoose using Node and Express.
User Model
let mongoose = require("mongoose");
userSchema = mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
});
userSchema.virtual("posts", {
ref: "Post",
localField: "_id",
foreignField: "userId"
});
// Making sure that password field is not present in responses
userSchema.methods.toJSON = function() {
let user = this.toObject();
delete user.password;
// console.log(user.posts);
return user;
};
// Creating model from schema
User = mongoose.model("User", userSchema);
module.exports = User;
Post Model
let mongoose = require("mongoose");
postSchema = mongoose.Schema({
body: {
type: String,
required: true
},
userId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User"
}
});
let Post = mongoose.model("Post", postSchema);
module.exports = Post;
These are the two models I am working with. Users have many posts.
Now I know I can get one user by:
let user = await User.findOne();
And I can populate the posts relation by:
await user.populate("posts").execPopulate();
But when I console.log(user) or res.send(user), I only see the user data, I don't see the posts relation data. I can console.log(user.posts) and get the data that way. But why won't it come with the object itself? Is this default behaviour and if it is, how do I get the user object with the posts array in my response?
To be able to use virtual populate you need to add toJSON: { virtuals: true } option to the schema.
So it should be like this:
userSchema = mongoose.Schema(
{
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
},
{
toJSON: { virtuals: true }
}
);
From the docs:
Keep in mind that virtuals are not included in toJSON() output by
default. If you want populate virtuals to show up when using functions
that rely on JSON.stringify(), like Express' res.json() function, set
the virtuals: true option on your schema's toJSON options.

UnhandledPromiseRejectionWarning: TypeError: place.toObject is not a function

Here I am trying to fetch Users Created places using userId. Here are User model and places model and in Controller, I have writing logic to fetch places by userId. Unfortunately, I am getting error "UnhandledPromiseRejectionWarning: TypeError: place.toObject is not a function" during sending response in res.json({ }) method.
Place Model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const placeSchema = new Schema({
title: { type: String, required: true },
description: { type: String, required: true },
image: { type: String, required: true },
address: { type: String, required: true },
location: {
lat: { type: Number, required: true },
lng: { type: Number, required: true },
},
creator: { type: mongoose.Types.ObjectId, required: true, ref: 'User'}
});
module.exports = mongoose.model('placemodels', placeSchema);
User Model
const mongoose = require('mongoose');
const uniqueValidator = require('mongoose-unique-validator');
const Schema = mongoose.Schema;
const userSchema = new Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true, minlength: 6 },
image: { type: String, required: true },
places: [{ type: mongoose.Types.ObjectId, required: true, ref: 'Place'}]
});
userSchema.plugin(uniqueValidator);
module.exports = mongoose.model('usermodels', userSchema);
Controller
const getPlacesByUserId = async (req, res, next) => {
const userId = req.params.uid;
let userWithPlaces;
try {
userWithPlaces = await User.findById(userId).populate('placemodels');
} catch (err) {
console.log(err);
const error = new HttpError(
'Fetching places failed, please try again later',
500
);
return next(error);
}
// if (!places || places.length === 0) {
if (!userWithPlaces || userWithPlaces.places.length === 0) {
return next(
new HttpError('Could not find places for the provided user id.', 404)
);
}
res.json({
places: userWithPlaces.places.map(place =>
place.toObject({ getters: true })
)
});
};
The references are really important in mongoose populate. In the schema, the refs refer to the mongoose name of the schema. Since the names are: 'placemodels' and 'usermodels'. The refs fields should use the exact name.
Reference: https://mongoosejs.com/docs/api.html#schematype_SchemaType-ref
The second important part is the parameters of the populate methods. The documentation specifies that the first argument of the populate function is a name path and is an object or a string. In the case above a string is used. It should refer to the name field to populate.
This means that the code should be the following because we want to populate the places field. The schema is responsible to know from where to get the information
...
userWithPlaces = await User.findById(userId).populate('places');
...
References: https://mongoosejs.com/docs/api.html#query_Query-populate
The references are really important in mongoose populate. In the schema, the refs refer to the mongoose name of the schema. Since the names are: 'placemodels' and 'usermodels'. The refs fields should use the exact name.
Reference: https://mongoosejs.com/docs/api.html#schematype_SchemaType-ref
The second important part is the parameters of the populate methods. The documentation specifies that the first argument of the populate function is a name path and is an object or a string. In the case above a string is used. It should refer to the name field to populate.
This means that the code should be the following because we want to populate the places field. The schema is responsible to know from where to get the information

Mongoose Populate() doesn't return anything, it just stays stuck

I have 2 Schemas, one is for Questions and the other is for Themes
It's a one to many relationship, where theme_id references the id for the Theme
It works great but thene I try to use populate to get the theme info from the id it just doesn't do anything, literally
I am making an API so when I hit /questions/:id with the respective id of the question, nothing happens Postman just freezes and the server does nothing
This is the Question Schema:
const questionSchema = mongoose.Schema({
question: {
type: String,
required: true,
index: { unique: true }
},
answers: [{
name: String,
is_true: Boolean
}],
theme_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'themes'
}
});
const Question = module.exports = mongoose.model('Question', questionSchema);
This is the Themes Schema:
const themeSchema = mongoose.Schema({
name: {
type: String,
required: true,
index: { unique: true }
},
relation: {
type: String,
required: true
}
});
const Theme = module.exports = mongoose.model('Theme', themeSchema);
This is how my get question method:
exports.getQuestion = (req, res, next) => {
Question.findById(req.params.id)
.populate('theme_id')
.exec((err, question) => {
if(err) return err;
console.log(question);
res.json(question);
})
}
When I do
populate('theme_id')
Nothing happens as described above
When I do
populate('theme') //or any other string, it doesn't matter
I get the theme_id field from MongoDB but it's not populated, it's just the ID of the theme
Been stuck here for a while now, what am I doing wrong?
mongoose give reference using ref:"ModelName"
const questionSchema = mongoose.Schema({
...
theme_id: { //<-- use this key in populate
type: mongoose.Schema.Types.ObjectId,
ref: 'Theme' // <-- model name here
}
});
const Question = module.exports = mongoose.model('Question', questionSchema);
and populate using field name in schema using : populate('field_name')
in your case : populate('theme_id')
I think the error is in your ObjectId reference .
Change theme_id reference to
theme_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Theme'
}
Then
populate('theme_id')
You can read more at Mongoose v5.6.13: Query Population

Mongoose Populate - array

can someone please help me with population of this schema? I need to populate array of Staff by their userId.
var PlaceSchema = new Schema ({
name: { type: String, required: true, trim: true },
permalink: { type: String },
country: { type: String, required: true },
...long story :D...
staff: [staffSchema],
admins: [adminSchema],
masterPlace:{ type: Boolean },
images: []
});
var staffSchema = new Schema ({
userId: { type: Schema.Types.ObjectId, ref: 'Account' },
role: { type: Number }
});
var adminSchema = new Schema ({
userId: { type: Schema.Types.ObjectId, ref: 'Account'}
})
var Places = mongoose.model('Places', PlaceSchema);
I tried to use this query, but without success.
Places.findOne({'_id' : placeId}).populate('staff.userId').exec(function(err, doc){
console.log(doc);
});
Polpulation is intended as a method for "pulling in" information from the related models in the collection. So rather than specifying a related field "directly", instead reference the related fields so the document appears to have all of those sub-documents embedded in the response:
Places.findOne({'_id' : placeId}).populate('staff','_id')
.exec(function(err, doc){
console.log(doc);
});
The second argument just returns the field that you want. So it "filters" the response.
There is more information on populate in the documentation.

Resources