Why populate returns empty results in mongodb - node.js

I want to populate my property (subCategories) but it's return empty although there are results in my database. Did I have miss something? I followed the populate method in mongoose:
const Document = mongoose.model('Document', new mongoose.Schema({
name: { type: String },
description: { type: String },
subCategory: { type: mongoose.Schema.Types.ObjectId }
}));
const Category = mongoose.model('Category', new mongoose.Schema({
name: { type: String },
subCategories: [
{
name: { type: String }
}
]
})
);
var cat1 = await new Category({ name: 'cat1', subCategories: [{ name: 'sub-1-cat-1' }, { name: 'sub-1-cat-2' } ]}).save();
var cat2 = await new Category({ name: 'cat2', subCategories: [{ name: 'sub-2-cat-1' }, { name: 'sub-2-cat-2' } ]}).save();
await new Document({ name: 'doc1', description: 'blabla', subCategory: cat2.subCategories[1] }).save();
const results = Document.find({}).populate('subCategory');
// results[0].subCategory is empty?! why?

The subcategory must be a mongoose model in order to be populated, currently the subCategory that you're trying to populate is just an array item for Category model so I would refactor the code you've posted above to this:
const SubCategory = mongoose.model('SubCategory', new mongoose.Schema({
name: { type: String }
}));
const Document = mongoose.model('Document', new mongoose.Schema({
name: { type: String },
description: { type: String },
subCategory: { type: mongoose.Schema.Types.ObjectId, ref: "SubCategory" }
}));
const Category = mongoose.model('Category', new mongoose.Schema({
name: { type: String },
subCategories: [
{ type: mongoose.Schema.Types.ObjectId, ref: "SubCategory" }
]
})
);
var sub1Cat1 = await new SubCategory({ name: 'sub-1-cat-1' }).save();
var sub1Cat2 = await new SubCategory({ name: 'sub-1-cat-2' }).save();
var sub2Cat1 = await new SubCategory({ name: 'sub-2-cat-1' }).save();
var sub2Cat2 = await new SubCategory({ name: 'sub-2-cat-2' }).save();
var cat1 = await new Category({ name: 'cat1', subCategories: [sub1Cat1, sub1Cat2 ] }).save();
var cat2 = await new Category({ name: 'cat2', subCategories: [sub2Cat1, sub2Cat2 ] }).save();
await new Document({ name: 'doc1', description: 'blabla', subCategory: cat2.subCategories[1] }).save();
const results = Document.find({}).populate('subCategory');
// results[0].subCategory is not empty!

Related

Populate does not work in mongoose create

User schema looks something like this
addressId: [{ type: mongoose.Schema.Types.ObjectId, ref: 'address' }],
mongoose.model('user', userSchema);
Address model
mongoose.model('address', postalAddressSchema);
I am trying to do this:
const createdUser = await mongoose.models.user.create(user);
return { success: true, user: createdUser.populate('addressId') };
I am trying to populate address in user creation. It returns null
You may try to find, populate and exec after creating the user. Here is a working example
const theMongoose = await mongoose.connect(mongoServerUri);
const accountModel = theMongoose.model(
'Account',
new Schema({
_id: Schema.Types.ObjectId,
name: String
})
);
const activityModel = theMongoose.model(
'Activity',
new Schema({
account: { type: Schema.Types.ObjectId, ref: 'Account' },
date: Date,
desc: String
})
);
const account = await accountModel.create({ name: "Test User", _id: mongoose.Types.ObjectId.createFromTime(new Date().getTime()/1000) });
await activityModel.create({ date: new Date(), desc: "test", account: account.id });
// find, populate, & exec
const res = await activityModel.find({ desc: "test" }).populate("account").exec()
console.log(res);
And the output is
[
{
_id: new ObjectId("62da50ec77c026e2aad5577b"),
account: {
_id: new ObjectId("62da50ea0000000000000000"),
name: 'Test User',
__v: 0
},
date: 2022-07-22T07:25:32.119Z,
desc: 'test',
__v: 0
}
]

autopopulate & virtual in child schema does not work

