WhereHas method in Sequelize - orm

Sequelize reminds me Eloquent library in Laravel, but I can't find anything similar to method WhereHas that allows filtering records using associated tables.
Say, I need to select all users in the group with ID = 1, then I can use the query below, but then I have to extract users from results.
let group = group.findOne({
include: [{
as: 'users',
model: schema.users
}],
where: {
id: groupId
}
})
let users = group.users;
Is there a method like WhereHas that I could use to create a query for users only and then filter them using association with groups?
let users = group.findAll({
whereHas: [{
as: 'groups',
model: schema.groups,
where: {
id: groupId // something like this, group is used for filtering only
}
}]
})

I had the same problem and my solution is required:true in include option. If you want to find all users that join a group, you can use code below:
let users = users.findAll({
include: [{
as: 'groups',
model: schema.groups,
required:true,
where: {
id: groupId
}
}],
})

Related

Hasura: How to query counts of records from multiple related tables

So I have create this query for my GraphiQL in Hasura, This will get all of the users data and it aims to include the total amount/count of the other data from analytics and follow table where user's id is equal to user_id from the analytics and follow table
query getAllUsersData {
users {
id
username
bio
first_name
last_name
banner
profile_image_url
created_at
followers: follow_aggregate(where: {user_id: {_eq: id}, is_follow: {_eq: 1}}) {
aggregate {
count: count
}
}
visits: analytics_aggregate(where: {user_id: {_eq: id}, type: {_eq: "visit"}}) {
aggregate {
count: count
}
}
shares: analytics_aggregate(where: {user_id: {_eq: id}, type: {_eq: "share"}}) {
aggregate {
count: count
}
}
}
}
The problem here is I wont be able to run it because of the follow_aggregate and analytics_aggregate, It gives me an error of Cannot query field "follow_aggragate" and "analytics_aggregate" on type "users".
I am trying this thru Hasura's project dashboard, I have check already the documentations of this from Hasura and I am still really lacking. Hoping someone would guide me into this, Would really appreciate thank you in advance.
I have already done my research, Look for steps of aggregation and also look for other methods.
What I am trying to output is:
{
users:
{
userdatahere..
followersinthere,
visitsinthere,
sharesinthere
},
{
userdatahere..
followersinthere,
visitsinthere,
sharesinthere
},
}
Or let me give you a quick structure but SQL:
SELECT u.id, u.username, u.bio, u.first_name, u.last_name, u.banner, u.profile_image_url, u.created_at,
COUNT(f.user_id) AS followers,
SUM(CASE WHEN a.type = 'visit' THEN 1 ELSE 0 END) AS visits,
SUM(CASE WHEN a.type = 'share' THEN 1 ELSE 0 END) AS shares
FROM user u
LEFT JOIN follow f ON u.id = f.user_id
LEFT JOIN analytics a ON u.id = a.user_id
GROUP BY u.id
Update 1/8/2023:
I have also tried this method (which is array relationship):
Database Table Users relationship to other tables
Here's the column of the users table
And this is the output users table + links table
The only problem now is how can I implement it with schema graphiql
Answer: Preview Output
I setup foreign keys
Tracked the Untracked foreign-key relationships
and it created array of relationship
The I use my schema again but from follow to follows
query getAllUsersData {
users {
id
username
bio
first_name
last_name
banner
profile_image_url
created_at
followers: follows_aggregate(where: {is_follow: {_eq: 1}}) {
aggregate {
count: count
}
}
visits: analytics_aggregate(where: {type: {_eq: "visit"}}) {
aggregate {
count: count
}
}
shares: analytics_aggregate(where: {type: {_eq: "share"}}) {
aggregate {
count: count
}
}
}
}
Thanks to spatialaustin
Are you sure that the follow and analytics relationships are properly configured in your DB and tracked by Hasura? You can validate by looking at the "relationships" tab of the users table.
You also need to configure Hasura's permissions to allow aggregate queries (docs).
If you're using Hasura Cloud, you might want to double-check that you're allowing queries of a depth of at least 2 (docs).
The easiest way to check all of this is to build your query in the Hasura console's GraphIQL editor, which will show you which entities are available in your queries.
Lastly, if your relationships are indeed configured correctly, you should not need to include a foreign key in your query's where clauses—this is implied through the query structure.
query getAllUsersData {
users {
id
username
bio
first_name
last_name
banner
profile_image_url
created_at
followers: follow_aggregate {
aggregate {
count: count
}
}
visits: analytics_aggregate(where: { type: { _eq: "visit" } }) {
aggregate {
count: count
}
}
shares: analytics_aggregate(where: { type: { _eq: "share" } }) {
aggregate {
count: count
}
}
}
}
I hope that helps!

