How to insert a Distinct and Order in include? - sql

i'm doing a project in nodejs (express and angular) and im finding difficulties with this sequielize. I cannot add a distinct (at the beginning of the query, i receive too much copies) and i cannot add an order by for a "include field"
I went through Sequielize documentation ( http://docs.sequelizejs.com/manual/querying.html#operators-aliases ) but without success. I found some answers in here but still no success. What am I doing wrong?
return models.Inspection.findAndCountAll({
distinct: true,
attributes: [],
include: [{model: models.InspectionGroup, attributes:['ID', 'InspectionTypeModel_ID', 'Notes', 'Date'],
required: true, include: [{
model: models.InspectionTypeModel, attributes: ['Model'], required: true
}]},
{model: models.UnitElement, attributes: [], required: true, include: [{
model: models.Span, attributes: [], required: true, include: [{
model: models.StructuralUnit, attributes: [], required: true, include: [{
model: models.Building, attributes: [], required: true, include: [{
model: models.Direction, attributes: [], required: true, include: [{
model: models.Road, attributes: [], where: {Dealer_ID: dealerList}, required: true
}]
}]
}]
}]
}]
}],
order: [[ { model: models.InspectionGroup }, 'Date', 'DESC']],
limit: limit,
offset: offset
})
.then(function (results) {
res.json(results);
})
.catch(function (err) {
commons.showerror(err, res);
});
This is the sql generated:
SELECT [Inspection].[ID], [InspectionGroup].[ID] AS [InspectionGroup.ID], [InspectionGroup].[InspectionTypeModel_ID] AS [InspectionGroup.InspectionTypeModel_ID],
[InspectionGroup].[Notes] AS [InspectionGroup.Notes], [InspectionGroup].[Date] AS [InspectionGroup.Date], [InspectionGroup.InspectionTypeModel].[ID] AS [InspectionGroup.InspectionTypeModel.ID],
[InspectionGroup.InspectionTypeModel].[Model] AS [InspectionGroup.InspectionTypeModel.Model]
FROM [Inspection] AS [Inspection]
INNER JOIN [InspectionGroup] AS [InspectionGroup] ON [Inspection].[InspectionGroup_ID] = [InspectionGroup].[ID]
INNER JOIN [InspectionTypeModel] AS [InspectionGroup.InspectionTypeModel] ON [InspectionGroup].[InspectionTypeModel_ID] = [InspectionGroup.InspectionTypeModel].[ID]
INNER JOIN [UnitElement] AS [UnitElement] ON [Inspection].[UnitElement_ID] = [UnitElement].[ID] INNER JOIN [Span] AS [UnitElement.Span] ON [UnitElement].[Span_ID] = [UnitElement.Span].[ID]
INNER JOIN [StructuralUnit] AS [UnitElement.Span.StructuralUnit] ON [UnitElement.Span].[StructuralUnit_ID] = [UnitElement.Span.StructuralUnit].[ID] INNER JOIN [Building] AS [UnitElement.Span.StructuralUnit.Building] ON [UnitElement.Span.StructuralUnit].[Building_ID] = [UnitElement.Span.StructuralUnit.Building].[ID]
INNER JOIN [Direction] AS [UnitElement.Span.StructuralUnit.Building.Direction] ON [UnitElement.Span.StructuralUnit.Building].[Direction_ID] = [UnitElement.Span.StructuralUnit.Building.Direction].[ID] INNER JOIN [Road] AS [UnitElement.Span.StructuralUnit.Building.Direction.Road] ON [UnitElement.Span.StructuralUnit.Building.Direction].[Road_ID] = [UnitElement.Span.StructuralUnit.Building.Direction.Road].[ID] AND [UnitElement.Span.StructuralUnit.Building.Direction.Road].[Dealer_ID] IN (4, 12)
ORDER BY [InspectionGroup].[Date] DESC ORDER BY [ID] OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
And this is the error
Invalid usage of the option NEXT in the FETCH statement.
I can see that the sequielize generated 2 different order by, but I actually don't know why it generates the "ID order by", considering i specified that the order by should be on InspectionGroup.Date...and the "distinct:true" is totally ignored.
Edit (generated SQL with attributes: [[Sequelize.fn('distinct', Sequelize.col('ID')), 'inspectionId']])
And error: "Invalid usage of the option NEXT in the FETCH statement."
SELECT [Inspection].[ID], distinct([ID]) AS [inspectionId], [InspectionGroup].[ID] AS [InspectionGroup.ID], [InspectionGroup].[InspectionTypeModel_ID] AS [InspectionGroup.InspectionTypeModel_ID],
[InspectionGroup].[Notes] AS [InspectionGroup.Notes], [InspectionGroup].[Date] AS [InspectionGroup.Date], [InspectionGroup.InspectionTypeModel].[ID] AS [InspectionGroup.InspectionTypeModel.ID],
[InspectionGroup.InspectionTypeModel].[Model] AS [InspectionGroup.InspectionTypeModel.Model]
FROM [Inspection] AS [Inspection]
INNER JOIN [InspectionGroup] AS [InspectionGroup] ON [Inspection]. [InspectionGroup_ID] = [InspectionGroup].[ID]
INNER JOIN [InspectionTypeModel] AS [InspectionGroup.InspectionTypeModel] ON [InspectionGroup].[InspectionTypeModel_ID] = [InspectionGroup.InspectionTypeModel].[ID]
INNER JOIN [UnitElement] AS [UnitElement] ON [Inspection].[UnitElement_ID] = [UnitElement].[ID] INNER JOIN [Span] AS [UnitElement.Span] ON [UnitElement].[Span_ID] = [UnitElement.Span].[ID]
INNER JOIN [StructuralUnit] AS [UnitElement.Span.StructuralUnit] ON [UnitElement.Span].[StructuralUnit_ID] = [UnitElement.Span.StructuralUnit].[ID] INNER JOIN [Building] AS [UnitElement.Span.StructuralUnit.Building] ON [UnitElement.Span.StructuralUnit].[Building_ID] = [UnitElement.Span.StructuralUnit.Building].[ID]
INNER JOIN [Direction] AS [UnitElement.Span.StructuralUnit.Building.Direction] ON [UnitElement.Span.StructuralUnit.Building].[Direction_ID] = [UnitElement.Span.StructuralUnit.Building.Direction].[ID]
INNER JOIN [Road] AS [UnitElement.Span.StructuralUnit.Building.Direction.Road] ON [UnitElement.Span.StructuralUnit.Building.Direction].[Road_ID] = [UnitElement.Span.StructuralUnit.Building.Direction.Road].[ID] AND [UnitElement.Span.StructuralUnit.Building.Direction.Road].[Dealer_ID] IN (4, 12)
ORDER BY [Inspection].[Date] DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY

If i am not wrong, you should put at least one attribute on the route model to be able to apply distinct on that column. Otherwise, there is no column to apply it.
Let’s try and see if it works
distinct: true,
attributes: [´id’]
Hope it will help you
[EDIT]
With the following update it should be fine
return models.Inspection.findAndCountAll({
attributes: [[ Sequelize.fn('DISTINCT', Sequelize.col('Inspection.ID')), 'Inspection.ID']],
include: [{model: models.InspectionGroup, attributes:['ID', 'InspectionTypeModel_ID', 'Notes', 'Date'],
required: true, include: [{
model: models.InspectionTypeModel, attributes: ['Model'], required: true
}]},
{model: models.UnitElement, attributes: [], required: true, include: [{
model: models.Span, attributes: [], required: true, include: [{
model: models.StructuralUnit, attributes: [], required: true, include: [{
model: models.Building, attributes: [], required: true, include: [{
model: models.Direction, attributes: [], required: true, include: [{
model: models.Road, attributes: [], where: {Dealer_ID: dealerList}, required: true
}]
}]
}]
}]
}]
}],
order: [[ { model: models.InspectionGroup }, 'Date', 'DESC']],
limit: limit,
offset: offset
})
.then(function (results) {
res.json(results);
})
.catch(function (err) {
commons.showerror(err, res);
});

Related

vuetable-2 select multiple sort items in same column

I need to filter data, including or excluding, multiple possible options in a column, but I don't know how to do it.
this only shows me a "single" select below the column name, with the possible options, but I can't select multiple of them, and I can't find an option to do that.
I need to filter by 2 rooms or multiple users, but the selection is not multiple, and there is no option to do it.
Now I have this HTML code:
<v-client-table id="messages" style="width:100%;" ref="table_reference" :options="table_options" :columns="table_fields" v-model="messages_array">
</v-client-table>
and this table configuration (messages, and other vars excluded):
const app2 = {
el: "#messages-container",
data() {
return {
table_options: {
filterByColumn: true,
texts: {
filterPlaceholder: ""
},
selectable: {
mode: 'single', // or 'multiple'
only: function(row) {
return true // any condition
},
selectAllMode: 'all',
programmatic: false
},
sortIcon: {
base: 'fa fas',
up: 'fa-long-arrow-alt-up',
down: 'fa-long-arrow-alt-down',
is: 'fa-sort'
},
listColumns: {
user_id: [],
room: [],
status: []
},
sortable: ['user_id', 'status', 'room', "created"],
filterable: ['user_id', "room", "status"],
headings: {
id: '#',
user_id: 'Name',
navigator_info: 'Details',
message: 'Message',
room: 'Room',
status: 'Status',
created: 'Date',
response_to: 'Actions'
}
},
table_fields: ["id", "user_id", "message", "room", "status", "created"],
}
}
};
I think this is a common use of a data table, and there should be a way to do it. I would appreciate your help. Thank you very much in advance!

Unknown column in sequelize count

I do such node.js Sequelize query to get rows quantity of included unread_messages, so I can get amount of unread messages of specifi user. But it returns me Unknown column 'unread_messages.id' in 'field list'.
If I remove attributes: {...} error disappears
const result = await Chats.findAndCountAll({
attributes: {
include: [[Sequelize.fn('COUNT', Sequelize.col('unread_messages.id')), 'total_unread_messages']]
},
where: {
...(req.query.filters as WhereOptions),
},
include: [
{ model: Users, as: 'createdBy', required: false },
{ model: ChatTypes, as: 'type', required: false },
{
model: ChatMessages,
as: 'unread_messages',
where: {
id: {[Op.gt]: Sequelize.literal(`(
SELECT last_read_message_id
FROM chats_users
WHERE
user_id = '${req.user?.id}'
AND
chat_id = Chats.id
)`),}
},
required: false,
},
{
model: ChatMessages,
as: 'last_message',
required: false,
include: [
{ model: Users, as: 'to_user' },
{ model: Users, as: 'from_user' },
{ model: Chats, as: 'chat' },
{ model: MessageTypes, as: 'message_type' },
{
model: Users,
as: 'is_mine',
required: false,
where: { id: req.user?.id },
},
],
},
],
group:['chats.id'],
order: req.query.sort as Order,
offset,
limit,
});

I need to transform an SQL arguments into Sequelize

I need to transform an SQL arguments into Sequelize
SELECT `WeekHours`.`id_weekhours` AS `idWeekhours`, `WeekHours`.`hour`, `WeekHours`.`week_day` AS `weekDay`
FROM `week_hours` AS `WeekHours`
LEFT OUTER JOIN `user_schedule` AS `UserSchedule`
ON `WeekHours`.`id_weekhours` = `UserSchedule`.`id_weekhours` AND `UserSchedule`.`date` = '2021-05-03'
WHERE `UserSchedule`.`id_weekhours` IS NULL AND `WeekHours`.`week_day` = 'Monday';
I created this in sequelize:
await WeekHours.findAll({
attributes: ['hour', 'weekDay'],
where: {
idWeekhours: {
[Op.eq]: null
},
weekDay,
},
include: [{
attributes: [],
model: UserSchedule,
where: {
date,
},
required: false,
}],
})
but i get that:
SELECT `WeekHours`.`id_weekhours` AS `idWeekhours`, `WeekHours`.`hour`, `WeekHours`.`week_day` AS `weekDay`
FROM `week_hours` AS `WeekHours`
LEFT OUTER JOIN `user_schedule` AS `UserSchedule` ON `WeekHours`.`id_weekhours` = `UserSchedule`.`id_schedule` AND `UserSchedule`.`date` = '2021-05-03'
WHERE `WeekHours`.`id_weekhours` IS NULL AND `WeekHours`.`week_day` = 'Monday';
the issues that i have is:
wrong:
`WHERE `WeekHours`.`id_weekhours
correct:
`WHERE `UserSchedule`.`id_weekhours
wrong:
`ON `WeekHours`.`id_weekhours` = `UserSchedule`.`id_schedule
correct:
`ON `WeekHours`.`id_weekhours` = `UserSchedule`.`id_weekhours
const usedHours = await WeekHours.findAll({
attributes: ['hora'],
where: {
'$UserSchedule.id_weekhours$': {
[Op.eq]: null
},
diaSemana,
},
include: [{
attributes: [],
model: UserSchedule,
on: {
id: Sequelize.where(Sequelize.col("WeekHours.id_weekhours"), "=",Sequelize.col("UserSchedule.id_weekhours"))
},
where: {
data,
},
required: false,
}],
})

Sequelize: on a subset of model A, sum an integer-attribute of an associated model B

I want to do this:
select sum("quantity") as "sum"
from "orderArticles"
inner join "orders"
on "orderArticles"."orderId"="orders"."id"
and "orderArticles"."discountTagId" = 2
and "orders"."paid" is not null;
which results in on my data base:
sum
-----
151
(1 row)
How can I do it?
My Sequelize solution:
The model definitions:
const order = Conn.define('orders', {
id: {
type: Sequelize.BIGINT,
autoIncrement: true,
primaryKey: true
},
// ...
paid: {
type: Sequelize.DATE,
defaultValue: null
},
// ...
},
// ...
})
const orderArticle = Conn.define('orderArticles',
{
id: {
type: Sequelize.BIGINT,
autoIncrement: true,
primaryKey: true
},
// ...
quantity: {
type: Sequelize.INTEGER,
defaultValue: 1
}
},
{
scopes: {
paidOrders: {
include: [
{ model: order, where: { paid: {$ne: null}} }
]
}
},
// ...
})
Associations:
orderArticle.belongsTo(order)
order.hasMany(orderArticle, {onDelete: 'cascade', hooks: true})
I came up with this after hours of research:
db.models.orderArticles
.scope('paidOrders') // select only orders with paid: {$ne: null}
.sum('quantity', { // sum up all resulting quantities
attributes: ['quantity'], // select only the orderArticles.quantity col
where: {discountTagId: 2}, // where orderArticles.discountTagId = 2
group: ['"order"."id"', '"orderArticles"."quantity"'] // don't know why, but Sequelize told me to
})
.then(sum => sum) // return the sum
leads to this sql:
SELECT "orderArticles"."quantity", sum("quantity") AS "sum",
"order"."id" AS "order.id", "order"."taxRate" AS "order.taxRate",
"order"."shippingCosts" AS "order.shippingCosts", "order"."discount"
AS "order.discount", "order"."paid" AS "order.paid",
"order"."dispatched" AS "order.dispatched", "order"."payday" AS
"order.payday", "order"."billNr" AS "order.billNr",
"order"."createdAt" AS "order.createdAt", "order"."updatedAt" AS
"order.updatedAt", "order"."orderCustomerId" AS
"order.orderCustomerId", "order"."billCustomerId" AS
"order.billCustomerId" FROM "orderArticles" AS "orderArticles" INNER
JOIN "orders" AS "order" ON "orderArticles"."orderId" = "order"."id"
AND "order"."paid" IS NOT NULL WHERE "orderArticles"."discountTagId" =
'4' GROUP BY "order"."id", "orderArticles"."quantity";
which has this result on the same data base: 0 rows
If you know what I got wrong please let me know!
Thank you :)
Found the solution:
in the scopes definition on the orderArticle model:
scopes: {
paidOrders: {
include: [{
model: order,
where: { paid: {$ne: null}},
attributes: [] // don't select additional colums!
}]
}
},
//...
and the algorithm:
db.models.orderArticles
.scope('paidOrders')
.sum('quantity', {
attributes: [], // don't select any further cols
where: {discountTagId: 2}
})
Note: In my case it was sufficient to return the promise. I use GraphQL which resolves the result and sends it to the client.

