How to set a field value with same document id in mongoose - node.js

I want to create a schema on mongoose where a field value is the same as document id.
Desired output:
{
"_id": "61c75bf3b151c6c6854c83db",
"password":"some password",
"name":"some name",
"linkId": "61c75bf3b151c6c6854c83db"
}
Here document id and linkId is same. How can I do that when creating a new document? It maybe redundant but is it possible?

You can use pre-hook middleware for the save method, when you insert any document it will clone the _id field in to linkId field,
// SCHEMA
const SchemaObj = new mongoose.Schema(
// your schema...
);
// PRE MIDDLEWARE
SchemaObj.pre('save', function (next) {
this.linkId = this._id;
next();
});

Related

Why we use ObjectId(id) in Mongodb?

What is the purpose of using Object Id here?
app.get('/product/:id', async(req, res) => {
const id = req.params.id;
const query = { _id: ObjectId(id) };
const product = await productCollection.findOne(query);
res.send(product);
})
The field name _id is reserved for use as a primary key; its value must be unique in the collection, is immutable, and may be of any type other than an array as default mongo use it as ObjectId.
So the purpose you parse the id it because id its a String and in you database _id it not a string it an ObjectId.
Note: if you are using mongoose you don't need to parse the id to ObjectId cause mongoose auto cast that field to ObjectId

How to prevent Mongoose findOneAndUpdate to add ObjectId automatically

I have a collection with the following document
{_id:'12345',account:{ba:0,br:0,ac:0}, scores:{a:0,b:0}}
I have a resolver and after running it
const input = {account:{ba:1,br:2,ac:3}, scores:{a:1,b:1}}
const profileId='12345'
const res = await modelProfile.findOneAndUpdate({ _id: profileId }, input);
it finds the document and properly changes it but it is adding also _id to each object there :
{_id:'12345',account:{_id:'46456',ba:1,br:2,ac:3}, scores:{_id:'4645677', a:1,b:1}}
How can I prevent mongoose from automatically adding _id to the SUBDOCUMENT objects of the document that they don't have ObjectId already. (as you saw, the document has ObjectId but objects inside as subdocuments does not have it)
I found the solution, I should change schema
var subSchema = mongoose.Schema({
// your subschema content
}, { _id : false });

Prevent _id field from being supplied in a MongoDB query

I'm using Mongoose in NodeJS to control a MongoDB database.
I'm creating an API and for obvious security reasons, I want to prevent the auto generated document _id field from getting replaced by a manually generated one in the API request.
Schema:
{ name: String }
Creating a document:
const record = {
_id: '5e35517cc894c90327a34baf'
name: 'bob'
}
const insertRecords = async () => {
await Quiz.create(record);
};
insertRecords();
Results in the following document:
{
_id: '5e35517cc894c90327a34baf'
name: 'bob'
}
As can be seen, the _id supplied in the query, as long as it's a valid ObjectID, would replace the _id that was supposed to be auto generated by mongo.
Is there a way to check if this _id field is in the query so that I can reject the API request? The .create method triggers the pre save middleware hook which would always have the _id of the final document so I cannot depend on it to know whether the _id was in the query or it's the auto generated one.
The only option I found is to disable the _id field altogether but this does not make sense.
Solution #1 - Use .create() method with an explicit object.
It's actually easier than you think. This is self-explanatory - we only define what we want to allow. Mongoose will ignore anything that's not in the object.
const record = {
_id: '5e35517cc894c90327a34baf'
name: 'bob'
}
const insertRecords = async () => {
await Quiz.create({
name: record.name // only allow names.
});
};
insertRecords();
Solution #2 - Define a function to clear unwanted objects.
You can define a helper function to clear out unwanted fields.
const filterObj = (obj, ...allowedFields) => {
const newObject = {};
// If the current field is one of the allowed fields, keep them in the new object.
Object.keys(obj).forEach((el) => {
if (allowedFields.includes(el)) {
newObject[el] = obj[el];
}
});
return newObject;
};
How to use:
const filteredRecord = filterObj(record, 'name'); // arbitrary list of allowed fields. In this case, we'll only allow 'name'.
await Quiz.create(filteredRecord);

How to populate the User object with Mongoose and Node

I am trying to add a couple of attributes to the scaffolded MEAN.js User entity.
locationName: {
type: String,
trim: true
}
I also have created another entity Book connected with User. Unfortunately, I think I do not quite grasp the concept behind the populate method because I am not able to "populate" the User entity with the locationName attribute.
I tried the following:
/**
* List of Books
*/
exports.list = function(req, res) {
Book.find().sort('-created').populate('user', 'displayName', 'locationName').exec(function(err, books) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(books);
}
});
};
Unfortunately, I get the following error:
/home/maurizio/Workspace/sbr-v1/node_modules/mongoose/lib/connection.js:625
throw new MongooseError.MissingSchemaError(name);
^
MissingSchemaError: Schema hasn't been registered for model "locationName".
Any suggestion?
Thanks
Cheers
The error is clear, you should have a schema for the locationName.
If your location is just a string property in your user model and does not refer to separate model, you don't need and shouldn't use populate with it, it will simply be returned as a property of the returned user object from mongoose find() method.
If your want to make your location a stand alone entity (different mongodb document), you should have a mongoose model that defines your location object, aka have a file in your app\models name for example: location.server.model.js that contains something like:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var LocationSchema = new Schema({
_id: String,
name: String
//, add any additional properties
});
mongoose.model('Location', LocationSchema);
Note that the _id here replaces the auto generated objectId, so this has to be unique, and this the property you should refer to in your User object, meaning if you have a location like this:
var mongoose = require('mongoose'),
Location = mongoose.model('Location');
var _location = new Location({_id:'de', name:'Deutschland'});
you should refer to it in your User object like this:
var _user=new User({location:'de'});
//or:
var _user=new User();
_user.location='de';
then you should be able to populate your location object with your user, like this:
User.find().populate('location').exec(function(err, _user) {
if (err) {
//handle error
} else {
//found user
console.log(_user);
//user is populated with location object, makes you able to do:
console.log(_user.location.name);
}
});
I suggest you to further read in mongodb data modeling and mongoose Schemas, Models, Population.

Mongoose findByIdAndUpdate removes not updated properties

I have the following Mongoose model:
var mongoose = require('mongoose');
var userSchema = mongoose.Schema({
facebook: {
name: String,
email: String,
customerId: String
}
});
var User = mongoose.model('User', userSchema);
When I update a part of this document using findByIdAndUpdate
User.findByIdAndUpdate(id, {
$set: {
facebook: {
name: name
}
}
});
name gets updated, while email and customerId get removed (unset?).
I didn't find this documented.
Is there a way to update only specific document properties with findByIdAndUpdate?
FindByIdAndUpdate is actually Issues a mongodb findAndModify update command by a documents id.
The point is you are setting an object to overwrite the old object. if you want to update a field you need to modify your update object.
User.findByIdAndUpdate(id, {
$set: {
'facebook.name':name
}
});
This will only update the name field keeping rest of the field of the old object.

Resources