Sequelize find sum in Many to Many Relationship - mysql

I need to build a many-to-many relationship between entities Product and Store(Wharehouse).
So I managed to build a relationship with Sequlize like this.
Product.belongsToMany(Store, { through: 'product_store' });
Store.belongsToMany(Product, { through: 'product_store' });
product_store is the table that holds the relationship with Store and Product. When saving a product in a store in need some additional details like quantity so I added a field to the product_store
const Product_Store = sequelize.define("product_store", {
id: {
type: INTEGER,
primaryKey: true,
autoIncrement: true,
},
uid: {
type: STRING,
allowNull: false,
unique: true,
},
ws_price: {
type: DECIMAL(10, 2),
},
discount: {
type: DECIMAL(10, 2),
},
bonus: {
type: DECIMAL(10, 2),
},
quantity: {
type: DECIMAL(10, 2),
},
total: {
type: DECIMAL(10, 2),
},
active: {
type: BOOLEAN,
defaultValue: true,
},
createdAt: {
type: DATE,
},
updatedAt: { type: DATE },
});
I managed to save the product in the store in the following way
await store.addProduct(product, {
through: {
uid: "3462",
ws_price: 100,
discount: 2,
bonus: 1,
quantity: 2000,
total: 200000,
}
})
The values are saved properly and I could get products with store.getProdcuts(). But now I need to query the products and the sum of their quantities in all stores. I've started to query it with
let products = await Product.findAll({
include: {
model: Store,
through: { attributes: ["ws_price", "discount", "bonus", "quantity", "total"] }
}
})
Which queries the product with store details(including the quantity) like this
[
{
"id": 1,
"uid": "4323",
"name": "Sunlight",
"active": true,
"createdAt": "2022-01-24T06:38:47.000Z",
"updatedAt": "2022-01-24T06:38:47.000Z",
"supplierId": null,
"typeId": null,
"unitId": null,
"stores": [
{
"id": 1,
"uid": "122333",
"name": "Store1",
"active": true,
"createdAt": "2022-01-24T06:38:47.000Z",
"updatedAt": "2022-01-24T06:38:47.000Z",
"product_store": {
"ws_price": "150.00",
"discount": "2.00",
"bonus": "1.00",
"quantity": "2000.00",
"total": "300000.00"
}
},
{
"id": 2,
"uid": "34523",
"name": "Store2",
"active": true,
"createdAt": "2022-01-24T06:38:47.000Z",
"updatedAt": "2022-01-24T06:38:47.000Z",
"product_store": {
"ws_price": "300.00",
"discount": "2.00",
"bonus": "1.00",
"quantity": "3000.00",
"total": "300000.00"
}
}
]
}
]
I only need to get the product details and the sum of product in all stores how can I achieve this?. I'm not familiar with using Count/Sum with relationships. Thanks in advance!

The most correct way is to use a subquery via Sequelize.literal like this:
let products = await Product.findAll({
attributes: {
include: [
[Sequelize.literal('(SELECT sum(product_store.quantity) FROM product_store WHERE product_store.product_id=product.id)'), 'product_quantity']
]
}
})

Related

Sequelize populate joined table attributes

