Populating multiple tables in sails waterline orm - mysql

I am working on a sails applications which contains multiple(>2) tables which I need to join with the help of populate method
e.g.
Category.js model
attributes: {
CategoryID:{
type:"integer",
required:true,
primaryKey:true,
autoIncrement:true
},
SubCategories:{ //REFERING TO SUB-CATEGORY TABLE
collection:'SubCategory',
via:'CategoryID'
},
CategoryName:{
type:"string",
required:true,
maxLength:50
}
}
this is SubCategory.js model.
attributes: {
id:{
type:'integer',
required:true,
primaryKey:true,
autoIncrement:true,
maxLength:10,
columnName:'SubCategoryID'
},
CategoryID:{
model:'Category' //REFERING TO CATEGORY TABLE
},
ProductsOfCategory:{ //REFERING TO PRODUCT TABLE
collection:'Product',
via:'SubCategoryID'
},
SubCategory:{
type:"string",
required:true,
maxLength:50
}
}
and Product.js model
attributes: {
id: {
type: 'integer',
primaryKey: true,
autoIncrement: true,
maxLength: 10,
columnName:'ProductID'
},
SubCategoryID: {
model:'SubCategory'
},
ProductDetails:{
collection:'ProductDetails',
via:'ProductID'
},
ProductName: {
type: "string",
required: true,
maxLength: 50
}
}
and ProductDeatils.js model
attributes: {
id: {
type: 'integer',
primaryKey: true,
autoIncrement: true,
maxLength: 10,
columnName:'ProductDetailID'
},
ProductID:{
model:'Product'
},
Size:{
type:"string",
required:true,
maxLength:10
},
Color:{
type:"string",
required:true,
maxLength:10
}
}
On Populating, I am able to populate the category and sub-category of each category.
Category.find()
.populate('SubCategories')
.exec(function(err, categories){
if (err) {
console.log(err);
return res.json(err);
}
console.log(categories);
res.json(categories);
})
How to populate the all above table in one go such that after final query we get all the above details in one json.
We get join of all above tables
that is category having all sub-categories, sub-category having all products and all product have product details in one json

You ask a great question. There has been massive interest in getting nested populate feature into sails, literally tens of issue requests and PRs etc.
Take a look at some history here:
[FEATURE REQUEST] Recursively populate #308 - i was late to the party, making the request on October 29th 2014 as you'll see in the history.
As far as I know, most conversations eventually converged here (after a couple of years of Sails users requesting the feature):
Deep populate #1052 (the issue remains open as of writing 14 Jan 2016)
It is unclear from the state of that Issue where we are. The history of both links does suggest alternative workarounds others have used.
My hunch is that recursive populate is not supported out of the box.
What I did when using waterline model associations with SailsJS, was work with a package like async.js - use something like waterfall to explicitly populate the child relationships programmatically. You can combine doing this with overriding the default toJSON() of the models you invoke to add their relationships (which you have programmatically populated) to the JSON response. You could equally choose to use the built-in promises to achieve the same thing.
Found this (dated, 2014) SOF Question which offers more information.
Someone, do please correct me here if I have missed this feature addition in a recent Sails or Waterline version - couldn't find anything in the release notes for either project to say this was supported.

Related

Unable to create BLOB/Binary types with LoopBack 4

I'm trying to use Loopback for my new projects, but I've been facing some problems...
I have the habit of storing my UUIDs ID in a binary format at my databases, here's an example:
#model({
settings: { mysql: { table: 'application' } },
})
export class Application extends Entity {
#property({
type: 'buffer',
required: true,
generated: false,
id: true,
dataLength: 16,
})
id: BinaryType;
[...]
}
But when I try to do the migration, I've been receiving that error from mysql:
"BLOB/TEXT column 'id' used in key specification without a key length"
I really tried everything and nothing works. Hope that you'll be able to help me!
Thanks a lot!
I'll show the answer for this question that I made.
Just define your model with the following info:
#property({
required: true,
mysql: {
columnName: 'application_id',
dataType: 'VARBINARY',
dataLength: 16,
nullable: 'NO'
}
})
application_id: string;
It worked like a charm for me :)
Thank you all!

Where condition in nested include in Sequelize

