mongoose self reference with own ObjectId throwing: cast to ObjectId failed - node.js

I am using nodejs, mongoose and I trying to build a shema that contains a reference to itself via parent. Parent should be a reference to DataType.
var DataTypeSchema = new Schema({
_id: String,
label: { type: String, required: true },
comment: { type: String },
url: { type: String, required: true },
parent: { type: Schema.Types.ObjectId, ref: 'DataType' },
values: []
});
var DataType = mongoose.model('DataType', DataTypeSchema);
module.exports.DataType = DataType;
Every DataType has own ID(not generated by mongo) and I think this is a place where it causes problems. It throws me an error cast to objectid failed for value "Number" at path "parent", where Number is object with ID "Number" already saved in DB.
Thanks

The type of the parent reference must match the type of the _id in the model it references. So instead of Schema.Types.ObjectId it should be String:
...
parent: { type: String, ref: 'DataType' },

You can try this
parent: [ this ]
It works

Related

Mongoose - array of enum strings

I have a Schema that has a property with the type of array of strings that are predefined.
This is what I've tried to do:
interests: {
type: [String],
enum: ['football', 'basketball', 'read'],
required: true
}
The thing is that when I'm trying to enter a wrong value that isn't defined on the enum, to the array it wouldn't validate it with the enum list.
for example, this would pass which it shouldn't:
{ "interests": ["football", "asdf"] }
because "asdf" isn't predefined in the enum list it shouldn't pass the validation but unfortunately, it passes the validation and saves it.
I've tried to check this thing with a string type of values instead of an array of strings and it works.
for example:
interests: {
type: String,
enum: ['football', 'basketball', 'read'],
required: true
}
for example, this is failing as expected:
{ "interest": "asdf" }
In conclusion, I need a schema's property with a type of array of strings that would check it's elements based on predefined values
Is the most effective way to achieve this goal is by using the validate method or there is a better way?
Quoting from here:
const SubStrSz = new mongoose.Schema({ value: { type: String, enum: ['qwerty', 'asdf'] } });
const MySchema = new mongoose.Schema({ array: [SubStrSz] });
Using that technique you will able to validate values inside of your array.
You can try a custom validation?Like this
const userSchema = new Schema({
phone: {
type: String,
validate: {
validator: function(v) {
return /\d{3}-\d{3}-\d{4}/.test(v);
},
message: props => `${props.value} is not a valid phone number!`
},
required: [true, 'User phone number required']
}
});
this is the docs:
https://mongoosejs.com/docs/validation.html
Here distributers will be array of distributerObj, similarly you can define object of any type.
const distributerObj = new Schema({
"dis_id": {
"type": "String"
},
"status": {
"type": "String"
}
});
const productSchema = new Schema({
"distributers": {
"type": [distributerObj]
}
});
use ref to make a relation to the defined enum schema
const actionsEnums = new Mongoose.Schema({value: { type: String, enum:["account-deletion","account-update"]}});
const Privilege = new Mongoose.Schema(
{
userLevel: { type: String, required: true },
actions: [{type: String, refs: actionsEnums}],
})

How to define non-required field in Mongoose schema with nested "type" property?

I have the following Mongoose schema definition in my project:
export const ProductSchema: SchemaDefinition = {
type: { type: String, enum: constants.productTypes, required: true },
name: { type: String, required: false },
espData: {
type: { type: String, required: true },
text: { type: String, required: true }
},
error: {
type: {
message: { type: String, required: true },
statusCode: { type: Number, default: null }
},
required: false
}
// Some more definitions...
};
What's important from here is that I have collection of products where each product has its own type (which is a required string property that can have values defined in constants.productTypes), a non-required name field and so on. Also, there's espData field that has its own mandatory type property which is completely different from the top level type. And, there's error property that does not exist at all times, but when it does, it must have message property and optional statusCode property.
I now have to modify this schema so that espData becomes optional field since I may now have products that don't have this property. How do I do that? I tried couple of things, but none of them worked:
First, I modified espData so that it looks the same as error:
espData: {
type: {
type: { type: String, required: true },
text: { type: String, required: true }
},
required: false
},
But, this is not working, most probably because there's so many nested type properties. Funny thing is that it perfectly works for the error property which has the same structure as espData, but without nested type property. The code I used is
const model = new this.factories.product.model();
model.type = 'hi-iq';
// model.espData purposely left undefined
await model.save();
The error I'm getting is Product validation failed: espData.type.text: Path 'espData.type.text' is required., espData.type.type: Path 'espData.type.type' is required. This indicates that model created from schema is created as espData.type.type which is not what I wanted (I wanted espData.type).
Second, I have tried the same from above, just instead of required field, I wrote: default: null which gave me an error TypeError: Invalid value for schema path 'espData.default', got value "null".
So, how do I define espData as an optional field, which must have type and text properties when it exists?
Is this what you want. Create a new Document Schema with all the validations and nest it in another Schema with required: false (its default to false anyway)
const EspDataSchema = new Schema({
type: { type: String, required: true },
text: { type: String, required: true },
},
{
_id: false,
}
);
example
export const ProductSchema = new Schema({
...
espData: {
type: EspDataSchema,
required: false
},
...
})

