Referencing Object Id not working in Mongoose 4.11.6 - node.js

I have this problem. Basically, I have 2 schemas - a User schema and a Document schema. The Document schema has an owner which references the _id field of documents in the User collection.
The problem is that I am still able to save documents in the Document collection with owner ids that do not exist in the User collection which should not be so.
Here is my User schema and Document schema respectively
const UserSchema = new Schema({
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
email: {
type: String,
validate: [{ validator: value => isEmail(value), msg: 'Invalid email.'
}],
unique: true,
required: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
default: false,
},
}, {
timestamps: true,
});
const User = mongoose.model('User', UserSchema);
And the Document Schema
const DocumentSchema = new Schema({
title: {
type: String,
required: true,
},
text: {
type: String,
},
access: {
type: String,
enum: ['public', 'private'],
default: 'public',
},
owner: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
}, {
timestamps: true,
});
const Document = mongoose.model('Document', DocumentSchema);
Any help will be appreciated thanks.

For that situation you can add pre save function in your Document schema that will call before save your Document.
const DocumentSchema = new Schema({
// ...
}, {
timestamps: true,
});
DocumentSchema .pre("save",function(next) {
var self = this;
if (self.owner) {
mongoose.models['User'].findOne({_id : self.owner }, function(err, existUser){
if(err){
return next(false, err);
}
if(!existUser)
return next(false, "Invalid user reference");
else
return next(true);
});
} else {
next(false, "Owner is required");
}
});
const Document = mongoose.model('Document', DocumentSchema);

Related

post author Id turn into author username in node js for mongodb

i am trying to have my post's author's name in frontend. so i want to find the post according to it's user Id. but in model schema i used obejct Id of user in post Schema.
Here is my userSchema:
const mongoose = require('mongoose');
// user schema
const userSchema = new mongoose.Schema(
{
email: {
type: String,
trim: true,
required: true,
unique: true,
lowercase: true
},
name: {
type: String,
trim: true,
},
password: {
type: String,
required: true
},
salt: String,
bio: {
type: String,
trim: true
},
role: {
type: String,
default: 'subscriber'
},
resetPasswordToken: String,
resetPasswordExpire: Date,
},
{
timestamps: true
}
);
module.exports = mongoose.model('User', userSchema);
here is my postSchema model:
const mongoose = require("mongoose");
const PostSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
comments: [{
text: String,
created: { type: Date, default: Date.now },
postedBy: { type: mongoose.Schema.ObjectId, ref: 'User'}
}],
created: {
type: Date,
default: Date.now
},
creator: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
},
{
timestamps: true,
},
);
const Post = mongoose.model("Post", PostSchema);
module.exports = Post;
and here is my router for post lists by a specific user id:
exports.postByUser=async(req,res)=>{
try
{
const userID=async()=>{
await User.findById({ _id:req.params.id})
.then(posts=>{
res.status(200).json(posts.name)
})
}
await Post.find({creator: req.params.id})
.then(posts=>{
res.status(200).json(posts)
})
}catch(error){
res.status(500).send({error: error.message});
};
}
router.route('/post/mypost/:id').get(requireSignin,postByUser);
my target is to get a post list where every post's creator would have the user name. how can i achieve that in nodejs?
i have solved this way:
exports.postByUser=async(req,res)=>{
try
{
await Post.find({creator: req.params.id})
.populate({path:'creator', select:'name -_id'})
.then(post=>{
res.status(200).json(post)
})
}catch(error){
res.status(500).send({error: error.message});
};
}
and it worked

Mongoose(mongoDB) Linking multiple schema's