I have a model with 3 entities, Documents, Employees and Managers. A Document belongs to and Employee and an Employee belongs to a Manager.
My objective is to retrieve all the documents of a manager employees.
My piece of code is working
Document.findAll({
include: [{
model: models.Employee,
required: true,
as: 'employee',
include: [{
model: models.Manager,
required: true,
as: 'manager',
}],
}],
But I'm not really satisfied, I would like to have my where condition outside my include but when I try
where {
'employee.manager.id': id
}
an error is raised.
Is it possible to setup a where condition outside the includes ?
EDIT :
I changed my code to
Document.findAll({
where: {'$employee.manager.id$': id},
include: [{
model: models.Employee,
required: true,
as: 'employee',
include: [{
model: models.Manager,
required: true,
as: 'manager',
where: { id: managerId },
}],
}],
and it's working.
Now, I would like to refine my model. Each document as a type (administrative, evaluation ...) and I would like to retrieve the most recent document for each type for each manager. I used an order by which is working fine and tried to use a group by which is not working
order: [ [ 'updatedAt', 'DESC' ]],
group: ['type'],
I get the following message : column \"Document.id\" must appear in the GROUP BY clause or be used in an aggregate function.
Any idea what I'm doing wrong ?
Yes, you can do that ,
Issue with current one :
where {
'employee.manager.id': id // <--- Here 'employee.manager.id' cosidered as whole column name
}
Solution :
where {
'$employee.manager.id$': id //<--- To make sequlize realize you need to place it b/w $$ ,
//or
sequelize.col('employee.manager.id') : id // <--- You can try this also
}

SailsJS Blueprints going haywire

I had been working on an app using SailsJS v0.12. The app was working as expected. But, for some modules, it made sense to upgrade to v1.0 because it offered some great features, and the website implied (or seemed so) that the upgrade process was simple.
That wasn't the case. After a day of tedious refactoring, I succeeded in building the existing code and started working on the new modules. The problem to focus on began here.
I ran the functions as I did previously in POSTman and I stopped getting response. To check individual models, I tried using the Blueprints. Since no structural changes were made to any of the models, this should've worked. But I couldn't get any response in POST; GET was still working. I logged off for a break and then made a GET request to a model with some associations (working perfectly before).
I got this response
UsageError: Invalid populate(s). Details: Could not populate
customer because of ambiguous usage. This is a singular ("model")
association, which means it never refers to more than one associated
record. So passing in subcriteria (i.e. as the second argument to
.populate()) is not supported for this association, since it
generally wouldn't make any sense. But that's the trouble-- it looks
like some sort of a subcriteria (or something) was provided! (Note
that subcriterias consisting ONLY of omit or select are a special
case that does make sense. This usage will be supported in a future
version of Waterline.)
Here's what was passed in: { limit: 30 }
The complete JSON response is:
{
"stack": "UsageError: Invalid populate(s).\nDetails:\n Could not populate `customer` because of ambiguous usage. This is a singular (\"model\") association, which means it never refers to more than _one_ associated record. So passing in subcriteria (i.e. as the second argument to `.populate()`) is not supported for this association, since it generally wouldn't make any sense. But that's the trouble-- it looks like some sort of a subcriteria (or something) _was_ provided!\n(Note that subcriterias consisting ONLY of `omit` or `select` are a special case that _does_ make sense. This usage will be supported in a future version of Waterline.)\n\nHere's what was passed in:\n{ limit: 30 }\n\n at findRecords (D:\\Dev\\NodeJS\\zingr\\node_modules\\sails\\lib\\hooks\\blueprints\\actions\\find.js:40:21)\n at routeTargetFnWrapper (D:\\Dev\\NodeJS\\zingr\\node_modules\\sails\\lib\\router\\bind.js:181:5)\n at callbacks (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:164:37)\n at param (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:138:11)\n at pass (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:145:5)\n at nextRoute (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:100:7)\n at callbacks (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:167:11)\n at alwaysAllow (D:\\Dev\\NodeJS\\zingr\\node_modules\\sails\\lib\\hooks\\policies\\index.js:224:11)\n at routeTargetFnWrapper (D:\\Dev\\NodeJS\\zingr\\node_modules\\sails\\lib\\router\\bind.js:181:5)\n at callbacks (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:164:37)\n at param (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:138:11)\n at pass (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:145:5)\n at nextRoute (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:100:7)\n at callbacks (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:167:11)\n at module.exports (D:\\Dev\\NodeJS\\zingr\\node_modules\\sails\\lib\\hooks\\cors\\clear-headers.js:14:3)\n at routeTargetFnWrapper (D:\\Dev\\NodeJS\\zingr\\node_modules\\sails\\lib\\router\\bind.js:181:5)\n at callbacks (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:164:37)\n at param (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:138:11)\n at pass (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:145:5)\n at nextRoute (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:100:7)\n at callbacks (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:167:11)\n at sails.router.bind._middlewareType (D:\\Dev\\NodeJS\\zingr\\node_modules\\sails\\lib\\hooks\\csrf\\index.js:148:11)\n at routeTargetFnWrapper (D:\\Dev\\NodeJS\\zingr\\node_modules\\sails\\lib\\router\\bind.js:181:5)\n at callbacks (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:164:37)\n at param (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:138:11)\n at pass (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:145:5)\n at nextRoute (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:100:7)\n at callbacks (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:167:11)\n at _addResViewMethod (D:\\Dev\\NodeJS\\zingr\\node_modules\\sails\\lib\\hooks\\views\\res.view.js:372:3)\n at routeTargetFnWrapper (D:\\Dev\\NodeJS\\zingr\\node_modules\\sails\\lib\\router\\bind.js:181:5)\n at callbacks (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:164:37)\n at param (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:138:11)\n at pass (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:145:5)\n at nextRoute (D:\\Dev\\NodeJS\\zingr\\node_modules\\express\\lib\\router\\index.js:100:7)",
"message": "Invalid populate(s).\nDetails:\n Could not populate `customer` because of ambiguous usage. This is a singular (\"model\") association, which means it never refers to more than _one_ associated record. So passing in subcriteria (i.e. as the second argument to `.populate()`) is not supported for this association, since it generally wouldn't make any sense. But that's the trouble-- it looks like some sort of a subcriteria (or something) _was_ provided!\n(Note that subcriterias consisting ONLY of `omit` or `select` are a special case that _does_ make sense. This usage will be supported in a future version of Waterline.)\n\nHere's what was passed in:\n{ limit: 30 }\n",
"name": "UsageError",
"code": "E_INVALID_POPULATES",
"details": "Could not populate `customer` because of ambiguous usage. This is a singular (\"model\") association, which means it never refers to more than _one_ associated record. So passing in subcriteria (i.e. as the second argument to `.populate()`) is not supported for this association, since it generally wouldn't make any sense. But that's the trouble-- it looks like some sort of a subcriteria (or something) _was_ provided!\n(Note that subcriterias consisting ONLY of `omit` or `select` are a special case that _does_ make sense. This usage will be supported in a future version of Waterline.)\n\nHere's what was passed in:\n{ limit: 30 }"
}
The confusing aspect is that this is only happening locally. The blueprints work perfectly as expected on the deployment (same code, double-checked) hosted on Zeit.co. I have tested this enough times to deduce that this isn't a fault in my models. Nonetheless, here are the models - Bill and Customer:
models\Bill.js
module.exports = {
primaryKey:'id',
attributes: {
createdAt: { type: 'number', autoCreatedAt: true, },
updatedAt: { type: 'number', autoUpdatedAt: true, },
id:{
type:'string',
required:true
},
bill_number:{
type:'string'
},
admin_id:{
type:'string'
},
channel:{
type:'string'
},
amount:{
type:'number'
},
discount:{
type:'number',
defaultsTo:0
},
order_date:{
type: 'number'
},
status:{
type:'string',
isIn:['generated', 'settled', 'canceled'],
defaultsTo:'generated'
},
type:{
type:'string',
isIn:['dine-in', 'take-away', 'delivery', 'logistics-delivery'],
required:true
},
logisticsPartner:{
type:'string',
defaultsTo:'phone'
},
//association
customer:{
model:'customer'
},
store:{
model:'store'
},
orderContents:{
collection:'billmapping',
via:'bill'
},
transactions:{
collection:'transaction',
via:'bill'
}
}
};
models\Customer.js
module.exports = {
attributes: {
createdAt: { type: 'number', autoCreatedAt: true, },
updatedAt: { type: 'number', autoUpdatedAt: true, },
id: { type: 'number', autoIncrement: true}, // <-- for SQL databases
name:{
type:'string'
},
number:{
type:'string',
unique:true
},
email:{
type:'string',
unique:true
},
age:{
type:'number'
}
}
};
This issue has had me pulling my hair out for 8 hours now. Can anybody shed some light or share some suggestions?
Thanks in advance.

A Sequelize column that cannot be updated

Is it possible to create a column on a MySQL table using Sequelize that can be initialized when creating a new row, but never updated?
For example, a REST service allows a user to update his profile. He can change any field except his id. I can strip the id from the request on the API route, but that's a little redundant because there are a number of different models that behave similarly. Ideally, I'd like to be able to define a constraint in Sequelize that prevents the id column from being set to anything other than DEFAULT.
Currently, I'm using a setterMethod for the id to manually throw a ValidationError, but this seems hackish, so I was wondering if there's a cleaner way of doing this. Even worse is that this implementation still allows the id to be set when creating a new record, but I don't know a way around this as when Sequelize generates the query it calls setterMethods.id to set the value to DEFAULT.
return sequelize.define('Foo',
{
title: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
notEmpty: true
}
}
},
{
setterMethods: {
id: function (value) {
if (!this.isNewRecord) {
throw new sequelize.ValidationError(null, [
new sequelize.ValidationErrorItem('readonly', 'id may not be set', 'id', value)
]);
}
}
}
}
);
Look at this Sequelize plugin:
https://www.npmjs.com/package/sequelize-noupdate-attributes
It adds support for no update and read-only attributes in Sequelize models.
In your specific case, you could configure the attribute with the following flags:
{
title: {
type: DataTypes.STRING,
allowNull: false,
unique : true,
noUpdate : true
}
}
That will allow the initial set of the title attribute if is null, and then prevent any further modifications once is already set.
Disclaimer: I'm the plugin author.

ExtJs - Creating a Grid from records in a database

I'm using ExtJS 4.2 and I have some records in a MySql database. My question is: How can I create a grid that displays the records in the database?
I tried using ResultSet in a servlet to retrieve the records from the database, but I'm not sure how to proceed from there.
How can i populate the fields in the grid with the records in my database?
I'm new to ExtJS and I'm finding it difficult to come up with a solution for this. Does this have something to do with the store field? If so, how do i go about achieving the above said requirement?
You need to create store, bind to the grid and then load data from server. And sure you need backend for this ExtJS4 do not provide any tools for working with databases
For example( taken from sencha docs ):
Ext.onReady(function(){
Ext.define('Book',{
extend: 'Ext.data.Model',
proxy: {
type: 'ajax',
reader: 'xml'
},
fields: [
// set up the fields mapping into the xml doc
// The first needs mapping, the others are very basic
{name: 'Author', mapping: '#author.name'},
'Title', 'Manufacturer', 'ProductGroup'
]
});
// create the Data Store
var store = Ext.create('Ext.data.Store', {
model: 'Book',
autoLoad: true,
proxy: {
// load using HTTP
type: 'ajax',
url: 'sheldon.xml',
// the return will be XML, so lets set up a reader
reader: {
type: 'xml',
// records will have an "Item" tag
record: 'Item',
idProperty: 'ASIN',
totalRecords: '#total'
}
}
});
// create the grid
Ext.create('Ext.grid.Panel', {
store: store,
columns: [
{text: "Author", flex: 1, dataIndex: 'Author'},
{text: "Title", width: 180, dataIndex: 'Title'},
{text: "Manufacturer", width: 115, dataIndex: 'Manufacturer'},
{text: "Product Group", width: 100, dataIndex: 'ProductGroup'}
],
renderTo:'example-grid',
width: 540,
height: 200
});
});
The main idea is - models are for defining structure of the record and validation(read about it here), stores - for storing and fetching(by parsing response from server or local defined data) records that match model structure(Basic store) and finally grid handles some events(like "load" or "refresh") and updating rows based on grids column defenition(docs)