Fetching distinct rows from multiple includes with sequelize - postgresql

I have some nested joins below. I am trying to fetch distinct rows out of it.
Product.findAll({
attributes:[sequelize.fn('DISTINCT', sequelize.col('product.id')), 'product.id']],
include: {
model: Course,
attributes: [],
required: true,
include: {
model: Class,
where: {
classId: args.classId
},
attributes: []
}
}
})
syntax error at or near "DISTINCT"
Query being produced by sequelize,
SELECT "product"."id", DISTINCT("product"."id") AS "product.id" FROM "my_schema"."product_groups" AS "product" INNER JOIN "my_schema"."courses" AS "courses" ON "product"."id" = "courses"."product_group_id" INNER JOIN "my_schema"."classes" AS "courses->classes" ON "courses"."id" = "courses->classes"."course_id" AND "courses->classes"."organization_id" = '68700940-f509-4662-975f-a3ba3382aa9b';;
Working sql query,
SELECT DISTINCT("product"."id") FROM "my_schema"."product_groups" AS "product" INNER JOIN "my_schema"."courses" AS "courses" ON "product"."id" = "courses"."product_group_id" INNER JOIN "my_schema"."classes" AS "courses->classes" ON "courses"."id" = "courses->classes"."course_id" AND "courses->classes"."organization_id" = '68700940-f509-4662-975f-a3ba3382aa9b';
How can I product above query with sequelize to return distinct rows. I also tried distinct: true but it doesn't make any change. With this option query works and returns result but no distinct is there in the query generated.

The DISTINCT syntax in Postgres should look like this.
SELECT DISTINCT column AS alias FROM table;
DISTINCT is a clause and not function so you need to use literal.
Product.findAll({
attributes:[[sequelize.literal('DISTINCT "product"."id"'), 'product.id']],
...
})

Related

Knex, Objection.js, How to sort by number of associations

