MongoDB, collection name appears to be wrong - mongodb

When I try to call this request
const presidentModel = require('./modules/president.js')
app.get('/president', (req, res) => {
presidentModel.find({}, (err, result) => {
if (err) {
console.log(err)
} {
console.log(result)
}
})
})
It only returns an empty array []
then it creates a new collection with the name 'presidents'
Here is my Schema
const mongoose = require("mongoose")
const presidentSchema = new mongoose.Schema({
nickname: {
type: String,
required: true
},
fullname: {
type: String,
required: true,
},
votes: {
type: Number,
required: true
}
})
const president = mongoose.model("president", presidentSchema)
module.exports = president
The request should return the data on collection "president" but what it does is it creates a new collection with the name "presidents". I don't know where is it coming from tho

it is good practice to have your collections as plural, and therefore mongoose implicitly tries to make collections plural (as there are multiple items to be stored in them).
To override this, you can pass a third parameter to .model() with the name of the collection:
const president = mongoose.model("president", presidentSchema, "president")

Related

Overriding Mongoose _id and using properties directly from the schema

I need to create a patient model and override the _id property. I know I can override it by writing a schema like so:
const PatientSchema: Schema = new Schema({
_id: {type: String, required: true},
name: { type: String, required: true },
surname: { type: String, required: true },
provider: { type: Schema.Types.ObjectId, ref: "Provider" },
});
Is there a way to define the _id property inside the schema to reference the name + surname properties?:
_id: name+surname, (?)
or do I have to explicitly define it when creating and saving a new model?:
const patient = new Patient();
patient._id = name+surname;
Also, what should I consider if patients have the same name and surname? What is considered best practice in this case if the _id needs to = name + surname?
Thanks
you could use mongoose pre-save middleware in your schema
Basically, middlewares are functions that are called during the execution of a model query/method.
there are 2 types of middleware :
"pre" middlewares that are executed before the query
"post" middlewares that are executed after the query.
syntax :
schema.pre([method], function (next) {
console.log("pre middleware")
next();
});
schema.post([method], function (next) {
console.log("post middleware")
next();
});
/*[method] can be
"save","updateOne","findOne","findOneAndUpdate",etc...*/
//"pre" will always be executed before "post"
And depending on the method you are going to use, middlewares can change in the value of "this", there are 4 types of these:
where "this" refers to the document E.g."save"
where "this" refers to the query E.g."findOne"
where "this" refers to an aggregate E.g." aggregate"
where "this" refers to the model. E.g. "insertMany"
Solution :
const PatientSchema = new Schema({
_id: { type: String },
name: { type: String, required: true },
surname: { type: String, required: true },
provider: { type: Schema.Types.ObjectId, ref: "Provider" },
});
//will change _id before save
PatientSchema.pre("save", async function (next) {
try {
const patient = this; //the target document
patient._id = patient.name + " " + patient.surnam
next()
}
catch (err) { next(err) }
});
module.exports = model("Patients", PatientSchema);
and when you create a patient it will have the id based on the combination of their first and last name
const patient = new Patient({
name: "John",
surname: "smith"
})
await patient.save();
result : {
_id: "John smith ";
name: "John";
surname: "smith";
__v: 0;
}
[ Edit ]
if you want your id to be unique you could create an ObjectId and concatenate
it to the final id
const { ObjectId } = require("mongodb");
PatientSchema.pre("save", async function (next) {
try {
const patient = this; //the target document
const objectId = ObjectId();
patient._id = `${patient.name} ${patient.surname} ${objectId}`;
next();
} catch (err) {
next(err);
}
});

Mongo User.find return "Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters"

