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

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.

Related

Redis getting all fields of sorted set

im trying to make a freelancing website that will hold gigs at redis for caching, in order to categorise them, there are 2 fields called "categoryId" and "skillId" and i want to keep them sorted with "createdAt" field which is a date. So i have two options and i have some blank spots about first one.
option1
Im holding my gigs at sorted set and making a key with two parameter which holds categoryId And skillId, but the problem is user may want only select gigs with specific category and skill doesn't matter. But user may also want select gigs with both categoryId and skillId. So for that reason i used a key like
`gigs:${categoryId}:${skillId != null ? skillId : "*"}`
here's my full code
export const addGigToSortedSet = async (value) => {
return new Promise<string>((resolve, reject) => {
let date =
value.gigCreatedAt != null && value.createdAt != undefined
? Math.trunc(Date.parse(<string>value.createdAt) / 1000)
: Date.now();
redisClient
.zAdd(`gigs:${value.gigCategory}:${value.gigSkill}`, {
score: date,
value: JSON.stringify(value),
})
.then((res) => {
if (res == 1) {
resolve("Başarılı");
} else {
reject("Hata");
return;
}
});
});
};
export const multiAddGigsToSortedSet = async (gigs: any[]) => {
return new Promise((resolve, reject) => {
let multiClient = redisClient.multi();
for (const gig of gigs) {
let date =
gig.gigCreatedAt != null && gig.createdAt != undefined
? Math.trunc(Date.parse(<string>gig.createdAt) / 1000)
: Date.now();
multiClient.zAdd(`gigs:${gig.gigCategory}:${gig.gigSkill}`, {
score: date,
value: JSON.stringify(gig),
});
}
multiClient.exec().then((replies) => {
if (replies.length > 0) {
resolve(replies);
} else {
reject("Hata");
return;
}
});
});
};
export const getGigsFromSortedSet = async (
categoryId: string,
page: number,
limit: number,
skillId?: string
) => {
return new Promise<string[]>((resolve, reject) => {
redisClient
.zRange(
`gigs:${categoryId}:${skillId != null ? skillId : "*"}`,
(page - 1) * limit,
page * limit
)
.then((res) => {
if (res) {
resolve(res.reverse());
} else {
reject("Hata");
return;
}
});
});
};
Option 2
option two is way more simpler but less more effective with storage usage
i'll create two sorted set about category and skill and then will use zinterstore to get my values, and i will easily get gigs about only category since i have different set.
so my question is which way is more effective solution and will this line give me gigs with given category without skill parameter?
gigs:${categoryId}:${skillId != null ? skillId : "*"}
Your approach #2 is the most common implementation. See https://redis.io/docs/reference/patterns/indexes/
But...
Indexes created with sorted sets are able to index only a single
numerical value. Because of this you may think it is impossible to
index something which has multiple dimensions using this kind of
indexes, but actually this is not always true. If you can efficiently
represent something multi-dimensional in a linear way, they it is
often possible to use a simple sorted set for indexing.
For example the Redis geo indexing API uses a sorted set to index
places by latitude and longitude using a technique called Geo hash.
The sorted set score represents alternating bits of longitude and
latitude
Therefore if you can find an encoding scheme of your "categoryId" and "skillId" into a single value then you could use a single sorted set.

Ordering nested / second level Arrays with Prisma?

I am using Prisma & PostgreSQL. Here I grab some stuff:
await prisma.items.findMany({
where: { itemId: itemId },
include: {
modules: {
include: {
lessons: true
}
}
}
});
I do not need to order the items themselves, but I would like to order the modules & lessons that I get back. Both have an INT property (called: number) on which I could perform the ordering, but I do not know how to do this with prisma / postgresql, or even if it's possible.
Any ideas?
You can use the orderBy operator for this.
Here's what the query would look like for your use-case:
const data = await prisma.items.findMany({
where: {itemId: itemId},
include: {
modules: {
orderBy: {
number: 'asc'
},
include: {
lessons: {
orderBy: {
number: 'asc'
}
}
}
}
}
})
The article on filtering and sorting contains more information on this.