I have a few tables and I want to do some includes on a joined table, but I can't seem to figure it out. Here are my models:
/* Staff model */
const model = {
fisrName: {
type: DataTypes.INTEGER,
references: { model: 'Roles', key: 'id' },
},
lastName: {
type: DataTypes.INTEGER,
references: { model: 'Profiles', key: 'id' },
}
};
const Staff = createModel('Staff', model, { paranoid: true });
export default Staff
/* Service model */
const model = {
name: {
type: DataTypes.STRING,
},
category: {
type: DataTypes.STRING,
},
description: {
type: DataTypes.STRING,
}
};
const Service = createModel('Service', model, {});
export default Service;
/* Appointment model */
const model = {
endDate: {
type: DataTypes.DATE,
},
startDate: {
type: DataTypes.DATE,
},
day: {
type: DataTypes.DATE,
},
};
const Appointment = createModel('Appointment', model, {})
Appointment.belongsToMany(Service, { through: 'Products', as: 'products' });
export default Appointment;
/* Products model */
const model = {
serviceId: {
type: DataTypes.INTEGER,
},
appointmentId: {
type: DataTypes.INTEGER,
},
staffId: {
type: DataTypes.INTEGER,
references: { model: 'Staff', key: 'id' },
}
};
const Product = createModel('Product', model, {});
Product.belongsTo(Staff, { foreignKey: 'staffId', as: 'staff' });
export default Product;
This is my appointment query, where I include the services array, and on this services array, I have a Products object, and in this object I have a staffId that I want to populate, and I'm not sure how. I have tried different ways, but nothing worked.
const appointment = await Appointment.findByPk(req.params.id, {
include: [
{
model: Service,
as: 'services',
through: { attributes: { include: ['id', 'staffId', 'serviceId', 'appointmentId'], exclude: ['createdAt', 'updatedAt', 'AppointmentId', 'ServiceId'] },
},
],
});
And this is my response:
{
"startDate": "date",
"endDate": "date",
"day": "date",
"services": [{
"id": 1,
"name": "Service name",
"category": "service category",
"description": "service description",
"Products": {
"id": 1,
"staffId": 2,
"serviceId": 1,
"appointmentId": 1
}
}]
}
What I want is to do is to populate the staffId from Products with the model from the collection, something like this:
{
"startDate": "date",
"endDate": "date",
"day": "date",
"services": [{
"id": 1,
"name": "Service name",
"category": "service category",
"description": "service description",
"Products": {
"id": 1,
"staffId": {
"firstName": "First Name",
"lastName": "Last Name"
},
"serviceId": 1,
"appointmentId": 1
}
}]
}
const appointment = await Appointment.findByPk(req.params.id, {
include: [
{
model: Service,
as: 'services',
through: {
attributes: { include: ['id', 'staffId', 'serviceId', 'appointmentId'], exclude: ['createdAt', 'updatedAt', 'AppointmentId', 'ServiceId'] },
},
include: [{
as: 'Products', model: Product,
include: 'staff'
}]
}
]
});

MongoDB convert Select from UNION with Group By with sum

I'm building a COVID API in NodeJS which have very in-depth detail about my country.
Due huge bill I decided to rewrite my whole database from MySQL to NoSQL which is more affordable for me.
Basically what I have to do is, if date_onset is empty then I will use date_specimen as proxy.
I have the following MySQL query which I need to convert to NoSQL.
SELECT count(a.cases) as cases, a.date FROM
(SELECT date_specimen AS cases, date_specimen AS date from case_informations WHERE (date_specimen <> '' AND date_onset = '')
UNION ALL
SELECT date_onset AS cases, date_onset AS date FROM case_informations WHERE date_onset <> '') AS a
GROUP BY a.date ORDER BY a.date ASC
The Document:
{
"_id": {
"$oid": "5f29a6dcc4ce73be6be928ff"
},
"case_code": "C611583",
"age": 20,
"age_group": "20-24",
"sex": "female",
"date_specimen": "",
"date_result_release": "2020-04-22",
"date_rep_conf": "2020-04-24",
"date_died": "",
"date_recover": "",
"removal_type": "recovered",
"admitted": "no",
"region_res": "NCR",
"prov_res": "",
"city_mun_res": "",
"city_muni_psgc": "",
"health_status": "recovered",
"quarantined": "no",
"date_onset": "",
"pregnant_tab": "no",
"validation_status": "Removal Type is \"Recovered\", but no Recovered Date is recorded\nRemoval Type is \"Recovered\", but no Recovered Date is recorded\nHealth Status is \"Recovered\", but no Date Recovered is recorded\nHealth Status is \"Recovered\", but no Date Recovered is recorded"
}
This is the closest I can get:
collection.aggregate([
{$project: {date_specimen: 1, date_onset: 1}},
{$lookup:
{
from: 'case_informations',
pipeline: [
{$match: {date_specimen: {$exists: true}, date_onset: ''}},
{$group: {_id: '$date_specimen', cases: {$sum: 1}}},
{$sort: {_id: 1}},
],
as: 'a',
},
},
{$lookup:
{
from: 'case_informations',
pipeline: [
{$match: {date_onset: {$exists: true}}},
{$group: {_id: '$date_onset', cases: {$sum: 1}}},
{$sort: {_id: 1}},
],
as: 'b',
},
},
{$project: {'a': 1, 'b': 1}},
]).limit(1);
result:
{
_id: 5f29a6dcc4ce73be6be928fc,
a: [
{ _id: '2020-03-04', cases: 1 },
{ _id: '2020-03-06', cases: 8 },
{ _id: '2020-03-07', cases: 48 },
{...}
],
b: [
{ _id: '2020-03-03', cases: 45 },
{ _id: '2020-03-04', cases: 32 },
{ _id: '2020-03-05', cases: 55 },
{...}
]
}
expected:
{
_id: 5f29a6dcc4ce73be6be928fc,
UnionOfAandC: [
{ _id: '2020-03-03', cases: 45 },
{ _id: '2020-03-04', cases: 33 }, // merge object with same date
{ _id: '2020-03-05', cases: 55 },
{ _id: '2020-03-06', cases: 8 },
{ _id: '2020-03-07', cases: 48 },
{...},
],
}
After some trial and error I finally solved it
await collection.aggregate([
{$match: {$or: [{'date_onset': {'$ne': ''}}, {'date_specimen': {'$ne': ''}}]}},
{
$group: {
_id: {
'date': {
$cond: {
if: {$eq: ['$date_onset', '']}, then: '$date_specimen', else: '$date_onset',
},
},
},
cases: {$sum: 1},
},
},
{$sort: {'_id.date': 1}},
{$project: {'_id': 0, 'date': '$_id.date', 'cases': 1}},
]);

Get hierarchical structure data nodejs - sequelizejs - mysql

Here is table folders
id name isSub parentid status
------------------------------------------------------
1 Main Folder1 0 NULL 1
2 Main Folder2 0 NULL 1
3 Sub Main Fol2 1 2 1
4 Sub Main Fol2 1 3 1
5 Sub Main Fol1 1 1 1
6 Sub Main Fol1 1 1 1
7 Main Folder3 0 NULL 1
8 Sub Main Fol1 1 1 1
------------------------------------------------------
This is the table i used for getting hierarchical structure. I'm using nodejs - sequelizejs.
Here is my model
sequelize.define('Folders', {
id: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.STRING(100),
allowNull: false,
validate: {
len: [1, 100]
}
},
isSub: {
type: Sequelize.ENUM(),
values: ['0', '1'],
allowNull: false,
defaultValue: '0'
},
parentid: {
type: Sequelize.INTEGER,
allowNull: true,
validate: {
isNumeric: true
}
},
status: {
type: Sequelize.ENUM(),
values: ['0', '1', '2'],
allowNull: false,
defaultValue: '1'
},
},
{
tableName: 'folders'
}
);
Folders.associate = function(models) {
models.Folders.hasMany(models.Folders, {
foreignKey: 'parentid',
as: 'children'
});
};
And my controller function to get the list
models.Folders.findAll({
where: {
isSub : 1,
status : '1',
},
include: [{
model: models.Folders,
as: 'children',
where: {
status: '1'
},
required: false
}],
required: false
}).then((data) => {
data.map((foldData) => {
return Object.assign({}, {
fid: foldData['id'],
fName: foldData['name'],
fisSub: (foldData['isSub'] === '1')? 'Yes': 'No',
subFolders:
foldData['children'].map((sfoldData, index) => {
return Object.assign({}, {
sfid: sfoldData['id'],
sfName: sfoldData['name'],
sisSubFold: (sfoldData['isSub']) === '1'? 'Yes': 'No',
})
}),
});
});
});
By above code I'm getting the result in API calls as below
[
{
"fid": "2",
"fName": "Main Folder2",
"fisSub": "No",
"subCategory": [
{
"sfid": "3",
"sfName": "Sub Main Fol2",
"sisSubFold": "Yes"
}
]
},
{
"fid": "1",
"fName": "Main Folder1",
"fisSub": "No",
"subCategory": [
{
"sfid": "5",
"sfName": "Sub Main Fol1",
"sisSubFold": "Yes"
},
{
"sfid": "6",
"sfName": "Sub Main Fol1",
"sisSubFold": "Yes"
},
{
"sfid": "8",
"sfName": "Sub Main Fol1",
"sisSubFold": "Yes"
}
]
},
{
"fid": "7",
"fName": "Main Folder3",
"fisSub": "No",
"subCategory": []
}
]
Here for "fid": "2" having two level hierarchical structure in table, but I'm getting first level only.
Please correct my code for getting hierarchical structure.
Note: there will many hierarchical structure, not only 2nd / 3rd /soon levels.
I searched many things, but not get any correct one. Please help me out from this.
Thanks in advance
What you want to do is recursive query and it is not supported yet on Sequelize, there is a proposal here https://github.com/sequelize/sequelize/issues/4890 the work around is to use the raw function to write your own CTE request.

