Save entity with id created in code, mongoose - node.js

How can I do to pass the id of a document from outside and not that mongoose generates it?
I need the objects to be stored with the id I tell them, but mongoose overwrites it and saves it with the one he wants. I tried several ways now and nothing happens.
It is an entity that I am distributing through events in several databases, so I need it to be stored with the id I want to maintain the integrity of the data.
Now I have this and it says "document must have an _id before saving", but the id I have already put it, does not recognize it.
The scheme is like this:
const schema = new Schema({
_id: { type: String },
name : { type: String },
});
I also tried with this, and the error is the same:
const schema = new Schema({
_id: { type : String },
name : { type: String },
},
{
_id: false
});
I am passing the object like this:
Item.create({ _id: 'my uuid here', name: 'something' });
but when it is saved it remains with the id generated by mongoose replacing mine, that is, it changes it to me with a _id: '5twt563e3j5i34knrwnt43w'

Your syntax should work, but sometimes mongoose acts weird.
You can try this syntax (works on my project) :
const item = new Item({ name: 'something' });
item._id = 'my uuid here';
await item.save();

Instead of using a random uuid, you need to use a mongoDB objectID. Mongoose can also create that,
var mongoose = require('mongoose');
var id = mongoose.Types.ObjectId();
Store this id in the collection,
Item.create({ _id: id, name: 'something' });

Related

schema option _id: false and document must have an _id before saving error happening

I am trying to create a user document through this way:
// create the document ---------------
const id = mongoose.Types.ObjectId()
let userDoc = await Admin.create({ ...req.body, _id: id, by: id })
Schema:
adminSchema = new mongoose.Schema({
// some other fields, firstName, lastName ... etc
by: {
type: mongoose.Schema.ObjectId,
ref: 'Admin',
required: [true, "the 'by' field is required"],
immutable: true,
}
}, { _id: false })
Model:
const Admin = mongoose.model('Admin', adminSchema, 'users')
My schema doesn't have an _id property.
Now I want to have the _id field and the by field has the same value, which is a server-side generated id.
Mongoose is throwing this error:
Error: MongooseError: document must have an _id before saving at
.../node_modules/mongoose/lib/model.js:291:18
update:
I updated my question, I added the schema options, and now I know the reason why this error is happening. it's because of the _id: false schema option that I have set. But I need this option because I don't want to see _ids in the responses that I send to the clients. is there a workaround? because this option looks like its doing two unrelated things
Using Mongoose 6.4
I solved this by removing the _id: false schema type option.
and to remove the _id from the responses without having to pollute the routes with _.omit()s or deletes everywhere, I added the following schema type options to the schema:
toObject: {
virtuals: true,
transform(doc, ret) {
delete ret._id
},
},
Now the real question is, why does simply adding the option _id: false results in the Mongoose error when you're generating the id on the server-side without the help of Mongoose?
Error: MongooseError: document must have an _id before saving at
.../node_modules/mongoose/lib/model.js:291:18
I partially answered my own question, but for this one... I really don't know.
Based on your comment, if you want the response the user receives to not contain the _id you can:
Get the document
Remove the _id property and return this object without the _id (Or create a new object to avoid problems).
A brief example could be:
let responseDoc = await Admin.findOne({ _id: id });
delete responseDoc["_id"]
// responseDoc is now ready for use. Note that if I am not mistaken it is still a document.

How do I prevent Schema fields from being inserted into subdocument?

