How to require properties only on create entity in loopback4 - mongodb

I have a model with required properties but are only required on create, on update only the id property es required.
This is my model example:
#model()
export class MyModel extends Entity {
#property({
type: 'string',
required: true
})
name: string;
#property({
type: 'string',
id: true,
})
id: string;
}
On Mongoose schemas i can define a context to validate params but on Loopback 4 documentation i can't found nothing like this to solve this problem

Have you tried excluding the id property from the #requestBody decorator of the parameter of the request controller
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Model, {exclude: ['id']}),
},
},
}
Add this as a parameter to the method of the controller
Or
YOu can create a custom schema for the request body something like
const RequestSchema = {
type: 'object',
required: ['name'],
properties: {
name: {
type: 'string',
},
id: {
type: 'string',
},
},
};
export const RequestBody = {
required: true,
content: {
'application/json': {schema: RequestSchema},
},
};
See how only the name is required and Id is not in the RequestSchema Object
then pass it to the controller method as
#requestBody(RequestSchema)
Hopefully, this shall work.
Write me if this also not fixes the problem.
Thanks

Related

How to construct a custom validator that makes a MongoDB field required if another field is a particular value?

I am using MongoDB and Mongoose.
Suppose I have the following Schema.
const notificationMetaSchema = new mongoose.Schema({
listingId: { type: mongoose.Schema.Types.ObjectId },
});
const notificationSchema = new mongoose.Schema({
category: { type: String, required: true, enum: [ "categoryA", "categoryB" ] },
meta: notificationMetaSchema,
});
I want my "listingId" field to be required only when the "category" field is "categoryA".
This validation ideally exists during both document creation and updates.
How do I construct a custom validator to achieve this effect?
EDIT
I have tried the following:
const notificationSchema = new mongoose.Schema({
category: { type: String, required: true, enum: [ "categoryA", "categoryB" ] },
meta: {
listingId: {
type: mongoose.Schema.Types.ObjectId,
required: function () {
return [
"categoryA",
].includes(this.category);
}
},
},
});
However, when I call the following query:
Notification.findOneAndUpdate({}, $set: { category: "categoryA", meta: {} }).exec();
No validation error is thrown
You can write a javaScript function for a field in mongoose schema, that function can act as custom validator, Your schema should look like :
const notificationSchema = new mongoose.Schema({
category: {
type: String,
required: true,
enum: ["categoryA", "categoryB"]
},
meta: {
listingId: {
type: mongoose.Schema.Types.ObjectId,
required: function checkRequiredOrNot() {
/** This function returns true or false, 'this.category' will retrieve current object's 'category' value */
return this.category == "categoryA" ? true : false;
}
}
}
});

Mongoose findOneAndUpdate not saving

I have the a mongoose model I'm trying to update right now using the .findOneAndUpdate method with the below code:
MyModel.findOneAndUpdate({ _id: "xxxxx", userId: "xxxxx" }, { $set: { completion:"xxxx", date: "xxxxx" } }, { new: true }, function(err, doc) {
if(err) {
return res.json({success: false, message: err.message});
}
res.json({success: true, message: 'success'});
});
When I log doc, it returns the updated model, but the model is not being saved to the database. Any thoughts on this would be greatly appreciated.
Model Code:
var MyModel = new Schema({
name: {
type: String,
required: true
},
date: {
type: Date,
required: true
},
userId: {
type: String,
required: true
},
completion: {
type: Boolean,
required: true
}
});
There are few issues as per your posted code:
1) MyModel is a Schema object, you will have to create a Model object like this -
var model = mongoose.Model('modelName', MyModel); // where MyModel is a Schema object
Then using the above model object you can run your findOneAndUpdate, like in your code (like model.findOneAndUpdate).
2) Secondly, in the MyModel Schema you have not given the collection name. You can put it in the options object which comes after the schema object argument. So you should put:
var MyModel = new Schema({schema object...}, {collection: 'mongodbCollectionName'});
If you do not give above option,mongoose would create a default collection using the model name.
I believe if (1) is not there, (2) is most likely causing the issue in your case.

MissingSchemaError while using Mongoose Populate with only one model

