I am working with node(express) with mongoose and I have two collections,
Users
Comments
I added the sample Schema(added few fields only)
const UserSchema = mongoose.Schema({
name: String,
email: String,
});
const CommentsSchema = mongoose.Schema({
comments: String,
user_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
text: String,
});
So I trying to fetch the users list and no of comments count based on user..
Expecting output like below:
data = [
{
name: 'abcd',
email: 'aa#test.com',
commentsCount: 5
},
{
name: 'xxx',
email: 'xx#test.com',
commentsCount: 3
}
]
I am not sure how to get the results, because we don;t have ref in user table..
userModel.find({}).exec((err, users) => {
if (err) {
res.send(err);
return;
}
users.forEach(function(user){
commentsModel.countDocuments({user_id: users._id}).exec((err, count) => {
if(!err){
user.commentsCount = count;
}
})
});
console.log('users', users)
});
Can you anyone please help to fix, I needs to list out the users and count of comments
Related
I'm new to using a key other than ObjectId to link data from other collections. Currently, I have appointments with various other data I'd like to bring in so I can evaluate whether payment is due or not.
My query worked, except it doesn't bring in the plan information for each patient. I understand that it makes a separate query for each populate, so I'd have to do it after I populate the patient information with populate('patientID'):
const appts = await Appt.find(searchParams)
.populate('patientID')
.populate('patientID.plan')
.populate('status')
.populate('type')
.sort({ scheduled: -1 });
The above doesn't work for bringing in the nested JSON of the plan information, but it DOES work for bringing in the patient collection, status, and type. Only patientID.plan populate doesn't work.
My schemas:
const familySchema = new mongoose.Schema({
ID: {
type: Number,
index: true
},
family: String
});
const paymentplanSchema = new mongoose.Schema({
ID: {
type: Number,
index: true
},
plan: String,
planamt: Number
});
const patientSchema = new mongoose.Schema({
ID: {
type: Number
},
familyID: Number,
first: String,
last: String,
careplanID: Number,
otherData: variousTypes
});
patientSchema.virtual('plan', {
ref: 'PaymentPlan', // The model to use
localField: 'careplanID', // Find people where `localField`
foreignField: 'ID' // is equal to `foreignField`
});
patientSchema.pre('find', function() {
this.populate('plan');
});
const typeSchema = new mongoose.Schema({
ID: Number,
appttype: String,
abbr: String,
amt: Number,
code: String,
length: Number
});
const statusSchema = new mongoose.Schema({
ID: Number,
status: String
});
const apptSchema = new mongoose.Schema({
ID: Number,
patientID: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Patient'
},
oldPatientID: Number,
status: {
type: mongoose.Schema.Types.ObjectId,
ref: 'ApptStatus'
},
type: {
type: mongoose.Schema.Types.ObjectId,
ref: 'ApptType'
},
scheduled: Date,
note: String
});
mongoose.model('Appt', apptSchema);
mongoose.model('ApptStatus', statusSchema);
mongoose.model('ApptType', typeSchema);
mongoose.model('Patient', patientSchema);
mongoose.model('PaymentPlan', paymentplanSchema);
How do I get the patient data to load WITH the plan data? I don't get what I'm doing wrong, and I've got other things I'd like to connect in this way (via index instead of ObjectId) but just don't get what I'm doing wrong.
UPDATED TO ADD MORE DETAIL:
My query on the backend to get the appointments is this:
module.exports.search = async (req, res) => {
console.log('GET the appts');
const searchParams =
req.params.query === 'today'
? { scheduled: { $gt: new Date(dayStart), $lt: new Date(dayEnd) } }
: req.body;
console.log(searchParams);
try {
const appts = await Appt.find(searchParams)
.populate({
path: 'patientID',
populate: { path: 'plan' }
})
.populate('status')
.populate('type')
.sort({ scheduled: -1 });
if (!appts) {
console.log(`No appointments found`);
}
appts.forEach(p => {
const patient = p.patientID ? p.patientID.nickname : 'NONE';
const plan =
p.patientID && p.patientID.plan ? p.patientID.plan.planamt : 0;
console.log(patient, plan);
});
console.log(appts.length, 'appts found');
res.send(appts);
} catch (err) {
console.log(`Error`, err);
return res.status(500).send(err);
}
};
In the console, It's logging correctly (example):
CarF 60
8075 'appts found'
In the frontend, all the objects are populated EXCEPT patientID.plan. The patientID object does not include a plan field on any of the entries. patientID, status, and type all populated the corresponding objects.
WHY is this logging on the backend, but not visible on the frontend?
You should be able to do it by passing a path option to populate():
const appts = await Appt.find(searchParams)
.populate('patientID')
.populate({
path: 'patientID',
populate: {path: 'plan'}
})
.populate('status')
.populate('type')
.sort({ scheduled: -1 });
See https://mongoosejs.com/docs/populate.html#deep-populate in official docs
I have a collection of Items that a particular make and perform a transaction. In my schema, I associate the userId to each item. I want to be able to display as a list all the items that the user owns.
Here I have managed to total up all sizes of each item but I cant work out a way how to get a total for each user
{
id: Number,
x: Number,
y: Number,
xSize: String,
ySize: String,
imageSource: String,
user: { type: mongoose.Schema.ObjectId, ref: 'User' }
},
{ timestamps: true }
);
const UserSchema = new Schema(
{
id: Number,
name: String,
website: String,
},
{ timestamps: true }
);
Item.find({}, function (err, items) {
var itemMap = {};
items.forEach(function (item) {
itemMap[item._id] = item;
});
var countedNames = items.reduce(function (allNames, name) {
if (name.xSize in allNames) {
allNames[name.xSize]++;
}
else {
allNames[name.xSize] = 1;
}
return allNames;
}, {});
Essentially i want to get a list basically saying
{name:"Dave", website:"www.google.com, items:[item1, item2]}
where item1 and item2 relate to the item schema
You should rewrite your UserSchema to contain a reference to the item, in this format:
const UserSchema = new Schema(
{
id: Number,
name: String,
website: String,
items:
[{
item: {type: mongoose.Schema.ObjectId, ref: 'Item'}
}]
},
{ timestamps: true }
);
This will simply allow you to perform the following query:
User.find({}).populate('Item')
Which would return the User document, and all items associated under the document.
You could do the following:
let users = User
.find({})
.populate('Item')
.exec(function (err, users) {
if (err) { console.log(err); }
console.log(users)
}
Rewriting the schema will make querying users for their items much easier.
I've searched this site for days looking through the many different but similar questions on this topic to no avail.
Here's what I'd like to happen. A user signs in and their posts are automatically linked to the users collection. Eventually I'd like to link posts to the profile it was posted to, but i"m not quite there yet. Here's what I've tried so far.
In the User Schema:
const UserSchema = new Schema({
posts: [{
type: Schema.Types.ObjectId,
ref: 'posts'
}],
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
...
});
module.exports = User = mongoose.model('users', UserSchema);
In the Post Schema:
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
text: {
type: String,
required: true
},
name: {
type: String
},
...
});
module.exports = Post = mongoose.model('posts', PostSchema);
In my users api, here's how I'm signing the user in and attempting to populate the user's posts:
const User = require('../../models/User');
router.post('/login', (req, res) => {
const { errors, isValid } = validateLoginInput(req.body);
// Check Validation
if (! isValid) {
return res.status(400).json(errors);
}
const email = req.body.email;
const password = req.body.password;
// Find user by email
User.findOne({ email })
.populate('posts')
.then(user => {
if (! user) {
errors.email = 'User not found';
return res.status(400).json(errors);
}
// Check password
bcrypt.compare(password, user.password).then(isMatch => {
if (isMatch) {
// User Matched
// Create JWT Payload
const payload = {
id: user.id,
firstName: user.firstName,
lastName: user.lastName,
name: user.firstName + ' ' + user.lastName,
avatar: user.avatar,
posts: user.posts
};
jwt.sign(
payload,
keys.secretOrKey,
{ expiresIn: 3600 }, (err, token) => {
res.json({
success: true,
token: 'Bearer ' + token,
payload
});
});
} else {
errors.password = 'Password is incorrect';
return res.status(400).json(errors);
}
});
});
});
In the posts api, here's how the post is being submitted:
router.post('/', passport.authenticate('jwt', { session: false }), (req, res) => {
const { errors, isValid } = validatePostInput(req.body);
if (! isValid) {
// Return errors with 400 status
return res.status(400).json(errors)
}
const newPost = new Post({
text: req.body.text,
name: req.body.name,
avatar: req.body.avatar,
user: req.user.id
});
newPost.save().then(post => res.json(post));
});
Currently, all I'm seeing is an empty array and no errors. I've been spinning my wheels on this one for a couple days now so any help would be appreciated. Thanks!
I think you forgot to save the _id of your new post to the User model so that the populate() can lookup the posts to populate:
newPost.save().then(post => {
User.update({ _id: req.user.id }, { $push: { posts: post._id }}, (err) => {
res.json(post));
});
});
When I save a new "experience" document with the model Experience, the experience _id is not saved into the document of the user. So my "experiences" array in the user document remains empty. Why?
const mongoose = require('mongoose');
const ExperienceSchema = mongoose.Schema({
name: String,
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
reviews: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Review' }],
categories: [{ type: String }],
});
module.exports = mongoose.model('Experience', ExperienceSchema);
==============================================
const mongoose = require('mongoose');
const UserSchema = mongoose.Schema({
name: String,
experiences: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Experience' }],
});
module.exports = mongoose.model('User', UserSchema);
=============================================
// Update experience to database
router.post('/:id', (req, res, next) => {
const idexp = req.params.id;
const newExperience = {
name: req.body.name,
user: req.user._id,
};
Experience.findOneAndUpdate({ _id: idexp }, newExperience, (err, result) => {
if (err) {
return res.render(`/${idexp}/edit`, { errors: newExperience.errors });
}
return res.redirect(`/experiences/${idexp}`);
});
});
The experiences is the sub-document of user schema. So, when you save experiences, the user will not be saved. However, when you save user, the experience should be saved.
Refer this subdocs documentation
Here is the solution... I needed to use $push to update the user document with the experience id before rendering the site.
Experience.findOneAndUpdate({ _id: idexp }, newExperience, (err, result) => {
if (err) {
return res.render('experiences/edit', { errors: newExperience.errors });
}
User.findByIdAndUpdate({ _id: req.session.passport.user._id }, { $push: { experiences: idexp } }, (err) => {
if (err) {
next(err);
} else {
return res.redirect(`/experiences/${idexp}`);
}
});
});
I've been reading a few answers regarding this and yet I still can't get it to work.
My model objects aren't deeply nested and are quite simple. It's events that have a list of users attending them and users that have a list of events they've attended. like so:
let DinnerSchema = new mongoose.Schema({
date: {
type: Date,
unique: true,
timestamps: true,
required: true
},
title:{type: String, require: true},
attending: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}]
})
and the users:
let UserSchema = new mongoose.Schema({
email: {
type: String,
lowercase: true,
unique: true,
required: true
},
name:{ type: String, require: true },
password: {type: String ,required: true},
dinners: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Dinner'
}]
})
And for clarity here's the entire route that's using populate:
userpage.get('/', authCheck, (req, res) => {
const options = { _id: '57ebbf48bd6914036f99acc7' }
return Dinner
.findOne(options)
.populate('User', 'name') //I'VE TRIED ADDING 'name' BASED ON SOME ANSWERS I SAW
.exec((err, newDinner) => {
if (err) {
console.log(err)
res.status(400).end()
}
console.log(newDinner) // SHOW'S USERS ID'S BUT NO OTHER FIELDS
return res.json({
sucsess: true,
data: newDinner
})
})
})
If I understand correctly in the database itself there should only be a reference to the other model and not actually all of it's fields and the join happens with the populate. My db structure show's just the reference so that's ok.
I've tried specifying the name of the fields i'm after (the name field in this case) but that didn't work.
My population result always looks like the following and doesn't show any other fields except for the _id one:
{
_id: 57ebbf48bd6914036f99acc7,
date: 2016-09-27T22:00:00.000Z,
title: '1',
__v: 0,
attending: [ 57ebbcf02c39997f9cf26891, 57ebbdee098d3c0163db9905 ]
}
What am I screwing up here?
In mongoose populate receives 4 parameters.
path
selection(fields to be return) ,
condition
options (like {limit:10})
In your case you are not passing right path to populate. It should be
userpage.get('/', authCheck, (req, res) => {
const options = { _id: '57ebbf48bd6914036f99acc7' }
return Dinner
.findOne(options)
.populate('attending', 'name')
.exec((err, newDinner) => {
if (err) {
console.log(err)
res.status(400).end()
}
console.log(newDinner) // SHOW'S USERS ID'S BUT NO OTHER FIELDS
return res.json({
sucsess: true,
data: newDinner
})
})
})
Now it will return all the names of attending users.
you need to populate attending - that's your user reference in the dinner schema