Mongoose collection design - node.js

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)

Related

mongoose populate doesn't populate

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.

How to filter documents on mongodb based on reference populated subdocuments?

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");
}

How to add value inside an array of array in mongoose by value find by ID?

I want to add the data inside the order products_id array
I tried to push the array by find it by user id and then add push it inside the products_id but i won't able to add the data.
User.findById(user_id,(err,user)=>{
if(err) throw err;
const lastOrderId = user.order.slice(-1)[0]._id
//console.log(cb.order.slice(-1)[0]._id);
User.update({'order[0]._id': lastOrderId}, {$push: {'order.0.$.products_id': 123}},{safe:true,upsert:true})
/*User.findOneAndUpdate({'_id' : user_id, 'order._id': lastOrderId}
,{$push: [{'order[0].products_id': [123123]}]},{safe:true,upsert:true})*/
})
My User Schema
const userSchema = mongoose.Schema({
_id: mongoose.Types.ObjectId,
user_name: {type:String,require: true},
email: {type:String, require: true},
password: {type:String,require:true},
full_name: {type:String},
order: [{
_id: mongoose.Types.ObjectId,
products_id: [],
date: {
type: Date,
default: Date.now
}
}]
})
After updating i want
const userSchema = mongoose.Schema({
_id: mongoose.Types.ObjectId,
user_name: {type:String,require: true},
email: {type:String, require: true},
password: {type:String,require:true},
full_name: {type:String},
order: [{
_id: mongoose.Types.ObjectId,
products_id: [5d5d7a7ea0c4aaf6212993ec, 5d627a962a5c637e7a0d5d1c, 5d627a49fa24ba7d15451afb],
date: {
type: Date,
default: Date.now
}
}]
})
You can use arrayFilter for this.You can do it like this.
User.update({user_id},{$set: { 'order.$[ord]': lastOrderId },
$push: {'order.$[ord].products.$[prd]':123}
}, {
arrayFilters: [{'_id': user_id}]
})

How to take only articles by given user Express.js

I want to display in my user/myarticlesview only articles by logged in user.
How can i do that:
Here is my User model and Article schema:
let userSchema = mongoose.Schema(
{
email: {type: String, required: true, unique: true},
passwordHash: {type: String, required: true},
salt: {type: String, required: true},
articles: [{type: ObjectId, ref: 'Article'}],
roles: [{type: ObjectId, ref: 'Role'}]
}
);
let articleSchema = mongoose.Schema (
{
author: {type: ObjectId, ref: 'User'},
title: {type: String, required: true },
content: {type: String, required: true },
phone: {type: Number, required: true },
date: {type: Date, default: Date.now() }
}
);
I want to do this in my userController and passed it to the view:
myArticlesGet: (req, res) => {
if (!req.isAuthenticated()) {
res.redirect('/');
return;
}
res.render('user/myarticles')
}
I cant figure it out how to make the query.Thank you.
As you are using express sessions you can store userId in the express session when the user is authenticated and then you can get user articles from user like that
User.find({_id: req.session.userId}).populate('articles')

Mongoose: find all referenced documents

I have 2 schemas:
var pollSchema = new mongoose.Schema({
title: String,
created: {
type: Date, default: Date.now
},
options: [{
label: String,
count: {
type: Number, default: 0
},
backgroundColor: {
type: String, default: '#fff'
}
}],
author:{
id:{
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
var userSchema = new Schema({
username: {type: String, unique:true},
email: {type: String, unique:true, lowercase: true},
password: String
});
Now each poll will store data of it's author.
Questions:
How can I redesign my schemas - so I will be able to find all the polls belong to particular user?
Or should I leave the schemas the same and find another approach?
you can still find all the polls belonging to a particular user . You have the author.id for that.
Also you can keep an array as var userSchema = new Schema({
username: {type: String, unique:true},
email: {type: String, unique:true, lowercase: true},
password: String,
polls: []
});
And every time a user polls, push the userId inside the polls array, which you can later populate or get the count.

Resources