**I have answered below. In short you need to require the Model in the module in which you wish to populate, even though you do not refer to it directly.
I am hitting a strange problem with mongoose when populating just one particular array of IDs.
I have three models, User, Company and Widgets.
When I return the company populated with the users all is fine using:
Company.findOne({ name: 'xyz' })
.populate('users')
.exec(function(err, company) {
if (err) return res.send(err)
res.send(company)
})
However when I try to replace populate 'users' with 'widgets' I get the following error:
{
"message": "Schema hasn't been registered for model \"widget\".\nUse mongoose.model(name, schema)",
"name": "MissingSchemaError"
}
Here are the models:
USER:
const UserSchema = new Schema({
name: String,
email: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
company: {
type: Schema.Types.ObjectId,
ref: 'company'
}
});
const User = mongoose.model("user", UserSchema);
COMPANY:
const CompanySchema = new Schema({
name: String,
URL: {
type: String,
unique: true
},
users: [{
type: Schema.Types.ObjectId,
ref: 'user'
}],
widgets: [{
type: Schema.Types.ObjectId,
ref: 'widget'
}]
});
const Company = mongoose.model('company', CompanySchema);
WIDGET:
const WidgetSchema = new Schema({
title: {
type: String,
required: true
},
maker: String
});
const Widget = mongoose.model('widget', WidgetSchema);
I have manually inspected the _ids in the widget array of the company model and they are all correct.
OK, so this was a lack of understanding on my behalf.
In the module where I was using:
Company.findOne({ name: 'xyz' })
.populate('users')
.exec(function(err, company) {
if (err) return res.send(err)
res.send(company)
})
I had imported the User model for other uses in the module. However, as I was not directly referring to Widget I had not imported it. Having done some more research I found that you need to import a model when populating even though not referring to it directly.
Let me know if best to delete whole thread or leave for reference.

Sails js One-to-Many Relationship with custom column names

Faced serious problems here with Sails Js.
So, i have one-to-many relationship as described below (taken from official sails documentation):
// myApp/api/models/User.js
// A user may have many pets
module.exports = {
attributes: {
firstName: {
type: 'string',
columnName: "first_name"
},
lastName: {
type: 'string',
columnName: "last_name"
},
// Add a reference to Pets
pets: {
collection: 'pet',
via: 'owner'
}
}
};
and dependent model:
// myApp/api/models/Pet.js
// A pet may only belong to a single user
module.exports = {
attributes: {
breed: {
type: 'string'
//column name will be 'breed' by default
},
typeProperty: {
type: 'string',
columnName: "type_property"
},
nameProperty: {
type: 'string',
columnName: "name_property"
},
// Add a reference to User
owner: {
model: 'user'
}
}
};
When I'm calling in the code following query
User.find()
.populate('pets')
.exec(function(err, users) {
if(err) // handle error
// The users object would look something like the following
[{
id: 123,
firstName: 'Foo',
lastName: 'Bar',
pets: [{
id: 1,
// !! Only this property is loaded !!
breed: 'labrador',
// !! This property is NOT loaded !!
typeProperty: undefined,
// !! This property is NOT loaded !!
nameProperty: undefined,
user: 123
}]
}]
});
Basically, seems that sails (waterline if to be specific) is not mapping back properties, which have custom "columName" specified and differs from property name (ex. "typeProperty" stored in type_property column).
Has anyone faced this kind of problem?
In fact, I faced this problema. The property "columnName" is not working. Seems like Sails doesn't prioritizes this property over it's model's convention naming.
Try to change the model attribute name to be equal your database property.
type_property: {
type: 'string'
},
this should make your attributes to be populated. Worked here.
Column name works fine when the attribute in question is a foreign key.

Sails / Waterline model help - how can I store data that might be an array, json or string?

I'm storing user input into MongoDB with Sails / Waterline and because the type of field is dependent on their setup I'm finding it difficult to figure out the best way to store the data.
The 'surveyField' model is like:
// SURVEY FORM FIELD DEFINITIONS
module.exports = {
attributes: {
name: {
type: 'string',
required: true
},
label: {
type: 'string',
required: true
},
constraints: {
type: 'string',
enum: ['none', 'unique', 'combo', 'same'],
required: true,
defaultsTo: 'none'
},
isRequired: {
type: 'boolean',
required: true,
defaultsTo: false
},
attributeType: {
type: 'string',
enum: ['boolean', 'text', 'localizedText', 'enum', 'localizedEnum', 'number', 'money', 'date', 'time', 'dateTime'],
required: true
}
}
}
The user will have added any number of these fields to their form and so their form will contain a reference to the types of fields they have chosen. When their form is built I know exactly how to handle/display each of the fields based on this information, but saving the info is proving to be somewhat difficult because that Model needs to assume a type for the value field.
The 'surveyData' model looks like:
module.exports = {
attributes: {
value: {
**type: 'string' // THIS IS WHERE THE ISSUE IS**
},
surveyFieldType: {
model: 'surveyFieldType',
required: true
},
survey: {
model: 'survey',
required: true
},
user: {
model: 'user',
required: true
}
}
}
The issue occurs when the value might be a string or it might be json... or any of the other 'standard data types.'
Any help on this would be greatly appreciated.
** EDIT **
I'll also need this value to be searchable as well.
Maybe change it a little. And deal with survery answer as more complex value than just a value. Change type to JSON & build custom validator.
attributes: {
value: {
type: 'json'
}
}
and make it lake that
{
surveyFieldType: something,
value: {
type: 'Array',
value: [1,2,3]
},
survey: survey,
user: user
}
You can search now by that, you have flattened types of all replies. Based on value.type you can create custom validation rules for json.