Sort a store data based on inner JSON field sencha

i have a json in the following format:
{
"collection": [
{
"id": 4,
"tickets": [
{
"price": 40,
},
{
"price": 50,
}
],
},
{
"id": 1,
"tickets": [
{
"price": 10,
},
{
"price": 15,
}
]
},
]
}
STORE:
Ext.define("myProject.store.ABCs", {
extend: "Ext.data.Store",
config: {
model: "myProject.model.ABC",
autoLoad: false,
proxy: {
type: "ajax",
url: '', //myURL
reader: {
type: "json",
rootProperty: "collection", // this is the first collection
},
},
}
});
For this particular JSON i created the models as:
Ext.define("myProject.model.ABC", {
extend: "Ext.data.Model",
config: {
idProperty: "id",
fields:[
{name: "id", type: "int" },
],
hasMany: [
{
model: "myProject.model.XYZ",
name: "tickets",
associationKey: "tickets",
},
],
}
});
And second model as:
Ext.define("myProject.model.XYZ", {
extend: "Ext.data.Model",
config: {
// idProperty: "id",
fields:[
{name: "inner_id", type: "int" },
],
belongsTo: 'myProject.model.ABC'
}
});
This particular code creates a store and populates the models correctly.
var store = Ext.getStore('ABCs');
Now i want to sort this store based on store.tickets().getAt(0).get('price') that is sort the ABC records based on XYZ's first price property.
In the above json. ABC Records will be: [{id:4}, {id:1}]
But since first price in XYZ (40 > 10), i want to sort them and create [{id:1}, {id:4}]
Take a look at the Ext.util.Sorter class, where you can set a sorterFn. See the example at the top of the page - you should be able to simply write the logic for sorting records the way you describe.