I have the following schemas:
"use strict";
const mongoose = require('mongoose'),
Schema = mongoose.Schema,
autopopulate = require('mongoose-autopopulate');
const child = new Schema({
userUuid: {
type: String,
required: true
},
timeStamp: {
type: Date,
default: new Date()
}
}, {toJSON: {virtuals: true}});
child.virtual('user', {
ref: 'users',
localField: 'userUuid',
foreignField: 'uuid',
autopopulate: true
});
const parentList= new Schema({
//some properties
children: [child]
});
parentList.plugin(autopopulate);
module.exports = parentList;
I need the Children's list to extract a full object - but it does not work.
When I put a single user not as a child then it works well:
const try= new Schema({
//some properties
userUuid: {
type: String,
required: true
}
}, {toJSON: {virtuals: true}});
try.virtual('user', {
ref: 'users',
localField: 'userUuid',
foreignField: 'uuid',
autopopulate: true
});
try.plugin(autopopulate);
module.exports = try;
This leads me to the conclusion that the problem is when the virtual is within the child schema
What am I missing?
Here's the full code of my attempt to reproduce yours :
const
{ randomUUID } = require('node:crypto'),
{ MongoMemoryServer } = require('mongodb-memory-server'),
mongoose = require('mongoose'),
{ Schema } = mongoose,
autopopulate = require('mongoose-autopopulate');
(async () => {
const
dbServer = await MongoMemoryServer.create(),
dbClient = mongoose.createConnection(dbServer.getUri());
dbClient.on('disconnected', () => dbServer.stop());
await new Promise(resolve => dbClient.once('connected', () => resolve()));
try {
const trySchema = new Schema({
//some properties
userUuid: {
type: String,
required: true
}
}, { toJSON: { virtuals: true } });
trySchema.virtual('user', {
ref: 'users',
localField: 'userUuid',
foreignField: 'uuid',
autopopulate: true
});
trySchema.plugin(autopopulate);
const childSchema = new Schema({
userUuid: {
type: String,
required: true
},
timeStamp: {
type: Date,
default: new Date()
}
}, { toJSON: { virtuals: true } });
childSchema.virtual('user', {
ref: 'users',
localField: 'userUuid',
foreignField: 'uuid',
autopopulate: true
});
childSchema.plugin(autopopulate);
const parentListSchema = new Schema({
//some properties
children: [childSchema]
});
parentListSchema.plugin(autopopulate);
const userSchema = new Schema({
uuid: {
type: String,
required: true
}
});
const
Try = dbClient.model('try', trySchema),
Child = dbClient.model('child', childSchema),
ParentList = dbClient.model('parentList', parentListSchema),
User = dbClient.model('users', userSchema);
const userUuid = randomUUID();
await new User({ uuid: userUuid }).save();
await new Try({ userUuid }).save();
const child = await new Child({ userUuid }).save();
await new ParentList({ children: [child] }).save();
console.log('User:', (await User.findOne().exec()).toJSON());
console.log('Try:', (await Try.findOne().exec()).toJSON());
console.log('Child:', (await Child.findOne().exec()).toJSON());
console.log('ParentList:', (await ParentList.findOne().exec()).toJSON());
}
catch(error){
console.error(error);
}
dbClient.close();
})();
Which outputs :
User: {
_id: new ObjectId("62c6e7bcef50638fe0097866"),
uuid: 'bb5af665-759a-4da0-880d-8a54ce42be4c',
__v: 0
}
Try: {
_id: new ObjectId("62c6e7bcef50638fe0097868"),
userUuid: 'bb5af665-759a-4da0-880d-8a54ce42be4c',
__v: 0,
user: [
{
_id: new ObjectId("62c6e7bcef50638fe0097866"),
uuid: 'bb5af665-759a-4da0-880d-8a54ce42be4c',
__v: 0
}
],
id: '62c6e7bcef50638fe0097868'
}
Child: {
_id: new ObjectId("62c6e7bcef50638fe009786b"),
userUuid: 'bb5af665-759a-4da0-880d-8a54ce42be4c',
timeStamp: 2022-07-07T14:03:40.902Z,
__v: 0,
user: [
{
_id: new ObjectId("62c6e7bcef50638fe0097866"),
uuid: 'bb5af665-759a-4da0-880d-8a54ce42be4c',
__v: 0
}
],
id: '62c6e7bcef50638fe009786b'
}
ParentList: {
_id: new ObjectId("62c6e7bcef50638fe009786e"),
children: [
{
userUuid: 'bb5af665-759a-4da0-880d-8a54ce42be4c',
timeStamp: 2022-07-07T14:03:40.902Z,
_id: new ObjectId("62c6e7bcef50638fe009786b"),
__v: 0,
id: '62c6e7bcef50638fe009786b'
}
],
__v: 0
}
I'm not sure what you meant about [needing] the Children's list to extract a full object.
If you were talking about Child.user then you were only missing child.plugin(autopopulate);.
If you were talking about ParentList.children then it worked out of the box for me.