My Express router looks like this:
router.get('/user/events', verifyToken, (req, res) => {
User.find({
_id: req.userId,
'signedToEvents.isActive': true
}, (err, suc) => {
if (err) {
console.log(err)
}
console.log(suc);
res.status(200).send(suc)
})
})
This seems right to me, but it gives me error. Please explain me what i am doing wrong.
The userId is a Mongo userid.
The Mongoose Schema :
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const SignedToEvents = new Schema({
_id : Schema.Types.ObjectId,
eventSignedDate: {type : Date, default : Date.now()},
isActive : Boolean
})
SignedToEvents.set('toObject', { getters: true });
SignedToEvents.set('toJSON', { getters: true });
const UserSchema = new Schema({
email: String,
password : String,
age : Number,
sex : String,
createdAt: { type: Date, default: Date.now },
signedToEvents : [SignedToEvents]
})
UserSchema.set('toObject', { getters: true });
UserSchema.set('toJSON', { getters: true });
module.exports = mongoose.model('user', UserSchema, 'users');
The call made to the API (from Angular client)
getUsersEvents(){
//returns the events for a particular user
return this.http.get<any>(this.apiroot + 'user/events');
}
You can pass the user ID at least 3 ways:
QueryString
Route Parameter
Cookie
If you're using the querystring, the value would be available via: request.query.userId.
Via route parameter, it's: request.params.userId
Via cookie: request.cookies.userId
So the problem was that I had another express router that resembles the one in scope
router.get('/user/:challengeId'
Is the same as
router.get('/user/events'
Thanks for all your help anyway.

unable to populate documents using mongoose populate()

I am making an application in express using mongoose. I have a collection called users in which there is a filed called _subscriptions, which is an array of objects and each object contains a field named field which is an ObjectId for the documents of fields (this is another collection in my db).
I want to make such an API which after getting id parameter returns me a user form users collection with field attribute populated instead of its id in value of the field field. For this I am using populate method but it is not working.
This is screenshot showing users collection:
This is screenshot showing fields collection:
This is schema for field (File name Field.js):
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var FieldSchema = new Schema(
{
_id: Schema.Types.ObjectId,
name: String,
price: Number,
_categories: [{
type: Schema.ObjectId,
}],
}
);
module.exports = mongoose.model('Field', FieldSchema);`
This is schema and model for users
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
_id: Schema.Types.ObjectId,
salt: String,
provider: String,
name: String,
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
_subscriptions: [{
field: {
type: mongoose.Schema.ObjectId,
ref: 'Field',
},
status: String,
dateSubscribed: Date,
payments: [{}]
}],
role: String,
});
module.exports = mongoose.model('User', UserSchema);
This is code for user router
var Field = require('../model/Field');
var express = require('express');
var router = express.Router();
var User = require('../model/User');
router.get('/',function(req, res, next) {
User.find({}, function(err, result) {
if (err) {
console.log(err);
res.send('something wrong');
}
res.status(200).send(result);
}).populate( '_subscriptions.field').exec(function (err, story) {
if (err) return handleError(err);
console.log('Here!!!!!');
});
});
router.get('/findById/:id',function(req, res, next) {
var id = req.params.id;
User.findById(id, function(err, doc) {
if (err) {
console.error('error, no entry found');
}
res.status(200).send(doc);
}).populate('field').exec(function (err, story) {
if (err) return handleError(err);
console.log('Here!!!!!');
});
});
router.get('/getSubscriptions/:id',function(req, res, next) {
var id = req.params.id;
User.findById(id, function(err, doc) {
if (err) {
console.error('error, no entry found');
}
var type = typeof(doc);
res.status(200).send(doc);
})
});
module.exports = router;
This is where I have called app.use method:
And this is response I am getting using postman
I am looking forward for someones' assistance in resolving this issue
as i am unable to identify my mistake. Your help in this regard will be highly appreciated.
Thanking in advance
What I have understood is, In the user collection, there is _subscriptions and in _subscriptions, there is field. If this is your schema, then you should pass "_subscriptions.field" as a parameter to the populate function not "field" as you have passed currently.
So, your code for user's sub route, /findById/:id, must be like this:
router.get('/findById/:id',function(req, res, next) {
var id = req.params.id;
User.findById(id, function(err, doc) {
if (err) {
console.error('error, no entry found');
}
res.status(200).send(doc);
}).populate('_subscriptions.field').exec(function (err, story) {
if (err) return handleError(err);
console.log('Here!!!!!');
});
});

Populate does not retrieve the whole referenced object just the ids

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

Does Mongoose Actually Validate the Existence of An Object Id?

I like the validation that comes with Mongoose. We are trying to figure out whether we want to use it, and put up with the overhead. Does anyone know if providing a reference to the parent collection when creating a mongoose schema, (in the child schema, specify the object id of the parent object as a field,) does this then mean that every time you try to save the document it checks the parent collection for the existence of the refereneced object id?
I'm doing it with middleware, performing a search of the element on validation:
ExampleSchema = new mongoose.Schema({
parentId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Example'
}
});
ExampleModel = mongoose.model('Example', ExampleSchema);
ExampleSchema.path('parentId').validate(function (value, respond) {
ExampleModel.findOne({_id: value}, function (err, doc) {
if (err || !doc) {
respond(false);
} else {
respond(true);
}
});
}, 'Example non existent');
I'm using mongoose-id-validator. Works good
var mongoose = require('mongoose');
var idValidator = require('mongoose-id-validator');
var ReferencedModel = new mongoose.Schema({name: String});
var MySchema = new mongoose.Schema({
referencedObj : { type: mongoose.Schema.Types.ObjectId, ref: 'ReferencedModel'},
referencedObjArray: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ReferencedModel' }]
});
MySchema.plugin(idValidator);
No, an ObjectId field that's defined in your schema as a reference to another collection is not checked as existing in the referenced collection on a save. You could do it in Mongoose middleware, if needed.
I found this thread very helpful and this is what I came up with:
This Middleware (I think its one anyway please let me know if not) I wrote checks the referenced model for the id provided in the field.
const mongoose = require('mongoose');
module.exports = (value, respond, modelName) => {
return modelName
.countDocuments({ _id: value })
.exec()
.then(function(count) {
return count > 0;
})
.catch(function(err) {
throw err;
});
};
Example model:
const mongoose = require('mongoose');
const uniqueValidator = require('mongoose-unique-validator');
const Schema = mongoose.Schema;
const User = require('./User');
const Cart = require('./Cart');
const refIsValid = require('../middleware/refIsValid');
const orderSchema = new Schema({
name: { type: String, default: Date.now, unique: true },
customerRef: { type: Schema.Types.ObjectId, required: true },
cartRef: { type: Schema.Types.ObjectId, ref: 'Cart', required: true },
total: { type: Number, default: 0 },
city: { type: String, required: true },
street: { type: String, required: true },
deliveryDate: { type: Date, required: true },
dateCreated: { type: Date, default: Date.now() },
ccLastDigits: { type: String, required: true },
});
orderSchema.path('customerRef').validate((value, respond) => {
return refIsValid(value, respond, User);
}, 'Invalid customerRef.');
orderSchema.path('cartRef').validate((value, respond) => {
return refIsValid(value, respond, Cart);
}, 'Invalid cartRef.');
orderSchema.path('ccLastDigits').validate(function(field) {
return field && field.length === 4;
}, 'Invalid ccLastDigits: must be 4 characters');
orderSchema.plugin(uniqueValidator);
module.exports = mongoose.model('order', orderSchema);
I'm a very new dev so any feedback is greatly valued!
You can try https://www.npmjs.com/package/lackey-mongoose-ref-validator (I'm the developer)
It also prevents deletion if the reference is used on another document.
var mongooseRefValidator = require('lackey-mongoose-ref-validator');
mongoSchema.plugin(mongooseRefValidator, {
onDeleteRestrict: ['tags']
});
It's an early version, so some bugs are expected. Just fill in a ticket if you find any.
I know this is an old thread but I had the same problem and I came up with a more "modern" solution.
I'm not an expert myself, hope I'm not misleading anyone, but this seems to work:
for example, in a simple "notes" schema, which contains a user field:
const noteSchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: 'User' },
text: String
});
here's the middleware that checks if the userId exists:
noteSchema.path('user').validate(async (value) => {
return await User.findById(value);
}, 'User does not exist');