jqGrid not displaying valid json

After reading most of the questions and trying to find an answer, I think I give up.
So the problem I'm having is just like what other people have been having, that is, jqGrid doesn't display the valid json received from a spring mvc servlet.
Here is my json:
{
"firstRecordIndex": 0,
"pageSize": 20,
"sortDirection": {
"name": "descending",
"code": 1
},
"sortCriterion": "id",
"pageNumber": 1,
"objectsPerPage": 20,
"fullListSize": 1,
"searchId": null,
"totalPages": 1,
"index": 0,
"list": [
{
"issueNumber": "ABC-6799",
"entryDate": 1345763592879,
"billingType": "Non-Billable",
"notes": "Hello",
"customer": "XYZ",
"id": 1,
"hours": 5,
"userName": "John Doe"
}
]
}
An here is my jqGrid:
jQuery(function(){
jQuery("#list").jqGrid({
url:"http://localhost:8080/worktime/timesheet.html?type=list&ajax=true",
datatype: "json",
colNames:["User", "Hours", "Date", "Billing Type", "Notes"],
colModel :[
{name:"userName", width:90, editable:false, jsonmap:"userName", editoptions:{readonly:true,size:10}},
{name:"hours", width:55, editable:true, jsonmap:"hours", editoptions:{size:10}},
{name:"entryDate", width:90, editable:true, jsonmap:"entryDate", formatter:"date", "formatoptions":{"srcformat":"m/d/Y", "newformat":"m/d/Y"},
editoptions:{size:12, dataInit:function(el){jQuery(el).datepicker({dateFormat:"m/d/y"});},defaultValue: "m/d/y"}
},
{name:"billingType", width:100, editable:true, jsonmap:"billingType", edittype:"select", editoptions:{value:"INV:Billable-Invoice;TSA:Billable-TSA;NON:Non-Billable;OH:Overhead"}},
{name:"notes", width:150, sortable:false, editable: true, jsonmap:"notes", edittype:"textarea", editoptions:{rows:"2",cols:"20"}}
],
pager: "#pager",
rowNum:10,
rowList:[10,20,30],
sortname: "entryDate",
sortorder: "asc",
jsonReader: {
root: "list",
total: "totalPages",
page: "pageNumber",
records: "fullListSize",
repeatitems: false,
id: "0"
},
caption: "Timesheet History",
viewrecords: true,
gridview: true,
});
What's the correct jqGrid configuration? I am using jqGrid 4.4.0 and jQuery 1.7.2. Your help is much appreciated. Thanks!!
Why do you have this: id: "0"?
Your jsonReader should probably be:
jsonReader: {
root: "list",
total: "totalPages",
page: "pageNumber",
records: "fullListSize",
repeatitems: false,
id: "id"
}