I have an Express API using Postgres via Knex and Objection.
I want to set up a Model method or scope that returns an array of Parent model instances in order of number of associated Children.
I have looked through the Knex an Objection docs
I have seen the following SQL query that will fit, but trying to figure out how to do this in Knex:
SELECT SUM(O.TotalPrice), C.FirstName, C.LastName
FROM [Order] O JOIN Customer C
ON O.CustomerId = C.Id
GROUP BY C.FirstName, C.LastName
ORDER BY SUM(O.TotalPrice) DESC
This is how it should be done with knex (https://runkit.com/embed/rj4e0eo1d27f):
function sum(colName) {
return knex.raw('SUM(??)', [colName]);
}
knex('Order as O')
.select(sum('O.TotalPrice'), 'C.FirstName', 'C.LastName')
.join('Customer C', 'O.CustomerId', 'C.Id')
.groupBy('C.FirstName', 'C.LastName')
.orderBy(sum('O.TotalPrice'), 'desc')
// Outputs:
// select SUM("O"."TotalPrice"), "C"."FirstName", "C"."LastName"
// from "Order" as "O"
// inner join "Customer C" on "O"."CustomerId" = "C"."Id"
// group by "C"."FirstName", "C"."LastName"
// order by SUM("O"."TotalPrice") desc
But if you are really using objection.js then you should setup models for your Order and Customer tables and do something like this:
await Order.query()
.select(sum('TotalPrice'), 'c.FirstName', 'c.LastName')
.joinRelated('customer as c')
.groupBy('c.FirstName', 'c.LastName')
.orderBy(sum('TotalPrice'), 'desc')
In case anyone is interested, I just included the raw SQL query as follows:
router.get('/leaderboard', async (req, res) => {
let results = await knex.raw('SELECT users_id, username, COUNT(acts.id) FROM acts INNER JOIN users ON acts.users_id = users.id GROUP BY users_id, username ORDER BY COUNT(acts.id) DESC');
console.log(results);
res.json(results.rows);
});

How does nested SELECT and NOT IN work in knex

I'm trying to write this query in knex and am stuck.
I currently have 3 tables (group_tasks, completed_group_tasks and group_users)
I want to filter out the results when grouptask_id from completed_group_tasks is the same as the innerjoin.
SELECT * FROM group_tasks INNER JOIN group_users ON group_users.group_id = group_tasks.group_id WHERE user_id = 2 AND grouptask_id NOT IN (SELECT grouptask_id FROM completed_group_tasks WHERE user_id = 2)
I tried writing this:
db.from('group_users')
.select('groups.group_id', 'group_name')
.innerJoin('groups', 'group_users.group_id', 'groups.group_id')
.where({user_id: user_id})
.whereNotIn('grouptask_id', function() {
this.from('completed_group_tasks')
.select('grouptask_id')
.where({user_id: user_id})
})
This fails stating column "grouptask_id" does not exist
Subquery in Knex needs to be a separate Knex builder.
db.from('group_users')
.select('groups.group_id', 'group_name')
.innerJoin('groups', 'group_users.group_id', 'groups.group_id')
.where({ user_id: user_id })
.whereNotIn(
'grouptask_id',
db
.from('completed_group_tasks')
.select('grouptask_id')
.where({ user_id: user_id }),
);

Jsonb object parsing in PostgreSql

How to parse jsonb object in PostgreSql. The problem is - object every time is different by structure inside. Just like below.
{
"1":{
"1":{
"level":2,
"nodeType":2,
"id":2,
"parentNode":1,
"attribute_id":363698007,
"attribute_text":"Finding site",
"concept_id":386108004,
"description_text":"Heart tissue",
"hierarchy_id":0,
"description_id":-1,
"deeperCnt":0,
"default":false
},
"level":1,
"nodeType":1,
"id":1,
"parentNode":0,
"concept_id":22253000,
"description_id":37361011,
"description_text":"Pain",
"hierarchy_id":404684003,
"deeperCnt":1,
"default":false
},
"2":{
"1":{
"attribute_id":"363698007",
"attribute_text":"Finding site (attribute)",
"value_id":"321667001",
"value_text":"Respiratory tract structure (body structure)",
"default":true
},
"level":1,
"nodeType":1,
"id":3,
"parentNode":0,
"concept_id":11833005,
"description_id":20419011,
"description_text":"Dry cough",
"hierarchy_id":404684003,
"deeperCnt":1,
"default":false
},
"level":0,
"recAddedLevel":1,
"recAddedId":3,
"nodeType":0,
"multiple":false,
"currNodeId":3,
"id":0,
"lookForAttributes":false,
"deeperCnt":2,
}
So how should I parse all object and for example look if object inside has "attribute_id" = 363698007?
In this case we should get 'true' while selecting data rows in PostgreSql with WHERE statement.
2 question - what index should I use for jsonb column to get wanted results?
Already tried to create btree and gin indexes but even simple select returns 'null' with sql like this:
SELECT object::jsonb -> 'id' AS id
FROM table;
if I use this:
SELECT object
FROM table;
returns firstly described object.
The quick and dirty way (extended upon Collect Recursive JSON Keys In Postgres):
WITH RECURSIVE doc_key_and_value_recursive(id, key, value) AS (
SELECT
my_json.id,
t.key,
t.value
FROM my_json, jsonb_each(my_json.data) AS t
UNION ALL
SELECT
doc_key_and_value_recursive.id,
t.key,
t.value
FROM doc_key_and_value_recursive,
jsonb_each(CASE
WHEN jsonb_typeof(doc_key_and_value_recursive.value) <> 'object' THEN '{}'::jsonb
ELSE doc_key_and_value_recursive.value
END) AS t
)
SELECT t.id, t.data->'id' AS id
FROM doc_key_and_value_recursive AS c
INNER JOIN my_json AS t ON (t.id = c.id)
WHERE
jsonb_typeof(c.value) <> 'object'
AND c.key = 'attribute_id'
AND c.value = '363698007'::jsonb;
Online example: https://dbfiddle.uk/?rdbms=postgres_11&fiddle=57b7c4e817b2dd6580bbf28cbac10981
This may be improved a lot by stopping the recursion as soon as the relevant key and value are found, reverse sort and limit 1, aso. But it does the basic thing generically.
It also shows that jsonb->'id' does work as expected.

How can I execute this Postgres query in Sequelize?

I need to translate this join query into Sequelize. I have associated the three tables involved in the join:
tracks.belongsToMany(tracks_playlists, { foreignKey: 'track_id' });
tracks.belongsToMany(tracks_meta, { foreignKey: 'track_id' });
tracks_meta.belongsToMany(tracks_playlists, { foreignKey: 'track_id'});
Here's the raw query that works:
SELECT tracks_playlists.playlist_id, tracks_playlists.track_id, tracks.track, tracks.track_type, tracks_meta.status, tracks_meta.app_id, tracks_meta.description, tracks_meta.ip
FROM tracks_playlists
INNER JOIN tracks on tracks_playlists.track_id = tracks.track_id
INNER JOIN tracks_meta on tracks.track_id = tracks_meta.track_id
WHERE tracks_playlists.playlist_id = 4;
When I try to run the following code
self.tracks_playlists_table.find({
where: {
playlist_id: 4
},
include: [{
model: self.tracks_table
}]
})
.then(function(resp) {
console.log(resp);
});
it spits out this error:
relation "playlists.trackstracks_playlists" does not exist
This is the query that it generates from my code:
SELECT "TracksPlaylists".*, "Tracks"."track_id" AS "Tracks.track_id",
"Tracks"."track" AS "Tracks.track", "Tracks"."track_type" AS "Tracks.track_type", "Tracks"."created" AS "Tracks.created", "Tracks"."updated" AS "Tracks.updated", "Tracks.trackstracks_playlists"."created" AS "Tracks.trackstracks_playlists.created", "Tracks.trackstracks_playlists"."updated" AS "Tracks.trackstracks_playlists.updated", "Tracks.trackstracks_playlists"."track_id" AS "Tracks.trackstracks_playlists.track_id"
FROM (SELECT "TracksPlaylists"."track_playlist_id", "TracksPlaylists"."track_id", "TracksPlaylists"."playlist_id", "TracksPlaylists"."created", "TracksPlaylists"."updated"
FROM "playlists"."tracks_playlists" AS "TracksPlaylists"
WHERE "TracksPlaylists"."playlist_id" = 4 LIMIT 1) AS "TracksPlaylists"
LEFT OUTER JOIN ("playlists"."trackstracks_playlists" AS "Tracks.trackstracks_playlists"
INNER JOIN "playlists"."tracks" AS "Tracks"
ON "Tracks"."track_id" = "Tracks.trackstracks_playlists"."track_id")
ON "TracksPlaylists"."track_playlist_id" = "Tracks.trackstracks_playlists"."track_id";
Why is it creating the column "trackstracks_playlists"?

joining a table with more than one field in same table

I try to create a join query in Linq. I want to join a table more than one field with same
table. Please see my code below.
var roles = (from ords in _orderRepository.Table
join customers in _customerRepository.Table on ords.CustomerId equals customers.Id
join ordprvrnts in _orderProductVariantRepository.Table on ords.Id equals ordprvrnts.OrderId
join prdvrnts in _productVariantRepository .Table on ordprvrnts.ProductVariantId equals prdvrnts.Id
**join cstevntrle in _customerEventRoleRepository.Table on
new{ customers.Id equals cstevntrle.CustomerId } &&
new { cstevntrle.EventId == model.Event}**
orderby customers.Email ascending
select new CustomerEventRolesModel
{
Customer = customers.Email,
CUstomerId =customers.Id
});
I try to filter customerEventRoleRepository.Table with CustomerId and EventId
how can i do this in this join query.
Please Help.
you have boolean comparisons in your anonymous type definitions...
change your on clause to the following:
join cstevntrle in _customerEventRoleRepository.Table on
new { CustomerId = customers.Id, EventId = model.Event.EventId } equals
new { CustomerId = cstevntrle.CustomerId, EventId = cstevntrle.EventId }
I don't see "model" defined anywhere, so I'm not sure this is going to work, but it should be enough to demonstrate how joins based on multiple fields works - each anonymous class contains the fields from one "side" of the join.