NodeJS Sequelize and FindAll with Include and constraints - node.js

I am trying to use the Include capabilities in the Find all to Eager load the data i need in a single query. My issue is that all the includes are LEFT OUTER JOIN ... which is good but i am not seeing a way to add additional constraints to that LEFT OUTER JOIN.
My query would look something like:
Matches.findAll(
{where: ["match.isPublished = ?", true],
include: [RoundMaps,
Locations,
{model: MemberMaps, as: "MemberMaps", where: ["memberMaps.memberUUID = ?", authenticatedUser.uuid]}]})
But this does not seem to be supported ... at least the way i have the syntax written. I cannot add that constraint to the constraints on the LEFT OUTER JOIN ... adding the same constraint to the WHERE clause does not work ... as that gives me only the Matches that the Member is associated with. I want the list of all public Matches and to know which of those that the member has already established an association.
Can it be done?
Sequelize seems to be getting most things done! But still struggling with some things.
Insights welcome!
Cheers

According to your question this is not possible using where clause with LEFT OUTER JOIN.
For this you have to write SQL queries to list of all public Matches and to know which of those that the member has already established an association.
sequelize.query('SELECT * FROM "Matches" LEFT OUTER JOIN "RoundMaps" ON "Matches".id = "RoundMaps".match_id LEFT OUTER JOIN "Locations" ON "Matches".id = "Locations".match_id LEFT OUTER JOIN (SELECT * FROM "MemberMaps" WHERE "MemberMaps".memberUUID = ?) AS "MemberMaps" ON "Matches".id = "MemberMaps".match_id WHERE "Matches".isPublished = ?', null, { raw: true }, [memberUUID,true ]).success(function(myTableRows) {
console.log(myTableRows);
res.send(myTableRows);
});
Executing raw SQL queries in Sequelize

There is an option on INCLUDE that specifies whether the join will be inner or outer: include.require = boolean
When there isn't a where: clause specified, then it defaults to false. But, it gets set to true when a where: is set.
You can explicitly turn that off to fetch all of the results of the findAll().
Unfortunately, the only examples I've seen of this are in the codebase itself, but here it is:
sequelize/test/integration/associations/belongs-to-many.test.js#L167

Related

How to add AND condition when use inner join on Sequelize

I have some sql code like this
select *
from people_work_attendances pwa
inner join people_work_placements pwp on pwa.user_work_id = pwp.id
inner join company_job_profile_r_attendance cjpa on pwp.job_profile = cjpa.profile and pwa.policy_id=cjpa.policy
WHERE
pwa.user_work_id = 71072
and cjpa.profile IS NOT NULL
i want to implement this section to Sequelize
inner join company_job_profile_r_attendance cjpa on pwp.job_profile = cjpa.profile and pwa.policy_id=cjpa.policy
current situation is i can make it like this
if(!module.exports.Placement.associations.JobProfileAttendance){
module.exports.Placement.hasOne(module.exports.JobProfileAttendance, {
as:'JobProfileAttendance',
sourceKey:'jobProfile',
foreignKey:'profile'
})
}
but the result of this part is like this
INNER JOIN "public"."company_job_profile_r_attendance" AS "Placement->JobProfileAttendance" ON "Placement"."job_profile" = "Placement->JobProfileAttendance"."profile"
i want to make like this
INNER JOIN "public"."company_job_profile_r_attendance" AS "Placement->JobProfileAttendance"
ON "Placement"."job_profile" = "Placement->JobProfileAttendance"."profile"
AND "PeopleWorkAttendance"."policy_id" = "Placement->JobProfileAttendance"."policy"
want to add some AND conditions but in Sequelize
AND "PeopleWorkAttendance"."policy_id" = "Placement->JobProfileAttendance"."policy"
You can use on option in include, just keep in mind that you always should do it manually because Sequelize doesn't support composite primary and foreign keys.
See options.include[].on in findAll options

TypeORM count grouping with different left joins each time