Loopback 4 - How to find the highest value in column (Like SELECT MAX(column) from Table)?

I want to find the highest value in a specific column of a specific table. It should be very simple.
this is the documentation of LB4 https://loopback.io/doc/en/lb2/Where-filter But I didn't find it there.
We did this through a custom repository method where we execute a select max() query and have a custom controller method (i.e. /next-id) that calls it.
Repository method:
async nextId(): Promise<any> {
return this.dataSource
.execute('select MAX(id)+5 as nextId from route_lookup')
.then(data => {
if (data[0].NEXTID === null) {
data[0].NEXTID = 1005;
}
return data[0].NEXTID;
});
}
Controller method:
#get('/route-lookups/next-id')
#response(200, {
description: 'Next avaialble id for route lookup',
content: {
'application/json': {
schema: {
type: 'number',
},
},
},
})
async nextId(): Promise<number> {
return await this.routeLookupRepository.nextId();
}
Within the Loopback Filter Documentation they do mention a way to achieve this, even though it's not as obvious.
/weapons?filter[where][effectiveRange][gt]=900&filter[limit]=3
Essentially you can do the following:
Identify the column of interest.
Use the gt operator to set a min number
Add order if you wanted to ensure the sorting order is as expected.
Limit the results to 1.
Here is a code example:
Employees.find({
where: {
age: {
gt: 1
}
},
order: 'age ASC',
limit: 1
})
Please let me know if this is what you were going for or if you need some more support.

TypeORM select data from nested relations

