I have a list of users, each user have a list of exercises. I want to aggregate the basic user's info and their exercises to be displayed.
I did the following:
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Invalid username'],
unique: [true, 'This user already exists'],
},
exercises: {
type: [mongoose.Schema.Types.ObjectId],
ref: 'Exercise'
}
})
User = mongoose.model('User', userSchema);
And
const exerciseSchema = new mongoose.Schema({
user: {type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true},
description: {type:String, required: true},
duration: {type:Number, required: true},
date: {type:Date, required: true},
});
Exercise = mongoose.model('Exercise', exerciseSchema);
However, the aggregation part displays only the user's info with an empty array of exercises:
User.
find().
populate({
path: 'exercises',
model: 'Exercise'
}).
exec().
then(docs => res.json(docs).status(200)).
catch(err => res.json(err).status(500))
})
Gives:
[{
"exercises":[],
"_id":"6047b61bc7a4f702f477085b",
"name":"John Smith",
"__v":0}
]
Use the following aggregate query to get users and is exercise data.
User.aggregate([{
"$lookup":{
"localField":"exercises",
"foreignField":"_id",
"from":"Exercise",
"as" :"userExercises"
}
}])
You will get each user with its exercise data.
Related
first of all, I am a beginner and currently, I am working on a social media blog type.
Now, I have my userSchema and postSchema models:
USER MODEL
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please insert your name'],
},
email: {
type: String,
required: [true, 'Please insert your email'],
unique: true,
lowercase: true, //transform into lowercase / not validator
validate: [validator.isEmail, 'Please provide a valid email'],
},
avatar: {
type: String,
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user',
},
password: {
type: String,
required: [true, 'Please provide a password'],
minLength: 8,
select: false,
},
passwordConfirm: {
type: String,
required: [true, 'Please confirm your password'],
validate: {
validator: function (el) {
return el === this.password;
},
message: 'Passwords are not the same',
},
},
passwordChangedAt: Date,
posts: [] // ???????????????
});
POST MODEL
const postSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'A post must have a title'],
},
author: {
type: String,
required: [true, 'A post must have a title'],
},
likes: {
type: Number,
default: 10,
},
comments: {
type: [String],
},
image: {
type: String,
},
postBody: {
type: String,
required: [true, 'A post must contain a body'],
},
createdAt: {
type: Date,
default: Date.now(),
select: false,
},
});
Now I don't know if it's the best approach but I was thinking of having a field in userSchema with the type of an array of postSchema so I will have for each user their own posts created. Can I do that? If not how I can achieve that?
Should I use search params fields to filter posts by the author? I am really confused how I should approach this situation. Thank you guys
Check out this example
const UserSchema = new mongoose.Schema({
username: String,
posts: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}]
})
const PostSchema = new mongoose.Schema({
content: String,
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
})
const Post = mongoose.model('Post', PostSchema, 'posts');
const User = mongoose.model('User', UserSchema, 'users');
module.exports = { User, Post };
Credit: https://medium.com/#nicknauert/mongooses-model-populate-b844ae6d1ee7
I am trying to grab documents based on populated subdocuments.
Here are my models
// User model
var UserSchema = new mongoose.Schema({
username: {type: String, required: true, trim: true},
firstName: {type: String, required: true, lowercase: true},
lastName: {type: String, required: true, lowercase: true},
phone: {type: String, required: false},
email: {type: String, required: true},
password: {type: String, required: true},
blogs: {type: mongoose.Schema.Types.ObjectId, ref: 'Blogs'}
}, {timestamps: true});
// Blog Model
var BlogSchema = new mongoose.Schema({
description: String,
tags: [String],
other: [Object],
}, {timestamps: true});
This is how I am grabbing documents
fetchAllByFilter: async function(req, res) {
try {
let result = await Users.find({}).populate('blog');
return res.status(200).send(result);
} catch (err) {
return res.status(200).send({error: err});
}
},
Now my main question is, how would I grab Users based on their Blogs referenced documents?
For example, Find Users with Blogs that has Blog.tags of "food", "cars", "movies" and/or Blog.other of [{...SomeObject}, {...SomeOtherObject}]
looking at mongo docs match an array, you could make a utility function somewhat like this...
async function findByTag(tag) {
const blogIds = await Blog.find({ tags: tag }).select("_id");
const users = await User.find({
blogs: { $in: blogIds.map((blog) => blog._id) }
}).populate("blog");
}
I started learning some NodeJS, and how to make a REST API from Academind on YouTube, and learned what a relational and non-relational database is, etc.
With MongoDB, writes are rather cheap, so I want to minimize the number of reads that I do. At the moment I am trying to see how I could make an API, that will be for an app that's similar to discord's, although it'll be for fun.
Is this the right way to make a Schema?
const mongoose = require('mongoose')
const userSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true, unique: true},
email: { type: String, required: true },
password: { type: String, required: true }, // TODO: Hashing, etc
guilds: [{
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true},
channels: [{
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true},
// Only the X most recent messages
messages: [{
_id: mongoose.Schema.Types.ObjectId,
message: {type: String, required: true},
user: {
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true}
}
}]
}],
// Only an X amount of users
users: [{
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true}
}]
}]
})
module.exports = mongoose.model('User', userSchema)
And then for the Guilds,
const mongoose = require('mongoose')
const guildSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true},
channels: [{
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true},
// Only an X amount of messages
messages: [{
_id: mongoose.Schema.Types.ObjectId,
message: {type: String, required: true},
user: {
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true}
}
}]
}],
// All the users
users: [{
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true}
}]
})
module.exports = mongoose.model('Guild', guildSchema)
Channel Schema
const mongoose = require('mongoose')
const channelSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true },
guild: {
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true },
channels: [{
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true }
}],
// The users of the guild, or just the channel?
// Could add a users object outisde of the guild object
users: [{
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true }
}]
}
})
module.exports = mongoose.model('Channel', channelSchema)
And finally for the messages
const mongoose = require('mongoose')
const messageSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
user: {
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true}
},
message: {type: String, required: true},
channel: {
guild: {
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true}
// Store more data for each message?
}
}
})
module.exports = mongoose.model('Message', messageSchema)
I am not sure if this is how a non-relational schema should look like. If it's not, how would I go about to store the data that I need?
Also, let's say that I POST a message on channel X on guild Y with the users A B and C, how would I go about to update all the entries, to add a message?
I've only used the User.find({_id: id}).exec().then().catch() so far, so I am not sure how to go about to update them.
Thanks in advance!
The messages collection should be on its own, do not embed it into any collection. This is not a good idea to embed data that will grow without limit.
The idea to store the last 5 messages into other collection looks painful to implement.
Embed denormalised data from all collections into the users collection seems like a problem when you will have to update guilds, channels, guilds users.
You may embed channels into guilds. Channels would not grow without a limit, should be a reasonable amount, less than 100 of channels per guild and probably it always used with a guild that they belong to. If not, consider not to embed channels into guilds.
The power of mongodb is to build the schema that reflects how your app is using data. I would recommend starting with normalized data. And when problems with creating, reading, updating, deleting data will occur then make appropriate changes in your mongoose schema to solve the problem. Premature optimization will only hurts in the long run.
As always an answer depends on details. Since I do not know all details I would recommend three part article by William Zola, Lead Technical Support Engineer at MongoDB. part 1 part 2 part 3
Here actually I want to make the service collection that contain the array of references of the ratings. when a user rate a service than an element is pushed in the array containing reference of user , service ID no and the rating.
Service Model like this:
var ServiceSchema = new Schema({
user_id:{
type: String,
required: [true, 'please provide user id']
},
name: {
type: String,
required: [true, 'please enter your name']
},
rating : [{ type: Schema.Types.ObjectId, ref: 'rating' }],
});
Rating schema:
var RatingSchema = Schema({
S_id : { type: Schema.Types.ObjectId},
Rating : Number,
By : { type: Schema.Types.ObjectId}
});
user schema:
var UserSchema = new Schema({
id: {
type: String,
unique: true,
required: [true, 'please enter your id']
},
password: {
type: String,
required: [true, 'please enter your password']
},
name: {
type: String,
required: [true, 'please enter your name']
},
type: {
type: [{
type: String,
enum: ['visitor', 'seller']
}],
default: ['visitor']
},
});
and I have defined the export as:
module.exports = mongoose.model('user', UserSchema, 'users');
module.exports = mongoose.model('service', ServiceSchema, 'service');
module.exports = mongoose.model('rating', RatingSchema, 'rating');
I want to make a function called rate but I am not getting how to make it.
exports.rate = function(req, res) {
var curr_service = new Service(req.body, result);
new_service.save(function(err, service) {
if (err)
res.send(err);
res.json(service);
});
};
So far I have done this.
Can someone help me to understand what should I do now? because I haven't find that much about mongoose to add ref in array...
In my case. This error was happening because instead of putting {es_indexed: true} inside the object declaration, I was putting it in the object that was using. For example:
const Client: Schema({
name: {type: String, es_indexed: true},
address: {type: Adress, es_indexed: true} //Wrong, causing error
})
Adress: Schema({
address: {type: String},
zipCode: {type: Number}
})
The correct way to use, is putting es_indexed: true into primitive types inside "Adress" schema declaration.
const Client: Schema({
name: {type: String, es_indexed: true},
address: {type: Adress} //Right way
})
Adress: Schema({
address: {type: String, es_indexed: true},
zipCode: {type: Number, es_indexed: true}
})
I hope it was helpful
This is probably going to get downvoted off of here but Im having a hard time wrapping my head around this issue.
In my Mongoose Schema I have a list of groups. These groups will be people, they will have an _id of that person, or if they dont have an _id they be listed by their email.
It makes sense to me to use groups so I can loop through a list without having to hardcode the list names, reason being is that I would like to let users add thier own groups. What I am not understanding is....
1) How can I allow the users to add to this collection, can I just do groups.push(groupName)? Wont Mongoose kick it back out? Also how does it know that I want Ids, name, email in the other field of collection.
2) I need to display the names of the groups, then separately the individuals in the group. I dont think I can show the keys in this fashion. So how can I display the names of the groups.
I think Im doing this fundamentally wrong but I cant figure out the right way
const mongoose = require("mongoose"),
bcrypt = require('bcrypt-nodejs');
const passportLocalMongoose = require("passport-local-mongoose");
mongoose.createConnection("mongodb://localhost/familySite");
const userSchema = new mongoose.Schema({
username: {type:String, required: true, unique: true},
email: {type: String, required: false, unique: false},
password: { type: String, required: true},
resetPasswordToken: String,
resetPasswordExpires: Date,
profile: {picture: String, nickname: String},
permission:{app: Number}, //This will tell the app what level, 0 for Admin, 1 for User
name: {
first:String,
middle:String,
last:String,
other: {
startDate: Date,
endDate: Date,
first: String,
middle: String,
last: String
}
},
lastName: String,
address: String,
city: String,
state: String,
zipCode: Number,
phone: Number,
birthday: Date,
birthplace: String,
userCover: String,
categories: Array,
defaultPermission: Number,
events: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Event"
}],
moments: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Moments"
}],
instants: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Instant"
}],
groups:
[{family:{
type: mongoose.Schema.Types.ObjectId,
ref: "User"},
groupName: String,
name: String,
email: String
},
{friends:{
type: mongoose.Schema.Types.ObjectId,
ref: "User"},
name: String,
email: String,
groupName: String,
},
{colleagues:{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
name: String,
email: String,
groupName: String,
}
}],
likedMoments: [{ //This is for what this person likes, so they can refer later
type: mongoose.Schema.Types.ObjectId,
ref: "Moments"
}],
likedEvents: [{ //This is for what this person likes, so they can refer later
type: mongoose.Schema.Types.ObjectId,
ref: "Event"
}],
favoriteEvents: [{ //This is for personal events that are favorited
type: mongoose.Schema.Types.ObjectId,
ref: "Event"
}],
favoriteMoments: [{ //This is for personal moments that are favorited
type: mongoose.Schema.Types.ObjectId,
ref: "Moments"
}]
})
// I THINK I NEED TO MAKE THIS A METHOD SO I CAN GET IT ALL INTO APP.JS
// This uses bcrypt to hash passwords
userSchema.pre('save', function (next){
var user = this;
var SALT_FACTOR = 5;
if(!user.isModified('password')) return next();
bcrypt.genSalt(SALT_FACTOR, function (err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt,null, function (err, hash){
if(err) return next(err);
user.password - hash;
next();
});
});
});
userSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
userSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", userSchema)