Im relatively new to MongoDB and Mongoose. Im much used to MySQL so in used to inner joining tables on calls. Ive read a lot that you can link two Mongoose Schemas to achieve the same outcome. How would like like the two schemas together to when I make a call to get a chore by id it'll return the chore and then for the assignedTo & createdBy have the user scheme data for the said userId?
Chore Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ChoreSchema = new Schema({
title: {
type: String,
required: true
},
desc: {
type: String,
required: true
},
time: {
type: Number,
required: true
},
reaccurance: {
type: [{
type: String,
enum: ['Daily', 'Weekly', 'Bi-Weekly', 'Monthly']
}]
},
reward: {
type: Number,
required: true
},
retryDeduction: {
type: Number,
required: false
},
createdDate: {
type: Date,
default: Date.now
},
createdBy: {
type: String,
required: true
},
dueDate: {
type: Date,
required: true
},
status: {
type: [{
type: String,
enum: ['new', 'pending', 'rejected', 'completed', 'pastDue']
}],
default: ['new']
},
retryCount: {
type: Number,
default: 0,
required: false
},
rejectedReason: {
type: String,
required: false
},
familyId: {
type: String,
required: true
},
assignedTo: {
type: String,
required: false,
default: ""
}
});
let Chores = module.exports = mongoose.model('Chores', ChoreSchema);
module.exports.get = function (callback, limit) {
Chores.find(callback).limit(limit);
};
User Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
role: {
type: [{
type: String,
enum: ['Adult', 'Child']
}]
},
birthday: {
type: String,
required: false
},
familyId: {
type: String,
required: true
},
balance: {
type: Number,
required: true,
default: 0.00
}
});
let Users = module.exports = mongoose.model('Users', UserSchema);
module.exports.get = function (callback, limit) {
Users.find(callback).limit(limit);
};
Im trying to link ChoreSchema.createdBy & ChoreScheme.assignedTo by UserSchema._id
How I make the call in Node.js:
exports.index = function(req, res) {
Chore.get(function(err, chore) {
if (err)
res.send(err);
res.json({
message: 'Chore List',
data: chore
});
});
};
Mongoose has a more powerful alternative called populate(),
which lets you reference documents in other collections.
https://mongoosejs.com/docs/populate.html
Here is how you can link ChoreSchema.createdBy and ChoreScheme.assignedTo by UserSchema._id
var mongoose = require('mongoose');
const { Schema, Types } = mongoose;
var UserSchema = new Schema({
firstName: { type: String, required: true },
...
})
var ChoreSchema = new Schema({
title: { type: String, required: true },
...
//The ref option is what tells Mongoose which model to use during population
assignedTo: { type: Types.ObjectId, ref: 'Users' },
createdBy: { type: Types.ObjectId, ref: 'Users' },
})
let Chores = mongoose.model('Chores', ChoreSchema);
let Users = mongoose.model('Users', UserSchema);
Then in your express route handler you can populate assignedTo & createdBy like this
router.get('/chores/:id', function (req, res) {
const choreId = req.params.id;
Chores.find({ _id: choreId })
.populate('createdBy') // populate createdBy
.populate('assignedTo') // populate assignedTo
.exec(function (err, chore) {
if(err) {
return res.send(err)
}
res.json({ message: 'Chore List', data: chore });
});
})

Mongoose is not returning all fields in the document using a simple find()

I have the following Schema:
let User = new Schema({
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
firstName: {
type: String,
required: false
},
lastName: {
type: String,
required: false
},
templates: {
type: Schema.Types.ObjectId,
ref: 'TemplateInstance',
required: false
}
},{
collection: 'users',
timestamps: true
});
And the following Mongoose code:
exports.getUsers = (req, res) => {
User.find((err, users) => {
if(err)
return res.status(400).json( { 'users_get_all': 'failure', 'err': err } );
return res.status(200).json( { 'users_get_all': 'success', 'users': users } );
});
};
Initially, each user Document does not have anything in the 'templates' field because after the user creates their account, that's when they get to attach templates to it. I have manually added some Template ObjectIDs to the 'templates' field of some users but when I run the getUsers() function, the user documents are returned but with no 'templates' field:
{"users_get_all":"success","users":[{"_id":"5b39f9da294d041b58f97cb3","email":"testemail#email.com","password":"password","firstName":"firstName","lastName":"lastName","createdAt":"2018-07-02T10:09:30.400Z","updatedAt":"2018-07-02T10:21:34.579Z","__v":0},{"_id":"5b39ff5723d93c17bc00eabf","email":"testemail2#email.com","password":"password","firstName":"firstName2","lastName":"lastName2","createdAt":"2018-07-02T10:32:55.308Z","updatedAt":"2018-07-02T10:32:55.308Z","__v":0}]}
If I look at the MongoDB in something like Studio 3T, the templates array definitely has ObjectIDs in it that refer to Templates in the Template collection.
Any idea why the 'templates' field is not being returned?
Update your schema as follows:
let User = new Schema({
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
firstName: {
type: String,
required: false
},
lastName: {
type: String,
required: false
},
templates: [{
type: Schema.Types.ObjectId,
ref: 'TemplateInstance',
required: false
}]
},{
collection: 'users',
timestamps: true
});
As in database, you have templates in the array and you were declaring it an object in the schema.

Questions about Mongoose Schema design