Using
await this.budgetRepository.createQueryBuilder("budget")
.leftJoinAndSelect("budget.contact", "contact")
.leftJoinAndSelect("contact.photo", "contactPhoto")
.getMany();
I get a list with objects like this:
Budget {
id: 1,
unnecessary_property1: something,
contact: Contact {
unnecessary_property2: something,
photo: Photo {
unnecessary_property3: something,
url: "url.com"
},
},
}
But I want to select only the necessary properties in the nested objects (relations) and get a list of objects like this:
Budget {
id: 1,
contact: Contact {
photo: Photo {
url: "url.com"
},
},
}
How is that possible with TypeORM?
This is possible but we have to select everything manually with .select()
await this.budgetRepository.createQueryBuilder("budget")
.leftJoinAndSelect("budget.contact", "contact")
.leftJoinAndSelect("contact.photo", "contactPhoto")
.select(['budget.id', 'contactPhoto.url']
.getMany();
If you're using repository pattern that you will be achieve the similar result with:
await this.budgetRepository.find({
relations: ["contact", "contact.photo"]
})
You would have to use the .select() function and pass the given properties you want for each entity.
for your example:
const user = await createQueryBuilder("budget")
.leftJoinAndSelect("budget.contact", "contact")
.leftJoinAndSelect("contact.photo", "contactPhoto")
.select([/* everything from budget */, 'contact.photo.url'....]) // added selection
.getMany();

How do I write this Raw Postgres query in sequelize

I Have this postgres RAW query, I wanted to write it in Sequelize. How do I do this since I have less idea about Writing queries having JOINS in Sequelize. I have made models and Associations.
These are models and associations.
TestParticipant.hasMany(ParticipantHistory, {
sourceKey: "id",
foreignKey: "participantId",
as: "paticipantStatuses"
})
ParticipantHistory.belongsTo(TestParticipant, {
foreignKey: "participantId",
as: "paticipantStatuses"
})
This is the raw Query I wanna transform into Sequelize query
SELECT participant_histories.participant_id,
participant_histories.created_at,participant_histories.previous_status,
participant_histories.status,test_participants.test_type_id,test_participants.id,
test_participants.email,test_participants.scheduled_at,test_participants.valid_till,
test_participants.is_proctored
FROM test_participants
INNER JOIN participant_histories ON test_participants.id=participant_histories.participant_id
WHERE user_id='${userId}'
AND participant_histories.status='${activity}'
AND participant_histories.created_at>='${isoDate}'
Because I don't see model definitions in the post I suggest only something like this:
// First of all you should correct an alias for TestParticipant like this
ParticipantHistory.belongsTo(TestParticipant, {
foreignKey: "participantId",
as: "paticipant"
})
const rows = await ParticipantHistory.findAll({
raw: true,
attributes: ['participant_id', 'created_at', 'previous_status', 'status'],
where: {
status: activity,
created_at: {
[Op.gte]: isoDate
}
},
include: [{
required: true // this turns into INNER JOIN
model: TestParticipant,
attributes: ['test_type_id', 'id', 'email', 'scheduled_at', 'valid_till', 'is_proctored'],
as: 'participant',
where: {
user_id: userId
}
}]
})

Load only the data that's needed from database with Graphql

I'm learning graphql and I think I've spot one flaw in it.
Suppose we have schema like this
type Hero {
name: String
friends: [Person]
}
type Person {
name: String
}
and two queries
{
hero {
name
friends {
name
}
}
}
and this
{
hero {
name
}
}
And a relational database that have two corresponding tables Heros and Persons.
If my understanding is right I can't resolve this queries such that for the first query the resulting sql query would be
select Heros.name, Persons.name
from Heros, Persons
where Hero.name = 'Some' and Persons.heroid = Heros.id
And for the second
select Heros.name, Persons.name from Heros
So that only the fields that are really needed for the query would be loaded from the database.
Am I right about that?
Also if graphql would have ability to return only the data that's needed for the query, not the data that's valid for full schema I think this would be possible, right?
Yes, this is definitely possible and encouraged. However, the gist of it is that GraphQL essentially has no understanding of your storage layer until you explicitly explain how to fetch data. The good news about this is that you can use graphql to optimize queries no matter where the data lives.
If you use javascript, there is a package graphql-fields that can simplify your life in terms of understanding the selection set of a query. It looks something like this.
If you had this query
query GetCityEvents {
getCity(id: "id-for-san-francisco") {
id
name
events {
edges {
node {
id
name
date
sport {
id
name
}
}
}
}
}
}
then a resolver might look like this
import graphqlFields from 'graphql-fields';
function getCityResolver(parent, args, context, info) {
const selectionSet = graphqlFields(info);
/**
selectionSet = {
id: {},
name: {},
events: {
edges: {
node: {
id: {},
name: {},
date: {},
sport: {
id: {},
name: {},
}
}
}
}
}
*/
// .. generate sql from selection set
return db.query(generatedQuery);
}
There are also higher level tools like join monster that might help with this.
Here is a blog post that covers some of these topics in more detail. https://scaphold.io/community/blog/querying-relational-data-with-graphql/
In Scala implementation(Sangria-grahlQL) you can achieve this by following:
Suppose this is the client query:
query BookQuery {
Books(id:123) {
id
title
author {
id
name
}
}
}
And this is your QueryType in Garphql Server.
val BooksDataQuery = ObjectType(
"data_query",
"Gets books data",
fields[Repository, Unit](
Field("Books", ListType(BookType), arguments = bookId :: Nil, resolve = Projector(2, (context, fields) =>{ c.ctx.getBooks(c.arg(bookId), fields).map(res => res)}))
)
)
val BookType = ObjectType( ....)
val AuthorType = ObjectType( ....)
Repository class:
def getBooks(id: String, projectionFields: Vector[ProjectedName]) {
/* Here you have the list of fields that client specified in the query.
in this cse Book's id, title and author - id, name.
The fields are nested, for example author has id and name. In this case author will have sequence of id and name. i.e. above query field will look like:
Vector(ProjectedName(id,Vector()), ProjectedName(title,Vector()),ProjectedName(author,ProjectedName(id,Vector()),ProjectedName(name,Vector())))
Now you can put your own logic to read and parse fields the collection and make it appropriate for query in database. */
}
So basically, you can intercept specified fields by client in your QueryType's field resolver.