Is smart query custom variable name possible?

I'm using Vue alongside with Apollo in order to query a GraphQL endpoint in my project. Everything's fine but I want to start programming generic components to ease and fasten the development.
The thing is, in most views, I use the Smart Query system.
For instance, I use :
apollo: {
group: {
query: GROUP_QUERY,
variables () { return { id: this.groupId } },
skip () { return this.groupId === undefined },
result ({ data }) {
this.form.name = data.group.name
}
}
}
With the GROUP_QUERY that is :
const GROUP_QUERY = gql`
query groupQuery ($id: ID) {
group (id: $id) {
id
name
usersCount
userIds {
id
username
}
}
}
`
So my group variable in my apollo smart query has the same name as the query itself group (id: $id). It is this mechanism that is quite annoying for what I try to achieve. Is there a way to avoid that default mechanism ?
I'd like for instance to be able to give a generic name such as record, and it would be records for queries that potentially return multiple records.
With that, I would be able to make generic components or mixins that operate either on record or records.
Or have I to rename all my queries to record and records which would be annoying later on in case of troubleshooting with error messages ?
Or maybe there's another way to achieve that and I didn't think about it ?
Thanks in advance.
You can, in fact, rename the variable of Apollo smart queries using the update option, as seen here in the Vue Apollo documentation. Your example would look like:
apollo: {
record: {
query: GROUP_QUERY,
variables () { return { id: this.groupId } },
update: (data) => data.group,
skip () { return this.groupId === undefined },
result ({ data }) {
this.form.name = data.group.name
}
}
}
You should notice that the Apollo object will create a record variable in your component, and the update statement shows where to get the group for the record.
By doing so :
const GROUP_QUERY = gql`
query groupQuery ($id: ID) {
record: group (id: $id) {
id
name
usersCount
userIds {
id
username
}
}
}
`
If the GROUP_QUERY is used at several places, the result will be accessible under the record name, because it is defined as an alias over group.
See documentation for Aliases.

GraphQL, Dataloader, [ORM or not], hasMany relationship understanding

I'm using for the first time Facebook's dataloader (https://github.com/facebook/dataloader).
What I don't understand is how to use it when I have 1 to many relationships.
Here it is a reproduction of my problem: https://enshrined-hydrant.glitch.me.
If you use this query in the Playground:
query {
persons {
name
bestFriend {
name
}
opponents {
name
}
}
}
you get values.
But if you open the console log here: https://glitch.com/edit/#!/enshrined-hydrant you can see these database calls I want to avoid:
My Person type is:
type Person {
id: ID!
name: String!
bestFriend: Person
opponents: [Person]
}
I can use dataloader good for bestFriend: Person but I don't understand how to use it with opponents: [Person].
As you can see the resolver has to return an array of values.
Have you any hint about this?
You need to create batched endpoints to work with dataloader - it can't do batching by itself.
For example, you probably want the following endpoints:
GET /persons - returns all people
POST /bestFriends, Array<personId>` - returns an array of best friends matchin the corresponding array of `personId`s
Then, your dataloaders can look like:
function batchedBestFriends(personIds) {
return fetch('/bestFriends', {
method: 'POST',
body: JSON.stringify(personIds))
}).then(response => response.json());
// We assume above that the API returns a straight array of the data.
// If the data was keyed, you could add another accessor such as
// .then(data => data.bestFriends)
}
// The `keys` here will just be the accumulated list of `personId`s from the `load` call in the resolver
const bestFriendLoader = new DataLoader(keys => batchedBestFriends(keys));
Now, your resolver will look something like:
const PersonType = new GraphQLObjectType({
...
bestFriend: {
type: BestFriendType,
resolve: (person, args, context) => {
return bestFriendLoader.load(person.id);
}
}
});

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.