References in another Schema - mongoose

I'm doing some tests with MongoDB and NodeJS for a new project.
Searching the documentation I found that it is possible to make references to other collections and bring this data to JSON.
It was then that I decided to perform the following test:
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const userSchema = new Schema({
name: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unique: true,
},
posts: [{
type: Schema.Types.ObjectId,
ref: 'Post'
}]
})
const userModel = mongoose.model('User', userSchema)
const postSchema = new Schema({
title: {
type: String
},
content: {
type: String
},
author: {
type: Schema.Types.ObjectId,
ref: 'User'
}
})
const postModel = mongoose.model('Post', postSchema)
const saveUser = new userModel({
name: 'user',
email: 'user#email.com'
})
saveUser.save()
const savePost = new postModel({
title: 'Lorem',
content: 'Lorem Ipsum',
author: saveUser._id
})
savePost.save()
postModel.find()
.populate('User')
.exec((err, post) => {
console.log(post)
})
However the return of JSON is:
{
_id: 5edd0c24a4f42b0e126f4b15,
title: 'Lorem',
content: 'Lorem Ipsum',
author: 5edd0c24a4f42b0e126f4b14,
__v: 0
}
When should it be:
{
_id: 5edd0c24a4f42b0e126f4b15,
title: 'Lorem',
content: 'Lorem Ipsum',
author: {
_id: 5edd0c24a4f42b0e126f4b14,
name: user,
email: user#email.com
},
__v: 0
}
Does anyone know any solution to this problem where I can insert for all my new Schemas?
Regarding populate you should provide the field name provided in postSchema. So should replace .populate('User') with .populate('author').
Since the post requires the author _id you should save the post only after the author is successfully saved
const saveUser = new userModel({
name: "user",
email: "user#email.com",
});
saveUser.save((err, data) => {
if (err) console.log(err);
else {
const savePost = new postModel({
title: "Lorem",
content: "Lorem Ipsum",
author: saveUser._id,
});
savePost.save();
}
});

Graphql express nested schema

