How does nested SELECT and NOT IN work in knex - postgresql

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 }),
);

Related

Fetching distinct rows from multiple includes with sequelize

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']],
...
})

Query using query builder to make an inner join and select with condition in typeORM

I am new to query builder. I need to apply a query. I did it on mysql and it's working fine. But I couldn't make where condition in the query.
This is my SQL query:
select users_roles.user_id
from users_roles
inner join roles on roles.id = users_roles.role_id
where roles.role = 'ADMIN_ROLE'
or roles.role = 'SUPER_ADMIN_ROLE'
I tried to do this using query builder and did this without using any where condition. How can I filter according to that mysql code? Here I am adding my query builder query:
const query = this.userRoleRepository.createQueryBuilder('usersRoles');
const users = await query
.innerJoinAndSelect('usersRoles.user', 'user')
.getMany();
Actually it's returning whole user list. But I need only specified two type of user. That should be filtered with where condition. which is in mysql query.
const users = await query
.select(userRoles.user_id)
.innerJoin('userRoles.roles', 'roles', 'roles.id = users_roles.role_id')
.where("roles.role = :adminRole OR roles.role =: superRole", { adminRole: 'ADMIN_ROLE', superRole: 'SUPER_ADMIN_ROLE' })
.getMany();
I hope this works.

how use findAll() method in sequelize to get table data from postgresql?

I have used postgres for database and sequelize for ORM in nodejs, but when I tried to get all data from table using findAll() method in sequelize it gets undefined, and select query executes but at the place of data it shows undefined, tell me what i do wrong here?
const Data=require('./Data')
var Sequelize=require('sequelize')
const connection = new Sequelize('mydb', 'postgres', '12345', {
host: 'localhost',
dialect: 'postgres'
});
var Emp=connection.define('emp',{
fullName: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
})
connection.sync().then(function(){
Emp.findAll().then(function(emps){
console.log(emps.dataValues)
})
})
output
Executing (default): CREATE TABLE IF NOT EXISTS "emps" ("id" SERIAL , "fullName" VARCHAR(255), "email" VARCHAR(255), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, PRIMARY KEY ("id"));
Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'emps' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;
Executing (default): SELECT "id", "fullName", "email", "createdAt", "updatedAt" FROM "emps" AS "emp";
undefined
I have tried to replicate the code with the information you have provided with.
But it seems everything is working properly on my side here.
I do not know if you have set sequelize configuration options database, or how you have structure and organized the project itself.
Maybe this would help: instantiate sequelize, plese look under for "Instantiate sequelize with name of database, username and password.", the params and attributes will help to get an idea of it.
Anyways, I also took the liberty to make an basic code on the information you have shared us with: sequelize how use findall().
Hope these can help you to go thru with it. Have a great one!
I have solved my problem , just i did simple mistake and i solved it now and data is fetched from database table by using findAll() function in sequelize...
**code before changes:**
connection.sync().then(function(){
Emp.findAll().then(function(emps){
console.log(emps.dataValues) //line with mistake
})
})
**code after changes:**
connection.sync().then(function(){
Emp.findAll().then(function(emps){
console.log(emps) //here i did changes
})
})

Knex Raw Select Postgresql with variable

Can I use a variable in a knex query? And what is wrong with db.raw(select usr_vote.vote where usr_vote.user.id = ${loggedInUserId})? Everything else works fine.
In the db.raw that is nowt working, I'm trying to use a variable (loggedInUserId) to get the logged-in users' vote history for the Question (they can upvote / downvote so the value is either -1 or 1 or null). Thanks in advance for any help!
There are 4 tables that look like:
askify_users
id
user_name
askify_questions
id
title
body
tags
date_created
user_id (FK references askify_users.id)
askify_answers
id
answer
question_id (FK references askify_question.id)
user_id (FK references askify_users.id)
askify_question_votes
List item
question_id (FK references askify_questions.id)
user_id (FK references askify_users.id)
vote (-1 or 1)
PRIMARY KEY (question_id, user_id)
getAllQuestions(db, loggedInUserId) {
return db
.from('askify_questions AS q')
.select(
'q.id AS question_id',
'q.title AS question_title',
'q.body AS question_body',
'q.date_created AS date_created',
'q.tags',
db.raw(
`count(DISTINCT ans) AS number_of_answers`
),
db.raw(
`SUM(DISTINCT usr_vote.vote) AS sum_of_votes`
),
db.raw(
`select usr_vote.vote where usr_vote.user_id = ${loggedInUserId}`
),
db.raw(
`json_strip_nulls(
json_build_object(
'user_id', usr.id,
'user_name', usr.user_name,
'full_name', usr.full_name,
'date_created', usr.date_created
)
) AS "user"`
)
)
.leftJoin(
'askify_answers AS ans',
'q.id',
'ans.question_id'
)
.leftJoin(
'askify_users AS usr',
'q.user_id',
'usr.id'
)
.leftJoin(
'askify_question_vote AS usr_vote',
'q.id',
'usr_vote.question_id'
)
.groupBy('q.id', 'usr.id')
},
The query should look as follows. Everything except for 'user_vote_history' is working.
serializeQuestion(question) {
const { user } = question
return {
id: question.question_id,
question_title: xss(question.question_title),
question_body: xss(question.question_body),
date_created: new Date(question.date_created),
number_of_answers: Number(question.number_of_answers) || 0,
user_vote_history: question.user_vote_history,
sum_of_votes: Number(question.sum_of_votes),
tags: xss(question.tags),
user: {
user_id: user.user_id,
user_name: user.user_name,
full_name: user.full_name,
user_vote: user.user_vote,
date_created: new Date(user.date_created)
},
}
},
I note that #felixmosh is correct here regarding bound values, but just to elaborate: the key here is when the string substitution takes place. If you do this:
db.raw(`SELECT vote WHERE user_id = ${loggedInUserId}`)
the substitution takes place in JavaScript, as soon as the JS interpreter reaches this line. The database engine has nothing to do with whatever is in loggedInUserId and neither does Knex: you're essentially bypassing all the built-in protections.
Slightly better is:
db.raw("SELECT vote WHERE user_id = ?", loggedInUserId)
This allows Knex to escape the string in loggedInUserId. If you prefer, you can use a named binding:
db.raw("SELECT vote WHERE user_id = :loggedInUserId", { loggedInUserId })
However, all of this mucking about with bindings is easily avoided by using the facility Knex already provides for subqueries: just put the subquery in a function.
db
.from("askify_questions AS q")
.select(
"q.id AS question_id",
qb => qb.select("usr_vote.vote").where({ user_id: loggedInUserId })
)
.leftJoin(
"askify_question_vote AS usr_vote",
"q.id",
"usr_vote.question_id"
);
The qb parameter stands for "query builder", and is passed by Knex to your function. It behaves very much like your db object.
This generates SQL akin to:
SELECT
"q"."id" AS "question_id",
(
SELECT "usr_vote"."user_id" WHERE "user_id" = ?
)
FROM "askify_questions AS q"
LEFT JOIN "askify_question_vote" AS "usr_vote"
ON "q"."id" = "usr_vote"."question_id"

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);
});