Is there any way to populate created data in sails js (mongodb) - node.js

i have a model named Business which have a feild address with type modal: Address,
i want to send the address object with business data instead of only id when the business is created..
simple create method is just return id of created address object
i want to populate the response
that's why i am using this:
register: function(req, res) {
const values = req.body;
Business.create(values).then(function(response) {
Business.findOne({ id: response.id }).populate('address').then(function(response) {
res.ok(response);
});
});
},
But i dont think this is a proper way.. i am querying database again to populate the result. this is giving me exactly what i want.
i want to use something like this:
register: function(req, res) {
const values = req.body;
Business.create(values).then(function(response).populate('address') {
res.ok(response);
});
},
or anything that remove the second query to the database
i know this is crazy... but please help
The model is
module.exports = {
schema: true,
attributes: {
name: {
type: 'string'
},
description: {
type: 'string'
},
address: {
model: 'Address'
},
email: {
type: 'email',
unique: true
},
landline: {
type: 'string'
},
logo: {
model: 'Image'
},
status: {
type: 'string',
enum: ['open', 'closed'],
defaultsTo: 'open'
},
uniqueBusinessName: {
type: 'string',
unique: true
},
},
};

The create class method does not support populate, so the way you're doing this is correct. If you'd like to propose this as a feature, please check out the Sails.js project's guidelines for proposing features and enhancements!

Related

How to insert post method with mongoose having a reference in node.js