I am currently building mern + graphql app. I am new to graphql and I have two different models a User Model and Character model. The Character Model is nested in the User. When I create a user in the Graphiql interface I want it to display its data and the character that is associated with the specific user but instead it is displaying every character that has been created in the database and adding them to every user created instead of the specific user. Trying to figure out how to fix this. Link to Github Repo
Mongoose User Model
const mongoose = require('../db/connection')
const Schema = mongoose.Schema
const User = new Schema({
id: String,
name: String,
totalwins: Number,
description: String,
charactersID: [
{
type: Schema.Types.ObjectId,
ref: "Character"
}
]
})
module.exports = mongoose.model('User', User)
Mongoose Character Model
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const Character = new Schema({
name: String,
wins: Number,
losses: Number,
percentage: Number,
image: String
})
module.exports = mongoose.model('Character', Character)
const graphql = require('graphql')
const { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLInt, GraphQLID, GraphQLList,
GraphQLNonNull } = graphql
const Character = require("../models/Character")
const User = require("../models/User")
Types
// User Schema
const userType = new GraphQLObjectType({
name: 'User',
fields : () => ({
_id: {type: GraphQLID},
name : { type: GraphQLString},
totalwins : { type: GraphQLInt },
description : { type: GraphQLString },
character : {
type : new GraphQLList(characterType),
resolve(parent, args){
return Character.find({charactersID: parent.id})
}
}
})
})
// Character Schema
const characterType = new GraphQLObjectType({
name: 'Character',
fields : () => ({
id: {type: GraphQLID},
name : { type: GraphQLString},
wins : { type: GraphQLInt },
losses : { type: GraphQLInt },
percentage: {type: (GraphQLInt)},
image: { type: GraphQLString },
})
})
Query
//query the graph to grab the data
const Query = new GraphQLObjectType({
name: 'Query',
fields: {
user : {
type: userType,
// arguments passed by the user while making the query
args: {id : {type : GraphQLID}},
resolve(parent, args){
// return User.find((item) => { return item.id == args.id});
//finding a single user by id
return User.findById(args.id)
}
},
users : {
type: new GraphQLList(userType),
resolve(parent, args) {
return User.find({})
}
},
character : {
type: characterType,
args: {id : {type : GraphQLID}},
resolve(parent, args){
return Character.findById(args.id)
}
},
characters : {
type: new GraphQLList(characterType),
resolve(parent, args) {
return Character.find({})
}
}
}
})
Mutations
//allows user to add, update and delete to mondodb through graphql
const Mutation = new GraphQLObjectType({
name: 'Mutation',
fields: {
addUser: {
type: userType,
args: {
name: {type: GraphQLNonNull(GraphQLString)},
totalwins: {type: (GraphQLInt)},
description: {type: (GraphQLString)},
charactersID: { type: new GraphQLNonNull(GraphQLID)}
},
resolve(parent, args){
let user = new User({
name: args.name,
totalwins: args.totalwins,
description: args.description,
charactersID: args.charactersID
})
return user.save()
}
},
addCharacter: {
type: characterType,
args: {
name: {type: GraphQLNonNull(GraphQLString)},
wins: {type: (GraphQLInt)},
losses: {type: (GraphQLInt)},
percentage: {type: (GraphQLInt)},
image: {type: (GraphQLString)},
},
resolve(parent, args){
let character = new Character({
name: args.name,
wins: args.wins,
losses: args.losses,
percentage: args.percentage,
image: args.image
})
return character.save()
}
},
deleteUser: {
type: userType,
args: {
id: {type: new GraphQLNonNull(GraphQLString)}
},
resolve(parent, args){
const remUser = User.findByIdAndRemove(args.id)
if(!remUser){
throw new Error('No character found')
}
return remUser
}
},
deleteCharacter: {
type: characterType,
args: {
id: {type: new GraphQLNonNull(GraphQLString)}
},
resolve(parent, args){
const remCharacter = Character.findByIdAndRemove(args.id)
if(!remCharacter){
throw new Error('No character found')
}
return remCharacter
}
},
}
})
module.exports = new GraphQLSchema({ query : Query, mutation : Mutation})
Image of results from Grapiql interface

Express mongoose populate array of subdocuments from POST

This is my Mongoose Schema:
const InvoiceSchema = new Schema({
name: { type: String, required: true },
description: { type: String },
items: [{
product: { type: mongoose.Schema.Types.ObjectId, ref: 'Product'},
amount: { type: Number },
name: { type: String, required: true },
quantity: { type: Number },
rate: { type: Number, required: true }
}],
createdBy: { type: Schema.ObjectId, ref: 'User', required: true },
}
Now I want to populate my Schema from POST Datas, My problem is I don't Know how to post my items (How do I name my fields)??
I use PostMan to post Datas.
To get post data
To add a new record in mongoose
const {ObjectId} = mongoose.Schema.Types;
const newInvoice = new InvoiceSchema({
name: "John Smith",
description: "This is a description",
items: [{
product: 'THIS_IS_AN_OBJECT_ID_STRINGIFIED',
amount: 2,
quantity: 5,
//name - comes from the product model
//rate - comes from the product model
}]
});
newInvoice.save();
To POST and save it
//Response format
{
name: 'John Smith',
description: 'This is a description',
items: [
{
product: 'THIS_IS_AN_OBJECT_ID',
amount: 2,
quantity: 5
}
]
}
app.post('/yourRoute', (req, res) => {
const {name, description, items} = req.body;
const newInvoice = new InvoiceSchema({name, description, items});
newInvoice.save().then(()=>res.send('success'))
});
To bulk add items
const invoice = new Invoice();
invoice.items = req.body.items;
To add single item
invoice.items.push(item);
To update single item
const item = invoice.items.id(req.params._id);
item.attribute = ...
// Do update

Resources