I'm making a dating app in node js and vue, and everything works however I wish to exclude password from being inserted into subdocument upon creation of a conversation. Right now I know that i can say .select('-password') when using User.findOne() but it doesn't work, when adding the user schema as a subdoc to my Conversations schema, which has user_one and user_two, each referring to a User schema. I need the password field, so I can't ommit it when creating a schema. Right Now my code looks like this:
User.findOne({ _id: fromUserId }, (errUserOne, userOne) => {
User.findOne({ _id: toUserId }, (errUserTwo, userTwo) => {
conversation = new Conversation({
_id: mongoose.Types.ObjectId(),
user_one: userOne,
user_two: userTwo
});
conversation.save();
const message = new Message({
_id: mongoose.Types.ObjectId(),
conversation_id: conversation._id,
message: req.body.message,
user_id: fromUserId
});
message.save();
res.sendStatus(201);
});
});
However this code saves the password to the Conversation collection, which I don't want.
User.find({ _id: :{ $in : [fromUserId,toUserId] }, { password:0 } , (err, userArray) => {
//your code goes here
});
Two things, You are querying two time for getting users. You can merge it into single query and for excluding the password field you can pass {password:0}. Which will exclude it in the documents.
also while you define Conversation schema don't make user_one and user_two type of user. Instead define only whatever properties of user you want to save like:
var Conversation = new Schema({
_id : ObjectId,
user_one : {
_id: ObjectId,
//all other fields
},
user_two : {
_id: ObjectId,
//all other fields
},
});

Mongoose can't find any elements after changing property type

I originally have these two schemas:
var UserSchema = new Schema({
first: String,
last: String
});
var SaleSchema = new Schema({
createdAt: Date,
registeredBy: { type: Schema.ObjectId, ref: 'User' }
});
But I want to edit my SaleSchema to save the user name instead of the ID, so I changed it for:
var SaleSchema = new Schema({
createdAt: Date,
registeredBy: String
});
Next, I wanted to edit all the Sales documents and replace the user IDs on registeredBy for the user's full name, but I can't seem to be able to perform a find query for the old ID's.
Long story short, this query returns no matches on mongoose, but it works perfectly using the mongo console:
Mongoose
Sale.find({ registeredBy: '57ea0cbb47431f0b43b87d42' })
.then(results => res.json(results))
.catch(err => res.status(500).json(err));
// result: []
MongoDB console
db.sales.find({ registeredBy: '57ea0cbb47431f0b43b87d42' })
// result: 8 elements
After I modify my schema's property back to ObjectId, the mongoose query works again. Since I need to migrate to a new datatype, I want to be able to query and store both types of values. Is this possible?
Good question this is a complicated edge case. I am not super familiar with Mongoose specifically, but one way to do this would be to migrate your data at a lower level. For example, create a mongo script that uses the low-level mongo API to do the migration, something along the lines of:
db.sales.find().forEach(function(doc){
var user = db.users.find({ _id: doc.registeredBy });
db.sales.update({ _id: doc._id, }, {
$set: { registeredBy: user.first + ' ' + user.last }
});
});
This is similar to what a module like https://github.com/balmasi/migrate-mongoose does, but I've personally found it easier to use mongo scripts on the cli directly.
mongo < sale-schema-migration.js

Nested objects are not update

Allora, I'm using mongoose for the first time and I decided to create 2 schemes: the first one represents a user and the second one represents his enquires. Users have an array of enquires like:
var userSchema = new mongoose.Schema({
name: String,
enquires: { type : [Enquire.schema] , "default" : [] },
});
var enquireSchema = new mongoose.Schema({
status: {type: String, 'default': 'pending'},
enquire: String,
});
I see that if I search for an enquire and update its status, it doesn't update the same enquire on the user's array, meaning that they are different object. I don't want to save an array of IDs as it will be the same as a relational database, so I see only 1 solution which is forgetting about the enquire scheme and use only the User scheme. Is it the way mongoose works? For every relationship do I have to insert everything like nested object?
I think you should use references to achieve what you want to achieve.
For more information on mongoose references and populate see Mongoose Populate documentation.
Try this, It may help you.
User Schema :
var userSchema = new mongoose.Schema({
name: String,
enquires: [{ type : mongoose.Schema.Types.ObjectId , ref : 'Enquiry' }]//array of enquiries
});
var User = mongoose.model('User',userSchema );
module.exports = User;
Enquiry Schema :
var enquireSchema = new mongoose.Schema({
status: {type: String, 'default': 'pending'},
enquire: String,
});
var Enquiry = mongoose.model('Enquiry',enquireSchema );
module.exports = Enquiry ;
Working :
create a new Enquiry.
Push it's ID(_id) into user's enquires array.
var enquiry = new Enquiry();
enquiry.enquire = "Dummy enquiry";//set the enquiry
enquiry.save(function(err,result){
if(!err){
//push 'result._id' into users enquires array
}
});
whenever you update an enquiry, it will be automatically updated in
user's document.
use populate to retrieve user's enquiries.
You can embed sub documents (entity) which has id and is like a document or embed native array like a normal property.
And I think the correct definition for yours is :
var enquireSchema = new mongoose.Schema({
status: {type: String, 'default': 'pending'},
enquire: String,
});
var userSchema = new mongoose.Schema({
name: String,
enquires: { type : [enquireSchema] , "default" : [] },
});
If you use refs in embedded link then there are two separate collections and be like relational db's.

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