I'm completely new to the NoSQL world and it's been difficult to wrap my mind around it. This week I was learning MongoDB (Mongoose) with Node.js and here is my current schema:
var eventDataSchema = new Schema({
_id : Number,
notes : {type: String, required: true},
start_date : {type: Date, required: true},
end_date : {type: Date, required: true},
}, {
id : false,
collection : 'event-data'
});
eventDataSchema.plugin(AutoIncrement);
var EventData = mongoose.model('EventData', eventDataSchema);
Now that this is working, I would like to add a user and password and have access to have personal access to EventData.
Also... later if I want to send a JSON of only the eventData, but not the user to my javascript, how would I do that?
The way I am currently sending my eventData to my js in this format:
router.get('/data', function(req, res){
EventData.find({}, function(err, data){
if (err) {
console.error('Error occured');
}
res.send(data);
});
});
Thanks again
As i can understand you want to add events key in your schema. Then your schema will be like that:
var userSchema = new Schema({
user: { type: String, required: true, trim: true },
password: { type: String, required: true, trim: true },
events: [{
notes: { type: String,required: true, trim: true },
start_date: { type: Date,required: true },
end_date: { type: Date,required: true }
}]
}
userSchema.plugin(AutoIncrement);
var userSchema = mongoose.model('userSchema', userSchema);
});
If the above code is not working then you can create two schema,one for user and other for eventData, and can populate your eventData in userSchema.
so your code will be like that:
userSchema.js:
var userSchema = new Schema({
user: { type: String, required: true, trim: true },
password: { type: String, required: true, trim: true },
events: {type: mongoose.Schema.Types.ObjectId, ref: 'EventData' }
userSchema.plugin(AutoIncrement);
module.exports = mongoose.model('userSchema', userSchema);
});
And your eventDataSchema will be:
eventSchema.js:
var eventDataSchema = new Schema({
notes: { type: 'string',required: true, trim: true },
start_date: { type: Date,required: true },
end_date: { type: Date,required: true }
}
eventDataSchema.plugin(AutoIncrement);
module.exports = mongoose.model('EventData', eventDataSchema);
});
and then you can get the result like that:
index.js:
var eventSchema = require('./eventSchema');
var userSchema = require('./userSchema');
var populate = [{
path: 'events',
model: 'EventData',
select: '_id notes start_dat end_date'
}];
var find = function (query) {
return userSchema.find(query).populate(populate).exec();
}
console.log(find());
Result:
{
_id:cfgvhbjnkmkdcfxghgjklxnmbxhdhjxjhjhgx,
user: John Doe,
password: 123,
events: [ { _id: 1gfye56785g3ycgevhxeftx568765egcd,
notes: Event A,
start_date: 1/1/01,
end_date: 1/1/01
} ]
}

Using SchemaTypes other than ObjectId for Population (Mongoose)

I am relatively new to node.js and mongoose, so I've been trying to get population working for mongoose. According to the official mongoose documentation, Strings can be used as refs:
Note: ObjectId, Number, String, and Buffer are valid for use as refs.
Based on this, I've been trying to populate a field that is of type String, rather than ObjectId. However, every time I try to do so, I get a CastError:
CastError: Cast to ObjectId failed for value "testuser" at path "_id"
If it helps, here's my code:
/=============== In user.js ===============/
var UserSchema = new mongoose.Schema({
_username: { type: String, required: true, unique: true },
password: { type: String, required: true },
date_created: { type: Date, required: true, default: Date.now() },
last_accessed: { type: Date, required: true },
admin_access: { type: Boolean, required: true }
});
UserSchema.post('save', function(user) {
// Settings Model
var Settings = require('./settings');
var setting = new Settings.SettingsModel({
_user: user._username
});
setting.save(function(err) {
if (err) throw err;
console.log('Successfully created settings for user: ' + setting);
Settings.SettingsModel.findOne({ _user: user._username }, function(err, setting) {
Settings.SettingsModel.populate(setting, { path: '_user' }, function(err, setting) {
if (err) throw err;
console.log('User of setting is ' + setting._user);
});
});
});
});
/=============== In settings.js ===============/
var SettingsSchema = new mongoose.Schema({
_user: { type: String, ref: 'User', unique: true },
defaultCurrency: { type: String, default: 'USD', required: true },
secondaryCurrency: { type: String }
});
Basically, settings for a user is created after a user has been created.
Any help is appreciated. Thanks!
While you can use types other than ObjectId for refs, the refs can't reference any field besides _id. So to do this you'd have to declare _id of UserSchema as a String and set its value to the same value you give username:
var UserSchema = new mongoose.Schema({
_id: { type: String },
_username: { type: String, required: true, unique: true },
password: { type: String, required: true },
date_created: { type: Date, required: true, default: Date.now() },
last_accessed: { type: Date, required: true },
admin_access: { type: Boolean, required: true }
});

Resources