Sails.js application fails to create one-to-many relationship in mongo db:
According to official Documentation I created two linked one-to-many entities, Post-Likes. I located them in api/models folder and defined as follows:
Like.js
module.exports = {
attributes: {
post: {
model: 'post',
required: true
}
}
Post.js
module.exports = {
attributes: {
likes: {
collection: 'like',
via: 'post'
},
user: {
model: 'user'
}
}
}
And I populate likes for the post with a following statement on the page:
const posts = await Post.find({user: id})
.sort('createdAt DESC')
.populate('user')
.populate('likes');
After I run sails.js app with command sails lift --drop and load the page with code above I receive the following error message in console:
error: Sending 500 ("Server Error") response: UsageError: Invalid
populate(s). Details: Could not populate like. There is no
attribute named like defined in this model.
at Object.module.exports [as user/profile] (/Users/new/Developer/ios/social/web_social/api/controllers/user/profile.js:8:30)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
I checked mongo db instance and I cannot see any empty or null like attribute for post entity:
try to make your model name initCap. like below
module.exports = {
attributes: {
post: {
model: 'Post',
required: true
}
}
Related
I am trying to learn how to create a Normalized many to many Post request in express/router and Mongoose.
I have three collections: User, Building and Room which is also the order of parent to child documents.
My Building document schema includes both User and Room id's as follows:
const mongoose = require('mongoose');
const BuildingSchema = new mongoose.Schema({
user: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "user"
}
],
room: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "room"
}
],
My Room document schema includes the Building ID and another child document as follows:
const mongoose = require('mongoose');
const RoomSchema = new mongoose.Schema({
building: {
type: mongoose.Schema.Types.ObjectId,
ref: "building"
},
furniture: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "furniture"
}
],
I am having trouble understanding how to create a post request that allows a user/users to create a Building instance and multiple Room instances associated with it after...
My express code so far can create a new Building instance but I am unsure how to handle including the Room id's:
router.post('/createbuilding', [auth, [
check('name', 'Building Name is required').not().isEmpty(),
]
], async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
name,
type,
website,
location,
bio,
} = req.body;
const buildingFields = {
user: req.user.id,
//room: req.room.id,
name,
type,
website: website && website !== '' ? normalize(website, { forceHttps: true }) : '',
location,
bio
};
try {
let building = await Building.findOneAndUpdate(
{ user: req.user.id },
//{ room: req.room.id },
{ $set: buildingFields },
{ new: true, upsert: true }
);
res.json(building);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
}
);
Note: The references to room ID are commented out on purpose because this is what I am unsure of how to include in the Post request.
With Mongoose's .findOneAndUpdate() the first parameter is to query for the document, similar to .findOne(). Your second parameter is what fields to update. $set is redundant since Mongoose will do that for you.
If you want to add a room to the building rather than replacing all the associations, you'll want to add the room with a $push.
const buildingFields = {
$push: {
room: {
req.room.id
}
},
name,
type,
...
}
I am also assuming that you intended that the user field in BuildingSchema is a single association rather than a many associations. If not, you'd need to use a $elemMatch to query for that document:
Mongoose Mongodb querying an array of objects
am using sails-sqlserver as my adapter am just trying the create a new row in the database in one of the follwing models.
This is the first model :
// Roles.js
module.exports = {
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
name: {
type: 'string'
},
approval_level: {
model: 'approval_levels'
},
specified: {
type: 'boolean'
},
entity_operations: {
collection: 'entity_operations',
via: 'roles',
dominant: true
},
users: {
collection: 'system_users',
via: 'role'
}
},
createRole: function (name, approval_level, cb) {
values = {
name: name,
approval_level: approval_level
};
Roles.create(values).exec(cb);
},
getAll: function (cb) {
Roles.find().exec(cb);
}
};
This is the second model :
// Entity_Operations.js
module.exports = {
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
entity_name: {
type: 'string'
},
operation: {
model: 'operations'
},
roles: {
collection: 'roles',
via: 'entity_operations'
}
},
getAll: function (cb) {
Entity_operations.find().exec(cb);
}
};
These two models have a many to many relationship together what am trying to do is just this :
Entity_operations.create({
entity_name: 'example',
operation: 6
}).exec((err, entity: Entity_operations) => {
console.log(entity);
});
then this error comes out without explaining anything that could help me know from where this error is coming from :
/opt/nodejs/back-heaven/dev/node_modules/sails-sqlserver/lib/adapter.js:435
Object.keys(connections[connection].collections[collection].definition).forEach(function(key) {
^
TypeError: Cannot read property 'collections' of undefined
at Object.getPrimaryKey (/opt/nodejs/back-heaven/dev/node_modules/sails-sqlserver/lib/adapter.js:435:42)
at Object.create (/opt/nodejs/back-heaven/dev/node_modules/sails-sqlserver/lib/adapter.js:374:24)
at module.exports.create (/opt/nodejs/back-heaven/dev/node_modules/waterline/lib/waterline/adapter/dql.js:84:13)
at child.createValues (/opt/nodejs/back-heaven/dev/node_modules/waterline/lib/waterline/query/dql/create.js:220:16)
at /opt/nodejs/back-heaven/dev/node_modules/waterline/lib/waterline/query/dql/create.js:74:20
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:726:13
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:52:16
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:269:32
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:44:16
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:723:17
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:167:37
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:52:16
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:269:32
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:44:16
at child.<anonymous> (/opt/nodejs/back-heaven/dev/node_modules/waterline/lib/waterline/utils/schema.js:152:44)
at fn (/opt/nodejs/back-heaven/dev/node_modules/waterline/lib/waterline/utils/callbacksRunner.js:41:10)
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:181:20
at iterate (/opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:262:13)
at Object.async.forEachOfSeries.async.eachOfSeries (/opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:281:9)
at Object.async.forEachSeries.async.eachSeries (/opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:214:22)
at Object.runner.beforeCreate (/opt/nodejs/back-heaven/dev/node_modules/waterline/lib/waterline/utils/callbacksRunner.js:44:9)
at /opt/nodejs/back-heaven/dev/node_modules/waterline/lib/waterline/query/dql/create.js:180:17
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:718:13
at iterate (/opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:262:13)
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:274:29
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:44:16
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:723:17
at /opt/nodejs/back-heaven/dev/node_modules/async/lib/async.js:167:37
when i tried the same code in a controller it succeded am trying it in a seeder of my own implementation this seeder before it starts i make a sails object programmatically and call sails.load
like this :
let myApp = new Sails();
myApp.load({}, (err) => {
if (err) throw err;
// this execute the seeds in the database seeder class
let seeder = require(`../../api/seeders/${scope.args[0]}`);
seeder.handle(() => {
myApp.lower((err) => {
if (err) throw err;
console.log(`Seeder ` + scope.args[0] + ` finished seeding`);
});
});
});
I also tried with sails.lift() and still the same error.
I have Found out the problem i was just making a callback function that should call sails.lower() which unload or close the sails I was putting it in the wrong place where it was called even before the model creation code starts.just forget to call it in the database callback function.
so , for anyone else who is facing this same problem in sails your problem is that sails actually not loaded or maybe when the operation you were working on started sails was working but, after that for some reason sails stopped which made the models do this weird looking problem I hope sails handle such errors in their code to show out a more expressive error messages.
I hope this helps anyone.
I have two models (user & agent). I then create a user and an agent. I am expecting to see the association when using the blueprint routes for BOTH /user and /agent. I am only seeing the user model is associated with an agent via the /agent blueprint. The /user blueprint does not have any reference/association to an Agent.
The issue presents itself when I am trying to access the Agent via A userId with the following command:
User.findOne(req.body.userId).populate('agent').exec(function(err, agent)
"agent" is in fact the user information...not the agent.
Here are my models:
User:
attributes: {
agent: {
model: 'agent',
via: 'owner'
}
}
Agent:
attributes: {
owner: {
model: 'user'
}
}
Thanks for reading!
Sails does not fully support one-to-one model associations--you have to set the "foreign key" on both sides. See https://stackoverflow.com/a/27752329/345484 for more information. I'm tempted to just close this question as a duplicate of that one, but the setup is slightly different.
You have understood the populate wrong. populate() doesn't replace last call with new information. It takes attribute from model (that you specify in populate('attribute')) and replace id of that attribute in your model with another model information looked up by its id.
Let's dive into example.
You have User and Agent model.
// api/models/User.js
module.exports = {
attributes: {
agent: {
model: 'Agent',
via: 'owner'
}
}
};
// api/models/Agent.js
module.exports = {
attributes: {
owner: {
model: 'User',
via: 'agent'
}
}
};
You are calling User.findOne(userId).populate('agent').exec(function(err, user) {} and expect to get only agent as I understood. Wrong. It returns User model with Agent model as an attributes of User model.
// api/controllers/AnyController.js
module.exports = {
index: function(req, res) {
User
.findOne(req.param('userId'))
.populate('agent')
.then(function(user) {
console.log(user); // User information
console.log(user.agent); // Agent information for that user
return user;
})
.then(res.ok)
.catch(res.negotiate);
}
};
You can read about population more here - http://mongoosejs.com/docs/populate.html
In MVC peoples are using join query to join the two different tables, but In sails.js what I have to use? There is any method in waterline?
The answer based on database you are using.
For instance, you need to populate values in Mongo not to join. Or you need to join tables if you are using MySQL or similar.
In a nutshell, all this stuff is covered via Waterline. So you can just declare model in api/models with associations. Joining and populating is executing under the Waterline adapter.
For instance, you have User and Comment.
// api/models/User.js
module.exports = {
attributes: {
name: {
type: 'string'
},
comments: {
collection: 'Comment',
via: 'user'
}
}
};
// api/models/Comment.js
module.exports = {
attributes: {
text: {
type: 'string'
},
user: {
model: 'User',
via: 'comments'
}
}
};
Then you are execute User.find() and get already joined\populated tables from database.
But, if you want to execute manual joining, you can use .populate() method on Model instance. For instance:
// api/controllers/AnyController.js
module.exports = {
action: function(req, res) {
User
.findOne('ID_HERE')
.populate('comments')
.then(function(result) {})
.catch(function(error) {});
}
};
You can read more about populate here - http://sailsjs.org/documentation/reference/waterline-orm/queries/populate
I'm new to Node.js and i'm trying to run a sort of insert query. Here is my code:
exports.savetea = function(req, res){
var quantity = req.query.quantity;
var Sequelize = require('sequelize');
var sequelize = new Sequelize('nodehmssm', 'root', 'root', {host: "localhost", port: 3306, dialect: 'mysql'});
var Tea = sequelize.import(__dirname + "/model/tea");
Tea.build({quantity: quantity, status: "active"}).save();
res.send("Tea added...");
};
My tea.js file is as follows:
module.exports = function(sequelize, DataTypes) {
return sequelize.define("tea", {
id : DataTypes.INTEGER,
quantity : DataTypes.INTEGER,
status : DataTypes.STRING,
created_at : DataTypes.DATE
});
};
Whenever I run the code, I get the error
TypeError: Object [object Object] has no method 'save'
Also to mention, the settings are good as I can run the login code select * from where...
What am I doing wrong?
A Somewhat Misleading Error Message
In Sequelize, you cannot save an object with undefined properties.
Since your model had no defaultValue, and you didn't define id or created_at, the .build() method was not creating a .save() method on the object it returned.
For more on the documentation, checkout their documentation on built instances and this section on how to implement autoincrement.
I know this is an old question, but perhaps I can help someone that has that problem.
Actualy the problem is that you are calling a .save() method of something that doesnt have such a method.
This is a common mistake. You are not using the .build() method correctly. See example below.
What you are doing:
Tea.build({quantity: quantity, status: "active"}).save();
What you should be doing is this:
var actualObject = Tea.build({quantity: quantity, status: "active"});
actualObject.save() //etc...
You see an error because Tea is actualy an object of the model NOT a instace of that model that you just built.
Hope that helps.
The type error you are getting is stating the object Tea has no method called .save. In your code:
Tea.build({quantity: quantity, status: "active"}).save();
Tea is your object and build is calling an instance method called .save where you want to insert your JSON data. When you call either a class or instance method from a specific sequelize model, you will get the error you found.
I recommend for you to use a template for your sequelize models to help resolve this common mistake. Below is my suggestion:
modelTemplate.js
module.exports = function(sequelize, DataTypes) {
return sequelize.define('User', {
col1: DataTypes.STRING,
col2: DataTypes.STRING
},{
classMethods: {
doSomething: function(successcb, errcb, request) {}
},
instanceMethods: {
saveSomething: function(data) {}
}
});
};
For your specific answer. I would adjust your call to the model with the following lines:
var data_json = {quantity: quantity, status: "active"};
Tea.build().save(data_json);
tea.js
module.exports = function(sequelize, DataTypes) {
return sequelize.define('tea', {
id : DataTypes.INTEGER,
quantity : DataTypes.INTEGER,
status : DataTypes.STRING
},{
classMethods: {
doSomething: function(successcb, errcb, request) {}
},
instanceMethods: {
save: function(data) {}
var new_instance = Tea.build({
quantity: data[0].quantity,
status: data[0].status
});
//saving instance
// you can also build, save and access the object with chaining:
new_instance
.save()
.success(function(){}).error("instance error in .save() method");
}
});
};
Edit: made a correction to a comma in the template.