I'm using mongoose and have two schema models and one of them "control" has a reference in the fist model which is "framework" model.
With node.js I'm trying to create a post method and testing it in the postman which is not successful. Not sure if I'm approaching this the right way:
Framework Schema:
const FrameworkSchema = new Schema({
name: {
type: String,
trim: true
},
slug: {
type: String,
slug: 'name',
unique: true
},
image: {
data: Buffer,
contentType: String
},
description: {
type: String,
trim: true
},
isActive: {
type: Boolean,
default: true
},
control:
{
type: Schema.Types.ObjectId,
ref: 'Control'
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
My Control schema:
const ControlSchema = new Schema({
_id: {
type: Schema.ObjectId,
auto: true
},
mControlNo: {
type: String
},
sControlNo: {
type: String
},
name: {
type: String,
trim: true
},
slug: {
type: String,
slug: 'name',
unique: true
},
description: {
type: String,
trim: true
},
isApplicable: {
type: Boolean,
default: true
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
My router api:
router.post(
'/add',
auth,
role.checkRole(role.ROLES.Admin),
async (req, res) => {
try {
const name = req.body.name;
const description = req.body.description;
const isActive = req.body.isActive;
const control = {
mControlNo: req.body.control.mControlNo,
sControlNo: req.body.control.sControlNo,
name: req.body.control.name,
description: req.body.control.description,
isApplicable: req.body.control.isApplicable
};
const framework = new Framework({
name,
description,
isActive,
control
});
const frameworkDoc = await framework.save();
res.status(200).json({
success: true,
message: `Framework has been added successfully!`,
framework: frameworkDoc
});
} catch (error) {
res.status(400).json({
error
// error: 'Your request could not be processed. Please try again.'
});
}
}
);
My json document when I tested it in postman:
{
"name": "NCA2",
"description": "testdescription",
"isActive": true,
"control":
{
"mControlNo": "1",
"sControlNo": "2",
"name": "controltest",
"description": "controldescription",
"isApplicable": true
}
}
Response I'm getting:
{
"error": {
"errors": {
"control": {
"stringValue": "\"{\n mControlNo: '1',\n sControlNo: '2',\n name: 'controltest',\n description: 'controldescription',\n isApplicable: true\n}\"",
"kind": "ObjectId",
"value": {
"mControlNo": "1",
"sControlNo": "2",
"name": "controltest",
"description": "controldescription",
"isApplicable": true
},
"path": "control",
"reason": {}
}
},
"_message": "Framework validation failed",
"message": "Framework validation failed: control: Cast to ObjectId failed for value \"{\n mControlNo: '1',\n sControlNo: '2',\n name: 'controltest',\n description: 'controldescription',\n isApplicable: true\n}\" at path \"control\""
}
}
The whole point of using a framework like mongoose is to write models and let the framework check for you if the body you're sending it is right or wrong. You don't have to assign each variable from your body to your model. You can simply write the following, which will save lines:
const framework = new Framework(req.body);
(Of course, granted that the body has been correctly parsed to JSON via body-parser or another parser).
Then, you check for description or name to be present:
if (!description || !name)
but none of them exist. req.body.description and req.body.name do exist, possibly framework.description and framework.name do as well, but description or name are undefined variables.
The rest of the code looks good, if the error persists, please print out the error in the catch clause as others have suggested in the comments.
Following the code added in the question and the comments the OP made, we now have more elements to answer.
You have a ValidationError coming from mongoose, meaning one of the field you entered is not right. You can also see that it's coming from the field control.
In your Framework schema, you declare a field control that looks like that:
control:
{
type: Schema.Types.ObjectId,
ref: 'Control'
},
That means that Framework is taking an ObjectID for this field, and not an object like you send it here:
const framework = new Framework({
name,
description,
isActive,
control
});
The error is explicit on its own: Mongoose tried to cast your control object to an ObjectID and of course, it fails.
You have 2 solutions:
Either you implement your Control schema directly in your Framework schema as an object field
Or you create a separate Schema object in your route, save it, and give the ID to control when creating the Framework object.
The second solution could look something like this:
const control = new Control(req.body.control);
const controlDoc = await control.save();
const framework = new Framework({...req.body, control: controlDoc._id});
const frameworkDoc = await framework.save();
The first could look something like this:
const FrameworkSchema = new Schema({
// all your Framework schema
control:
{
mControlNo: { type: String },
sControlNo: { type: String },
name: { type: String, trim: true},
// and so on....
},
});

I am trying to get data from the related tables in the database using sails js. But I always get undefined error. Thank you

TransmitterController
transmitter: function (req, res) {
Transmitter.find().populate('TrceiverId').exec(function (errr, transmitter) {
Tranceiver.find().exec(function (err, tranceiver) {
res.view('pages/transmitter', {layout: "layouts/layout", 'transmitter': transmitter, 'tranceiver': tranceiver})
});
})
},
I'm trying to pull data from the database but the answer is: undefined
Model tranceiver
module.exports = {
datastore: 'mysql',
tableName: 'tranceiver',
attributes: {
id: { type: 'number', unique: true, autoIncrement: true, columnName: 'trnsId' },
Name: { type: 'string', required: true, columnName: 'Name' },
Feature: { type: 'string', required: true, columnName: 'Feature'}
},
};
Model Transmitter
module.exports = {
datastore:'mysql',
tableName:'transmitter',
attributes: {
id:{type:'number',unique:true,autoIncrement:true,columnName:'trnsId'},
Name:{type:'string',required:true,columnName:'Name'},
TrceiverId:{model:'Tranceiver'}
},
};
I am waiting for your answers
Check if your collection transmitter has correct TrceiverId in them. I tried same with mongodb and got correct result when used correct TrceiverId in my collections.

SailsJS Perform sorting on populate data

Here is my current event model:
module.exports = {
attributes: {
name: {
type: 'string',
required: true,
unique: true
},
client: {
model: 'client',
required: true
},
location: {
model: 'location',
required: true
}
}
};
The client Model:
module.exports = {
attributes: {
name: {
type: 'string',
required: true,
unique: true
},
address: {
type: 'string'
},
clientContact: {
model: 'user',
required: true
}
}
};
So how do I implement sorting based on the client name and also have the skip and limit property(for pagination) to work along with it.
I tried using the following query:
Event.find({ id: ['583eb530902db3d926345215', '583eb6dd91b972ee26dd97b1'] },
{ select: ['name', 'client'] })
.populate('client', { sort: 'name DESC' })
.exec((err, resp) => resp.map(r => console.log(r.name, r.client)));
But this does not seem to do it.
Waterline doesn't support sorting a result by child records like this. If client was a collection of child records attached to an event, then your code would sort each of the returned Event record's populated client array by name, but I'm assuming in this case client is a singular association (i.e. model: 'client').
If what you want is an array of Event records sorted by the name of their client, you can do it relatively easily using Lodash after you retrieve the records:
var _ = require('lodash');
Event.find(queryCriteria).populate('client').exec((err, resp) => {
if (err) { /* handle error */ }
var sortedRecords = _.sortBy(eventRecords, (record) => { return record.client.name; });
});

add a item inside a nested schema mongoose with addToSet

I know populating schemas is not a new question but I am having a little trouble following the logic on this in regards to multiple schemas. I am working with
"mongoose": "^4.8.5",
"express": "^4.15.0",
I have a schema with a collection of caffeine drinks. When a user selects a drink i would like for that drink to be assigned to the user.
** If at any point I am missing something simple in the architecture please let me know. This project has been my intro to mongodb.
I am reading through populating on the mongoose documentation http://mongoosejs.com/docs/populate.html.
Essentially, if I am to assign the drinks to the list it looks like I want to add them as a reference in an array. This was my approach with caffeine_list
const SelectedDrinks = require('./userDrinks');
const UserSchema = mongoose.Schema({
name: {
type: String
},
email: {
type: String,
required: true
},
username: {
type: String,
required: true
},
password: {
type: String,
required: true
},
caffeine_list: caffeine_list: [ // attempting to reference selected drinks
{
type: mongoose.Schema.Types.ObjectId,
ref: 'SelectedDrinks'
}
]
})
SelectedDrinks comes from the schema below. I added a reference to the user as the creator below
const User = require('./user');
let userDrinkSchema = new mongoose.Schema({
creator : {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
caffeine: Number,
mgFloz: Number,
name: String,
size: Number,
updated_at: {
type: Date,
default: Date.now()
}
});
This is where I start to get confused. I initially tried populate but could not get it going. If that was correct please let me know.
In regards to my task of adding a selected drink to the user I used addToSet. I was hoping that this would give me the drink info. I did my set up like so....
const User = require('../../models/user');
const UserDrinks = require('../../models/userDrinks');
router.post('/addDrink', (req, res, next) => {
let newDrink = new UserDrinks({
creator: req.body.creator,
caffeine: req.body.caffeine,
mgFloz: req.body.mgFloz,
name: req.body.name,
size: req.body.size,
updated_at: req.body.updated_at
});
newDrink.save( (err) => {
if(err) {
res.send(err);
} else {
User.findOne({ _id: newDrink.creator}, (err, user) => {
user.caffeine_list.addToSet(newDrink)
user.save( function (err) {
if(err) {
console.log(err);
}else {
res.status(201).json(newDrink);
}
})
})
}
})
});
However, after i do a post in postman I check caffeine_list and the result is
"caffeine_list" : [
ObjectId("58d82a5ff2f85e3f21822ab5"),
ObjectId("58d82c15bfdaf03f853f3864")
],
Ideally I would like to have an array of objects being passed with the caffeine info like so
"caffeine_list" : [
{
"creator": "58d6245cc02b0a0e6db8d257",
"caffeine": 412,
"mgFloz": 218.7,
"name": "1.95 Perfect Drink!",
"size": 42.93,
"updated_at": "2017-03-24T18:04:06.357Z"
}
]
Change your else part with below code instead of findOne and save use update
User.update(
{ _id: newDrink.creator},
{ $addToSet:{
caffeine_list: newDrink
}}).exec(function (err, updatedrink){
if(err) {
console.log(err);
}else {
res.status(201).json(updatedrink);
}
})
Although I am not sure this is the best approach I did find this to be give me the result that I was desiring. I had to make two small changes and I was able to get the caffeine_list to give me the desired response
I had to access the schema for selected drinks
const SelectedDrinks = require('./userDrinks').schema; //** need schema
Afterwards I was able to change
caffeine_list: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'UserDrinks' // name of the file
}
]
to
caffeine_list: [SelectedDrinks]
Now that I have the schema I am able to add the drinks directly into the caffeine_list on the UserSchema.

How to create new Sails model from an existing model

I am new to Sails and creating a simple application.
I am stuck with data model now.
User model is as follows:
module.exports = {
attributes: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
email: {
type: 'email',
required: true
},
password: {
type: 'String'
},
passwordSalt: {
type: 'String'
},
projects:{
collection: 'ProjectMember',
via: 'userId'
}
}
};
I need one more Model called TinyUser which gets some of attributes from User (Like Foreign key to User), so that I can access TinyUser instead of accessing User directly.
TinyUser Model is as follows:
module.exports = {
tableName: 'User',
attributes: {
firstName:{
type: 'string'
},
lastName: {
type: 'string'
},
email: {
type: 'email'
}
}
};
ProjectMember model is as follows:
module.exports = {
attributes: {
projectId: {
model: 'Project'
},
userId: {
model: 'TinyUser'
},
projectRole: {
model: 'ProjectRole'
},
}
};
In Sails, Is there anyway I can save data in TinyUser without actually creating but just holding some of attributes of User table data?
To Make it clear, If there is another table called Task and it is trying to access user data then TinyUser is better option as a model rather than User as it holds only required info. for task rather than storing all other fields which User does.
Is there any way I can fix this?
Thanks In Advance
Do you mean you need to have the "TinyUser" inherited from the "User" model?
You can do it like this.
/api/services/baseUserModel.js:
module.exports = {
attributes: {
firstName: 'string',
lastName: 'string',
email: {
type: 'email',
required: true
},
password: 'string',
passwordSalt: 'string',
projects: {
collection: 'ProjectMember',
via: 'userId'
}
}
};
/api/models/TinyUser.js:
var baseUserModel = require('../services/baseUserModel'),
_ = require('lodash');
module.exports = _.merge({
tableName: 'User',
attributes: {
firstName: 'string',
lastName: 'string',
email: {
type: 'email',
required: true
}
}
}, baseUserModel);
And the ProjectMember model should be:
module.exports = {
attributes: {
projectId: {
model: 'project'
},
userId: {
model: 'tinyuser'
},
projectRole: {
model: 'projectrole'
}
}
};

Resources