Mongoose $push into array adding _id [duplicate] - node.js

If you have subdocument arrays, Mongoose automatically creates ids for each one. Example:
{
_id: "mainId"
subDocArray: [
{
_id: "unwantedId",
field: "value"
},
{
_id: "unwantedId",
field: "value"
}
]
}
Is there a way to tell Mongoose to not create ids for objects within an array?

It's simple, you can define this in the subschema :
var mongoose = require("mongoose");
var subSchema = mongoose.Schema({
// your subschema content
}, { _id : false });
var schema = mongoose.Schema({
// schema content
subSchemaCollection : [subSchema]
});
var model = mongoose.model('tablename', schema);

You can create sub-documents without schema and avoid _id. Just add _id: false to your subdocument declaration.
var schema = new mongoose.Schema({
field1: {
type: String
},
subdocArray: [{
_id: false,
field: { type: String }
}]
});
This will prevent the creation of an _id field in your subdoc.
Tested in Mongoose v5.9.10

Additionally, if you use an object literal syntax for specifying a sub-schema, you may also just add _id: false to supress it.
{
sub: {
property1: String,
property2: String,
_id: false
}
}

I'm using mongoose 4.6.3 and all I had to do was add _id: false in the schema, no need to make a subschema.
{
_id: ObjectId
subDocArray: [
{
_id: false,
field: "String"
}
]
}

You can use either of the one
var subSchema = mongoose.Schema({
//subschema fields
},{ _id : false });
or
var subSchema = mongoose.Schema({
//subschema content
_id : false
});
Check your mongoose version before using the second option

If you want to use a predefined schema (with _id) as subdocument (without _id), you can do as follow in theory :
const sourceSchema = mongoose.Schema({
key : value
})
const subSourceSchema = sourceSchema.clone().set('_id',false);
But that didn't work for me. So I added that :
delete subSourceSchema.paths._id;
Now I can include subSourceSchema in my parent document without _id.
I'm not sure this is the clean way to do it, but it work.

NestJS example for anyone looking for a solution with decorators
#Schema({_id: false})
export class MySubDocument {
#Prop()
id: string;
}
Below is some additional information from the Mongoose Schema Type definitions for id and _id:
/**
* Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field
* cast to a string, or in the case of ObjectIds, its hexString.
*/
id?: boolean;
/**
* 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.
*/
_id?: boolean;

Related

How to use $addToSet in Mongoose with array of ObjectId?

In MongoDB collection, I need to add unique ids only for a array field of a document. I am using findByIdAndUpdate of Mongoose. Can I pass the value as id or I need to pass the value as ObjectId() in the update body.
Should it be 1 or 2?
1.
await User.findByIdAndUpdate(userId, { $addToSet: { roleIds: roleId } })
await User.findByIdAndUpdate(userId, { $addToSet: { roleIds: mongoose.Types.ObjectId(roleId) } })
The schema is as follows:
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
roleIds: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Role",
},
],
});
module.exports = mongoose.model("User", UserSchema);
whats your schema , if in your schema you defined roleIds element you must use 1

Saving ObjectId as reference dose not work in NodeJs MongoDB

I am going to store a category id to an item.
I do the below .save() function to add a new row.
const myNewItem = {
categoryId = ObjectId("5fc0a6e58dc3892120595384"),
title = "Apple"
}
var myNewItemSave = await new Item(myNewItem).save();
However, when I check my database record
_id: ObjectId("..."),
categoryId: "5fc0a6e58dc3892120595384",
title: "Apple"
The categoryId is not saved as ObjectId.
I want to save it as ObjectId is because I am going to query some lookup aggregation like this:
https://mongoplayground.net/p/50y2zWj-bQ6
so I have to make localField and foreignField are the same type as ObjectId.
It happen becuse your schema don't allow category id as mongoose objectId it is string
const schema = new Mongoose.Schema(
{
title: { type: String },
categoryId: { type: Mongoose.Schema.ObjectId, ref: 'categories' }
},
{ timestamps: true, versionKey: false }
)
export default Mongoose.model('Items', schema)
Import Item model and save will convert string into mongoose objectId
const myNewItem = {
categoryId: "5fc0a6e58dc3892120595384",
title: "Apple"
}
const myNewItemSave = await Item.create(myNewItem);
#Ashok defines a type in the model, if you dont want to modify the model you can "convert" the id with mongoose.Types.ObjectId, eg:
const mongoose = require('mongoose')
const myNewItem = {
categoryId = mongoose.Types.ObjectId("5fc0a6e58dc3892120595384"),
title = "Apple"
}

Can a subdocument be required in mongoose?

Is it possible to have nested schemas in mongoose and have a required validator on the children? Something like this:
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
}
});
const eventSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
host: {
type: userSchema,
required: true
}
});
I can't find anything in the documentation. Thanks.
Yes, your schema is correct.
The docs for mongoose nested schema (SubDocuments) can be found here
i suppose you'll update eventSchema with subdocuments of type user model.
you can use { runValidators: true} for update.
eventModel.update({ name: 'YOUR NAME' }, { $push: { host: user } }, { runValidators: true}, function(err) {
})
You can use the nested schema in mongoose.
It will also give you he Object Id on each sub schema values as well.
Doc: Here
Example: Here
required is a validator added to a schema or subschema in Mongoose (from docs)
so yes, you can set the required field to true ( it is false by default) for your subschema or subdocument in Mongoose.
The example schema you have created is correct.