Using group by and joins in sequelize

I have two tables on a PostgreSQL database, contracts and payments. One contract has multiple payments done.
I'm having the two following models:
module.exports = function(sequelize, DataTypes) {
var contracts = sequelize.define('contracts', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true
}
}, {
createdAt: false,
updatedAt: false,
classMethods: {
associate: function(models) {
contracts.hasMany(models.payments, {
foreignKey: 'contract_id'
});
}
}
});
return contracts;
};
module.exports = function(sequelize, DataTypes) {
var payments = sequelize.define('payments', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true
},
contract_id: {
type: DataTypes.INTEGER,
},
payment_amount: DataTypes.INTEGER,
}, {
classMethods: {
associate: function(models) {
payments.belongsTo(models.contracts, {
foreignKey: 'contract_id'
});
}
}
});
return payments;
};
I would like to sum all the payments made for every contract, and used this function:
models.contracts.findAll({
attributes: [
'id'
],
include: [
{
model: models.payments,
attributes: [[models.sequelize.fn('sum', models.sequelize.col('payments.payment_amount')), 'total_cost']]
}
],
group: ['contracts.id']
})
But it generates the following query:
SELECT "contracts"."id", "payments"."id" AS "payments.id", sum("payments"."payment_amount") AS "payments.total_cost"
FROM "contracts" AS "contracts"
LEFT OUTER JOIN "payments" AS "payments" ON "contracts"."id" = "payments"."contract_id" GROUP BY "contracts"."id";
I do not ask to select payments.id, because I would have to include it in my aggregation or group by functions, as said in the error I have:
Possibly unhandled SequelizeDatabaseError: error: column "payments.id"
must appear in the GROUP BY clause or be used in an aggregate function
Am I missing something here ? I'm following this answer but even there I don't understand how the SQL request can be valid.
This issue has been fixed on Sequelize 3.0.1, the primary key of the included models must be excluded with
attributes: []
and the aggregation must be done on the main model (infos in this github issue).
Thus for my use case, the code is the following
models.contracts.findAll({
attributes: ['id', [models.sequelize.fn('sum', models.sequelize.col('payments.payment_amount')), 'total_cost']],
include: [
{
model: models.payments,
attributes: []
}
],
group: ['contracts.id']
})
Try
group: ['contracts.id', 'payments.id']
Can you write your function as
models.contracts.findAll({
attributes: [
'models.contracts.id'
],
include: [
{
model: models.payments,
attributes: [[models.sequelize.fn('sum', models.sequelize.col('payments.payment_amount')), 'total_cost']]
}
],
group: ['contracts.id']
})
Is the issue that you might want to be selecting from payments and joining contracts rather than the other way around?