Insert a document with mongoose without initialize the model with empty attributes

I want to insert a document in my database from a website form. I have a model created with mongoose and I want to save in the database only the attributes that contains data and I don't want to save empty attributes.
This is my model:
const localizationSchema = new Schema({
name: { type: String, required: true },
spins: [{ type: String }],
spinsForChild: [{ type: String }],
parent: { id: String, name: String },
localizationType: { type: String },
count: { type: Number, default: 0 },
countries: [{ id: String, name: String, cities: [{ id: String, name: String }] }]
});
const Localization = mongoose.model('Localization', localizationSchema);
When I try to save a new document, it creates in the database all attributes although I don't send it on my query.
Localization.create({
name: body.name,
localizationType: body.localizationType,
"parent.id": parent.id,
"parent.name": parent.name,
spins: spins,
spinsForChild: spinsForChild
}, function(err) {
if (err) return handleError(err);
res.redirect('/localizations');
});
This code, for example, inserts in the DB an empty array called "countries".
I tried to use strict: false in the model declaration but it didn't works.
You could use this answer.
But, thing you try to implement seems to be anti-pattern and can cause errors when you will try to use array update operators with undefined array. So, use it carefully.
Good luck!

How to use a custom type in mongoose?

I have the following schema:
var Location = new Schema({
x: {
type: String
},
y: {
type: String
}
},{
_id: false
});
var AppSchema = new Schema({
link: {
type: String
},
location: {
type: Location
}
})
The above not working and I'm getting the following error:
Undefined type at `location'
There are specific valid types within Mongoose, found here: http://mongoosejs.com/docs/schematypes.html
If I were you, I'd consider using the type: Schema.Types.Mixed type.
To quote the mongoose documentation, it's literally an "anything goes" type.

mongoose sub populate not works

here is my schema :
var sourcesSchema = {
title: String,
name: String,
url: String,
description: String,
category: Array,
rating: Number,
source_pages: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'source_page',
}]
}
var sourcePageschema = {
uname: String,
source_name: String,
page_address: String,
driver_name: String,
product: {
type: mongoose.Schema.Types.ObjectId,
ref: 'products' //Edit: I'd put the schema. Silly me.
}
}
var productsSchema = {
title: String,
uname: String,
descriptin: String,
images: Array,
currency: String,
last_update_time: Number,
last_process_time: Number,
meta_data: {},
tags: Array,
min_price: Number,
max_price: Number,
prices: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'prices' //Edit: I'd put the schema. Silly me.
}]
}
this code works and populate the source_pages successfully :
_sources.find().populate('source_pages').exec(function (err,sources) {
res.json(200, sources);
});
but if I want to populate the product too :
_sources.find().populate('source_pages').populate('source_pages.product').exec(function (err,sources) {
res.json(200, sources);
})
this error :
TypeError: Cannot call method 'path' of undefined
at search (/home/sina/rhino2/node_modules/mongoose/lib/model.js:2088:28)
at search (/home/sina/rhino2/node_modules/mongoose/lib/model.js:2107:22)
at Function._getSchema (/home/sina/rhino2/node_modules/mongoose/lib/model.js:2114:5)
at populate (/home/sina/rhino2/node_modules/mongoose/lib/model.js:1719:22)
at Function.Model.populate (/home/sina/rhino2/node_modules/mongoose/lib/model.js:1702:5)
at cb (/home/sina/rhino2/node_modules/mongoose/lib/query.js:1690:11)
at /home/sina/rhino2/node_modules/mongoose/lib/utils.js:414:16
at /home/sina/rhino2/node_modules/mongoose/node_modules/mongodb/lib/mongodb/cursor.js:158:16
at commandHandler (/home/sina/rhino2/node_modules/mongoose/node_modules/mongodb/lib/mongodb/cursor.js:643:16)
at null. (/home/sina/rhino2/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js:1641:20)
I was just hunting down the same problem, and I believe what you are looking for is this Mongoose: deep population (populate a populated field).
Basically, you are not able to do what you are trying to do, unless you do it in your callback function and then insert it in your return. I was trying to avoid that, but at the moment it seems like the only option. The other option, if you plan on doing a lot of this type of stuff, is to look into using a Relational DB.

Resources