Using UUIDs in mongoose for ObjectID references

I'm building a CRUD-style REST service with Node.js, Express and MongoDB using mongoose. This service is going to allow users of an already existing android application to upload/sync the contents of their individual databases online.
The data model for the already-existing application uses UUIDs (generated in Java) which clashes with the shorter, monotonic MongoDB style _id fields. Because the data model already exists and is populated with data from many users, I cannot convert the source data over to monotonic MongoDB-style _ids. This has left me with 2 options that I can think of: either 1) Make Mongo/Mongoose (or some other ODM) play nicely with full UUIDs instead of the monotonic _ids or 2) add a uuid field to the mongoose model in addition to the _id field and fight the pitfalls of this approach. I'm attempting to choose option #1 and running into issues with ObjectID references.
I originally stumbled upon mongoose-uuid, but unfortunately this isn't working for my use-case properly because it was overwriting my explicitly-set _id value when creating new Mongoose objects. Diving into the plugin code, it assumes that an object is new (by calling checking Mongoose's .isNew value) and thus overwrites the _id with a new uuid. Since I need to retain the original uuid when creating new documents in Mongo, this plugin isn't working for me.
Next, I found a post by Aaron Heckmann, creator of mongoose, on a similar topic. This has been helpful, however I am now encountering the problem where I cannot have my mongoose schemas reference each other by ObjectID, since they technically they are now referencing each other using String `_ids.
Schema example:
var mongoose = require('mongoose');
var uuid = require('node-uuid');
var Schema = mongoose.Schema;
var trackPassSchema = new Schema({
_id: { type: String, default: function genUUID() {
uuid.v1()
}},
//Omitting other fields in snippet for simplicity
vehicle: [
{type: Schema.Types.ObjectId, required: true, ref: 'Vehicle'}
]
});
module.exports = mongoose.model('TrackPass', trackPassSchema);
Referencing schema:
var mongoose = require('mongoose');
var uuid = require('node-uuid');
var Schema = mongoose.Schema;
var vehicleSchema = new Schema({
_id: { type: String, default: function genUUID() {
uuid.v1()
}},
//Omitting other fields in snippet for simplicity
description: {type: String},
year: {type: Number}
});
module.exports = mongoose.model('Vehicle', vehicleSchema);
When I attempt to call save() a trackPass that has been passed in from my application:
var trackPass = new TrackPass(req.body);
//Force the ID to match what was put into the request
trackPass._id = req.params.id;
trackPass.save(function (err) { ... }
I get the following error:
{ [CastError: Cast to ObjectId failed for value "b205ac4d-fd96-4b1e-892a-d4fab818ea2a" at path "vehicle"]
message: 'Cast to ObjectId failed for value "b205ac4d-fd96-4b1e-892a-d4fab818ea2a" at path "vehicle"',
name: 'CastError',
type: 'ObjectId',
value: ["b205ac4d-fd96-4b1e-892a-d4fab818ea2a"],
path: 'vehicle' }
I believe this error makes sense as I'm now using Strings which are longer than typical Mongo ObjectIDs. Without having the ObjectID reference, I don't believe I will be able to populate() referenced objects from other collections. I suppose I could simply not reference the other nested objects in my schema definitions, however I don't like this approach as I feel I will be losing a lot of the benefit of utilizing the ODM. Any other thoughts?
You can still use populate() with _id values of types besides ObjectID, but you do need to use the same type in the reference definition.
So your trackPassSchema would need to change to:
var trackPassSchema = new Schema({
_id: { type: String, default: function genUUID() {
return uuid.v1()
}},
vehicle: [
{type: String, required: true, ref: 'Vehicle'}
]
});
As Adam notes in the comments, you could simplify your default value to:
var trackPassSchema = new Schema({
_id: { type: String, default: uuid.v1 },
vehicle: [
{type: String, required: true, ref: 'Vehicle'}
]
});
Both JohnnyHK and Adam C answers are correct. But if you're using uuid in schema for an array of objects, it is good to use it like this
var trackPassSchema = new Schema({
_id: { type: String, default: () => uuid.v1 },
vehicle: [
{type: String, required: true, ref: 'Vehicle'}
]
});
Because, in one such scenario when i tried using like this _id: { type: String, default: () => uuid.v1 } multiple objects of the array had the same id.
It is not possible in this case as _id is unique field, but it can happen when you are using with fields that aren't unique.

Can a Mongoose schema be inspected to determine if a field is required?

I have a Mongoose schema (excerpt below):
var PersonSchema = new Schema({
name : {
first: { type: String, required: true }
, last: { type: String, required: true }
}
...
I would like to inspect the schema to identify which fields are required then validate those fields are present in user input. I can test the 'required' attribute of name.first as follows:
var person_schema = require('../models/person');
if (person_schema.schema.paths['name.first'].isRequired) {
req.assert('first', messages.form_messages.msg_required).notEmpty();
But feel this is unsafe as the internal schema details could change. Is there a better way?
Mongoose does this type of validation for you. http://mongoosejs.com/docs/validation.html.

Resources