Mongoose Remove key(s) from child schema (nested subdocuments) - javascript

I have two schemas defined, imageSchema and userSchema. imageSchema has key postedBy: userSchema. Question is shall I remove unused attributes of userSchema (like password) when nested into imageSchema, if so how to do it?
var userSchema = new mongoose.Schema({
email: String,
password: String,
name: String,
}, {versionKey: false})
var imageSchema = new mongoose.Schema({
filePath: String,
user: userSchema,
createdAt: Date,
comments: [{body: "String", by: mongoose.Schema.Types.ObjectId}]
})
...
app.post("/upload-image", async function(req, res){
if(req.session.user_id) {
...
fileSystem.rename(oldPath, newPath, function(err2){
getUser(req.session.user_id, function(user){
delete user.password
var currentTime = new Date().getTime()
Image.create({
"filePath": newPath,
"user": user,
"createdAt": currentTime,
"comments": []
}, function(err2, data){
res.redirect("/?message=image_uploaded")
})
...
So after I created a new document of Image, I checked the database, the user field of the new Image has the attribute of password, which is not desired. "delete user.password" seems not working. How should I delete this attribute? Shall I do it when defining the imageSchema, or remove it when posting the data (the app.post part)?

This can be done is multiple ways. The way i like is to exclude the field from schema and also exclude while insertion.
Exclude from schema
var imageSchema = new mongoose.Schema({
filePath: String,
user: userSchema.pick(["email", "name"]), // this will return schema with only email and name from the schema
createdAt: Date,
comments: [{body: "String", by: mongoose.Schema.Types.ObjectId}]
})
Exclude while inserting
we are omitting the user here before insertion
this can be also done with underscore/lodash _.omit(user, 'password')
getUser(req.session.user_id, function({password, ...user}){
Image.create({
"filePath": newPath,
"user": user,
"createdAt": Date.now(),
"comments": []
})
}

Related

how do I get and display the referenced data in mongodb?

I have created two model users and post where users reference post.
This is my user model.
var mongoose = require("mongoose");
mongoose.connect("mongodb://localhost:27017/demo",{ useNewUrlParser: true });
var Post = require('./post');
var UserSchema = mongoose.Schema({
username: String,
password: String,
posts:[{
type:mongoose.Schema.Types.ObjectId,
ref: Post
}] });
This is my post model
var postSchema = mongoose.Schema({
title:String,
content: String,
likes:Number
});
When I create and save a new post to a specific user I get an id in the post field of the user model. like this:-
{
posts: [
60e33a2d18afb82f8000d8f0,
],
_id: 60d9e931b5268920245c27f0,
username: 'user1',
password: '1234',
__v: 5
}
How do I access and display the contents of the post field?
Thanks!
You can get and display Reference data in MongoDB with Aggregate.
Like:
await User.aggregate([
{
$match: { _id: ObjectId(user._id)}
},
{
$lookup: { from: "Posts", localField: "post", foreignField: "_id", as: "posts" }
},
{
$unwind: { path: "$users", preserveNullAndEmptyArrays: true,}
}, {
$project: {
" Now you can select here whatever data you want to show "
}
}
])
PS: If you have any confusion please do comment.
Thanks
Mongoose has a powerful alternative to using aggregate. You can do this with populate .
Use it like this 👇 this will populate the ids inside the posts field with the actual post documents.
User.findById(id).populate('posts')
// Full example
try {
const user = await User.findById(id).populate('posts').exec();
for (let post of user.posts) {
console.log(post.content);
}
} catch (ex) {
console.error(ex);
}
If I may, a small suggestion on how to setup your document model or just to show you an alternative. Don't reference the Posts inside your User Document. Do it the other way round and reference the User inside your Post Document.
const postSchema = mongoose.Schema({
title : String,
content : String,
likes : Number,
user : { type:mongoose.Schema.Types.ObjectId, ref: 'User' }
});
// GET ALL POSTS FROM ONE USER
Post.find({user:id});
This way you don't need to use populate when trying to show all posts of 1 user.

How do i use populate with a custom ObjectId of a type: String?

I have a firebase project that I use for authentication. I also save additional user info in the mongodb and assign the uid of the firebase user to the _id field of the user model. In order to do that, I have to set the type of ObjectId to a String, otherwise mongodb doesn't let me save the user as the firebase uid is a bit longer than ObjectId. It seems that the ObjectId is of type:String, I can no longer use populate on my queries.
Here are the models:
const UserSchema = new Schema({
_id: String,
name: String,
});
const SurveySchema = new Schema({
user_id: { type: String, ref: "users" },
category: String,
});
I tried to set user_id: { type: mongoose.ObjectId, ref: "users" }, but I just get an error(Cast to ObjectId failed) instead of undefined.
Here is the controller where I use populate:
const SurveyList = await Survey.find(
{
user_id: req.currentUser.uid,
category: "example",
},
"_id user_id category createdAt updatedAt"
).populate("user_id");
I checked, the ids match, but I still get undefined. The populate used to work when I had regular mongo ObjectIds, but after I started using firebase it no longer works.
The response I get is like this:
"SurveyList": [
{
"status": "1",
"_id": "60abcd94e9cddb2ba44f24b4",
"user_id": null,
"category": "Health",
"createdAt": "2021-05-24T16:00:20.688Z",
"updatedAt": "2021-05-24T16:00:20.688Z"
}
]
Please note that the error began occurring only after I changed _id to type:String. It used to work fine when it was a default mongoose.ObjectId
You cannot populate the field that you're using to store the reference to the user's id. That field is the one that will be used in order to populate the virtual field. If what you want to do is having a virtual field SurveyList[i].user that retrieves the user's data in each SurveyList entry, you need to create it:
SurveySchema.virtual("user", {
ref: "users",
localField: "user_id",
foreignField: "_id",
justOne: true,
});
Then you'll need to populate the virtual field:
const SurveyList = await Survey.find(
{
user_id: req.currentUser.uid,
category: "example",
},
"_id user_id user category createdAt updatedAt"
).populate("user");

Node and mongoose - Acces object inside another object

I got this schema:
var mongoose = require('mongoose');
var userSchema = new mongoose.Schema({
email: {type:String, unique: true, lowercase: true},
cpf: String,
password: String,
profile:{
name:{type:String, default:''},
photo: {type:String, default:''}
},
adress:String,
history:[{
date: Date,
paid:{type:Number, default:0},
item:{type:mongoose.Schema.Types.ObjectId, ref:'Products'}
}]
});
module.exports = mongoose.model('User', userSchema);
But, when I use User.create({email:req.body.email, password:req.body.password, profile.name: req.body.name}), when I try to pass some value to profile.name, console gives me an error. SyntaxError: Unexpected token .
How can I acess this object and pass some value for him?
Javascript lets you omit the quotes around keys on objects if your key contains a limited set of characters. The period . isn't one of them so you need to quote your key.
User.create({
email: req.body.email,
password: req.body.password,
'profile.name': req.body.name
})

Mongoose schema

I have these mongoose schemas:
var ItemSchema = new Schema({
"pieces": Number,
"item": { type: Schema.Types.ObjectId, ref: 'Items' }
});
var cartSchema= new Schema({
"items": [ItemSchema]
});
but when I want to push a new item in items, mongoose add an _id field(on the new item) but I don't understand why.
if you want to add item without _id field then you should add { _id: false } in ItemSchema.
var ItemSchema = new Schema({
"pieces": Number,
"item": { type: Schema.Types.ObjectId, ref: 'Items' }
}, { _id: false });
Mongoose assigns each of your schemas an _id field by default if one is not passed into the Schema constructor. The type assigned is an ObjectId to coincide with MongoDB's default behavior. If you don't want an _id added to your schema at all, you may disable it using this option.
You can only use this option on sub-documents. Mongoose can't save a document without knowing its id, so you will get an error if you try to save a document without an _id.
Link: http://mongoosejs.com/docs/guide.html#_id

Manage authentication of different types of users in express.js using passport.js

I am having problem managing the states of different types of users using Passport.js in Express.js 4.x.
I have 3 kinds user collections in my mongodb database
1. Member (has his own profile page)
2. Operator (has his own dashboard)
3. Admin (handles the backend)
I have created their separate Login/Registration systems. But only member seems to work, and the others don't. I have even written different sets of login/registration strategies for each user.
Like for the member passport.use('signup') and passport.use('login').
for operator passport.use('op-signup') and passport.use('op-login') and so on.
What I think is that I am not using the correct approach for handling users, means the collections don't need to be separated but role based in a single collection. Right ?
Here is the current mongoose models I have right now;
// Member Schema
var MemberSchema = new Schema({
username: String,
password: String,
name: { first: String, last: String },
locality: String,
// and other attributes
});
module.exports = mongoose.model('Member', MemberSchema);
// OperatorSchema
var OperatorSchema = new Schema({
username: String,
password: String,
name: { first: String, last: String },
officeAddress: String,
privatePhone: Number,
// and other attributes related to the operator
});
module.exports = mongoose.model('Operator', OperatorSchema);
Is the above approach correct or like this ?
var UserSchema = new Schema({
username: String,
password: String,
roles: {
member: { type: Schema.Types.ObjectId, ref: 'Member' },
operator: { type: Schema.Types.ObjectId, ref: 'Operator' },
admin: { type: Schema.Types.ObjectId, ref: 'Admin' }
}
});
module.exports = mongoose.model('User', UserSchema);
// and then plug the sub models to this parent one
// Member Schema
var MemberSchema = new Schema({
_user: { type: Schema.Types.ObjectId, ref: 'User' },
name: { first: String, last: String },
locality: String,
// and other attributes
});
module.exports = mongoose.model('Member', MemberSchema);
// OperatorSchema
var OperatorSchema = new Schema({
_user: { type: Schema.Types.ObjectId, ref: 'User' },
name: { first: String, last: String },
officeAddress: String,
privatePhone: Number,
// and other attributes related to the operator
});
module.exports = mongoose.model('Operator', OperatorSchema);
I am quite confused here and in a stuck situation, because when a user state is managed in session after login, the user object is exposed to the request object, and so it can only handle one type of user at a time, and may be member, operator and admin can't log in at the same time from the same browser.
So how do I manage all of these user as different instances in the browser ?
I am quite a newbie in Node.js and coming from a PHP background where managing user states was a breeze :)
What i would do is to add plugins, because you are duplicating username and password field, it is very redundant
models/plugins/member.js
module.exports = function(schema) {
schema.add({
// All the appropriate fields that your member schema need
role: String,
});
}
models/user.js
var member = require(./plugins/member);
var UserSchema = mongoose.Schema({
username: String,
password: String
});
UserSchema.plugins(member);
Later on when you want to check which user could access to which route, use middleware to check it
create this in your passport configuration
exports.requireRole = function(role) {
return function(req, res, next) {
if (req.user && req.user.role === role) next();
else
res.send(404);
}
}
In your route later
app.get('/profile', requireRole('member'), function(req, res) {
// do whatever you want to do
});
app.get('/dashbord', requireRole('operator'), function(req, res) {
// do whatever you want to do
});
There are a lot of ways to implement different access level to a user. This method is one of many.
The best solution would be to use schema inhertiance. That is why we use an ORM like mongoose.
var VehicleSchema = mongoose.Schema({
make : String,
}, { collection : 'vehicles', discriminatorKey : '_type' });
var CarSchema = VehicleSchema.extend({
year : Number
});
var BusSchema = VehicleSchema.extend({
route : Number
})
var Vehicle = mongoose.model('vehicle', VehicleSchema),
Car = mongoose.model('car', CarSchema),
Bus = mongoose.model('bus', BusSchema);
var accord = new Car({
make : 'Honda',
year : 1999
});
var muni = new Bus({
make : 'Neoplan',
route : 33
});
accord.save(function(err) {
muni.save(function(err) {
// vehicles are saved with the _type key set to 'car' and 'bus'
});
})
At this point in MongoDB you will have documents similar to this
{ "_type" : "car", "make" : "Honda", "year" : 1999, "_id" : ObjectId("5024460368368a3007000002"), "__v" : 0 }
{ "_type" : "bus", "make" : "Neoplan", "route" : 33, "_id" : ObjectId("5024460368368a3007000003"), "__v" : 0 }
Source
when querying
Vehicle.find({}, function(err, vehicles) {
console.log(vehicles[0]); // vehicles[0] instanceof Car === true
console.log(vehicles[1]); // vehicles[1] instanceof Bus === true
});
Checkout source / more examples by looking at briankircho little cheatsheet enter link description here

Categories

Resources