I am using NestJS with TypeORM and PostgreSQL. I have a queryBuilder which joins other tables based on the provided array of relations.
const query = this.createQueryBuilder('user');
if (relations.includes('relation1') {
query.leftJoinAndSelect('user.relation1', 'r1');
}
if (relations.includes('relation2') {
query.leftJoinAndSelect('user.relation2', 'r2');
}
if (relations.includes('relation3') {
query.leftJoinAndSelect('user.relation3', 'r3');
}
// 6 more relations
Following that I select a count on another table.
query
.leftJoin('user.relation4', 'r4')
.addSelect('COUNT(case when r4.value > 10 then r4.id end', 'user_moreThan')
.addSelect('COUNT(case when r4.value < 10 then r4.id end', 'user_lessThan')
.groupBy('user.id, r1.id, r2.id, r3.id ...')
And lastly I use one of the counts (depending on the request) for ordering the result with orderBy.
Now, of course, based on the relations parameter, the requirements for the groupBy query change. If I join all tables, TypeORM expects all of them to be present in groupBy.
I initially had the count query separated, but that was before I wanted to use the result for ordering.
Right now I planned to just dynamically create the groupBy string, but this approach somehow feels wrong and I am wondering if it is in fact the way to go or if there is a better approach to achieving what I want.
You can add group by clause conditionally -
if (relations.includes('relation1') {
query.addGroupBy('r1.id');
}

Couchbase N1QL query with joined subquery with USE KEYS

I need to write a n1ql query which demands another sub-query in select clause. As it is mandatory to use 'USE KEYS' while writing subqueries in n1ql. How to write USE KEYS clause for an inner joined query, below is an example of same case:
select meta(m).id as _ID, meta(m).cas as _CAS,
(select c.description
from bucketName p join bucketName c on p.categoryId = c.categoryId and p.type='product' and
c.type='category' and p.masterId=m.masterId ) as description //--How to use USE KEYS here ?
from bucketName m where m.type='master' and m.caseId='12345'
My requirment is to fetch some value from another 2 joined tables. however, I simplified above query to make it more understandable.
Please suggest the correct way to implement.
Also, is writting
sub-queries in n1ql is better than fetching documents seperatly and
merging them in coding?
Non FROM CLAUSE, correlated sub queries requires USE KEYS due to global secondary indexes queries can take long time and resources. This is restriction at present in the N1QL. If you can derive p's document key from the m you can give that as USE KEYS in p.
Otherwise you have two options
Option 1: As your subquery is in the projection Use ANSI JOIN https://blog.couchbase.com/ansi-join-support-n1ql/
SELECT META(m).id AS _ID, META(m).cas AS _CAS, c.description
FROM bucketName AS m
LEFT JOIN bucketName AS p ON p.masterId=m.masterId AND p.type='product'
LEFT JOIN bucketName AS c ON c.type='category' AND p.categoryId = c.categoryId
WHERE m.type='master' AND m.caseId='12345';
CREATE INDEX ix1 ON (caseId) WHERE type='master';
CREATE INDEX ix2 ON (masterId, categoryId) WHERE type='product';
CREATE INDEX ix3 ON (categoryId, description) WHERE type='category';
NOTE: If there is no Unique relation m to p to c JOIN can produce more results.
If that is case, you can do GROUP BY META(m).id, META(m).cas and
ARRAY_AGG(c.description). All descriptions are given as ARRAY.
Option 2:
As described by you issue two separate quires and merge in the application.

Postgres SQL Joins for Many To Many Relationship

right now I am "learning" Postgres SQL. I have 3 tables:
1) User: userId
2) Stack :stackId
3) User_Stack: userId, stackId
Now I want to fetch all stacks belonging to one user, given the userId. I understand I need to use Joins, but thats were I get stuck... I try it like this:
SELECT * FROM "Stack" LEFT OUTER JOIN "User_Stack" ON ('User_Stack.stackId' = 'Stack.stackId') WHERE "userId" = '590855';
Error: The returned data is empty.
PS: Is there any GUI Query builder out there ? Or do you have any other tips how to systematically create queries ?
EDIT: If I change the query to this:
SELECT * FROM "Stack" INNER JOIN "User_Stack" ON (User_Stack.stackId = Stack.stackId) WHERE "userId" = '590855';
I get the following error:
Kernel error: ERROR: missing FROM-clause entry for table "user_stack"
LINE 1: SELECT * FROM "Stack" INNER JOIN "User_Stack" ON (User_Stack...
Your main error is in the join. If you do 'something' = 'other' you're comparing string literals, not getting anything from the database. So this will always return false. You will want to compare table1.field1 = table2.field2
Another thing is the LEFT OUTER JOIN. I'm pretty sure you want an INNER JOIN since you want only fields that exist in the other table.
Also don't use double quotes for fields and table names since then the database will require case sensitivity and usually it's not good to have case sensitive names. You can use them with lowercase names if you need and always create them in lowercase.
Numbers also don't need to be quoted, it will just cause more processing when the system has to convert them from text to numbers.

JPQL / QueryDSL: join subquery and get aliased column

I'm trying to get an average for a count on a groupBy by joining with a subquery. Don't know if that the right way to go at all but I couldn't anything about subqueries other than the mysema doc.
Scenario:
How many orders per product did a customer do on average?
Meaning: A Customer orders products. So a customer ordered a specific product a number of times (count). What's the average number of orders that customer placed for any product?
Might sound a bit hypothetical, in fact it's just part of a prototype, but it made me wonder, how to get a reference to a custom column created within a subquery with the fancy QueryDSL from Mysema.
In SQL you just give the count column an alias and join using a second ID column. QueryDSL has the "as()" method as well but I have no Idea, how to retrieve that column plus I dont't see how it can join one query with anothers, since query.list() just gets a list but for some reason the join accepts it. Feels wrong...
Here's my code:
JPQLQuery query = createJPQLQuery();
QOrdering qOrdering = QOrdering.ordering;
QProduct qProduct = QProduct.product;
QCustomer qCustomer = QCustomer.customer;
// how many of each product did a customer order?
HibernateSubQuery subQuery = new HibernateSubQuery();
subQuery.from(qOrdering).innerJoin(qOrdering.product,qProduct).innerJoin(qOrdering.customer, qCustomer);
subQuery.groupBy(qCustomer,qProduct).list(qCustomer.id,qProduct.id,qProduct.count());
// get the average number of orders per product for each customer
query.from(qCustomer);
query.innerJoin(subQuery.list(qCustomer.id,qOrdering.count().as("count_orders")));
query.groupBy(qCustomer.id);
return (List<Object[]>) query.list(qCustomer.firstname,subQuery.count_orders.avg());
Again: How do I join with a subquery?
How do I get the aliased "count" column to do more aggregation like avg (is my group right btw?)
Might be that I have some other errors in this, so any help appreciated!
Thanks!
Edit:
That's kind of the native SQL I'd like to see QueryDSL produce:
Select avg(numOrders) as average, cust.lastname from
customer cust
inner join
(select count(o.product_id) as numOrders, c.id as cid, p.name
from ordering o
inner join product p on o.product_id=p.id
inner join customer c on o.customer_id=c.id
group by o.customer_id, o.product_id) as numprods
on cust.id = numprods.cid
group by numprods.cid
order by cust.lastname;
Using subqueries in the join clause is not allowed. in JPQL, subqueries are only allowed in the WHERE and HAVING part. The join method signatures in Querydsl JPA queries are too wide.
As this query needs two levels of grouping, maybe it can't be expressed with JPQL / Querydsl JPA.
I'd suggest to write this query using the Querydsl JPA Native query support.
As Querydsl JPA uses JPQL internally, it is restricted by the expressiveness of JPQL.
I know that this question is old and already has an accepted answer, but judging from this question, it seems to still be troubling guys. See my answer in the same question. The use of JoinFlag in the join() section and Expression.path() is able to achieve left-joining a subquery. Hope this helps someone.
QueryDsl does not support subQuery in join but you can achieve this via following way:
We wanted to achieve the following query:
select A.* from A join (select aid from B group by aid) b on b.aid=A.id;
Map a View or SQL query to JPA entity:
import lombok.Setter;
import org.hibernate.annotations.Subselect;
import org.hibernate.annotations.Synchronize;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
#Getter
#Setter
#Subselect("select aid from B group by aid")
#Synchronize("B")
public class BGroupByAid {
#Id
private Integer aId;
}
then use the equivalent QueryDSl entity in the class just like the regular entity:
JPAQuery<QAsset> query = new JPAQuery<>(entityManager);
QBGroupByAid bGroupById = QBGroupByAid.bGroupByAid;
List<A> tupleOfAssets =
query.select(A)
.from(A).innerJoin(bGroupById).on(bGroupById.aId.eq(A.aId))
.fetchResults()
.getResults();
You can also use blazebit which supports also subquery in join. I have try it and it is working. You can create SubQueryExpression f.e like this
SubQueryExpression<Tuple> sp2 = getQueryFactory().select(entity.id,
JPQLNextExpressions.rowNumber().over().partitionBy(entity.folId).orderBy(entity.creationDate.desc()).as(rowNumber))
.from(entity)
.where(Expressions.path(Integer.class, rowNumber).eq(1));
and then just join it like this:
return getBlazeQueryFactory()
.select(entity1, entity)
.from(entity1)
.leftJoin(sp2, entity).on(entity.id.eq(entity1.id)).fetch();
I have put here just simple example. So maybe it doesn't make a perfect sense but maybe can be helpful.
Also don't be confused it will can produce union in the generated select. This is just for naming columns from subquery you can read better explanation about this union here: Blaze-Persistence GROUP BY in LEFT JOIN SUBQUERY with COALESCE in root query

Resources