Comments (8)
That query fetches all customers (in any order) and for each one, fetches the invoices. The invoices themselves are sorted by the customer's last name (which in this case wouldn't do anything since they're already constrained to a single customer). If you want to order the customers by last name:
{
customers(orderBy: { lastName: ASC }) {
lastName
invoices {
total
}
}
}
Ordering the customers by last name and each customer's invoices by the total:
{
customers(orderBy: { lastName: ASC }) {
lastName
invoices (orderBy: { total: DESC }){
total
}
}
}
from sqlmancer.
Alright, I'm going to be argumentative here, and push for reopening this, possibly under a different title. Here are two queries, one with a nonworking orderBy, the other with:
query no1 {
customers {
lastName
invoices (orderBy: {customer:{lastName:ASC}}){
createdOn
}
}
}
query no2 {
invoices (orderBy: {customer:{lastName:ASC}}){
customer {
lastName
}
total
}
}
The no2 orderBy clause is identical to the nonworking no1 clause. The problem as I see it is the clause in no2 works, whereas the one in no1 is a null operation.
I view the ability to specify non-functioning operations as bug, not a feature. I can't think of a reason (there may be one) why the results of the relational join operation between customer and invoice can't be ordered independently of the hierarchical relation that is presented by GraphQL. I would expect something like the following to be generated:
select `c1`.`CustomerId` as `id`, `c1`.`FirstName` as `firstName`, `c1`.`LastName` as `lastName`, `c1`.`Email` as `email`, (select json_group_array(json(`i1`.`o`)) from (select json_object(\'id\', `i2`.`InvoiceId`, \'total\', `i2`.`Total`, \'createdOn\', `i2`.`InvoiceId`) as `o` from `invoices` as `i2` where `c1`.`CustomerId` = `i2`.`CustomerId` orderby (select `c2`.`LastName` from `customers` as `c2` where `i2`.`CustomerId` = `c2`.`CustomerId`) asc) as `i1`) as `invoices` from `customers` as `c1`
order by 'lastName';
The only difference I added was the orderBy clause at the end. The order by is on the result set, which is the product of the join (c1, i1), with the column alias 'lastName'. I'm not that familiar with Postgres' JSON query support, but I don't think that matters here*, though maybe there is a SqlLite3 restriction of some kind on order by syntax.
In any case, if for some reason an orderBy parameter can't be respected, an error or warning should be thrown. That's more work, I know, but I don't think it's unreasonable.
* Observations:
- why is Email in the SELECT statement? It wasn't asked for. For many-columned tables, and those with BLOBs, it would be really inefficient to fetch more fields than requested.
- I see invoices is stored as JSON and aliased to i2 in the inline join in the select clause, then outer alias as i1 (I think). I can't grok why JSON is used here, though. Maybe I need to finish reading the docs.
- Why the use of relational expressions in WHERE instead of JOIN statements? I'm sure there are reasons, just asking.
from sqlmancer.
Thanks for your comments. Maybe I'm not fully groking the point you're trying to raise, so bear with me if I'm misunderstanding. In our example, we have a graph of data representing a list of customers and each customer's invoices:
query no1 {
customers {
lastName
invoices {
createdOn
}
}
}
We can provide an orderBy
to both the customers
field and the invoices
field. In the former case, we're saying "get all customers and sort them by these criteria". In the latter case, we're saying "for each customer, get their invoices and sort that set of invoices by these criteria".
In query no1
, we don't provide any criteria for sorting customers
so their order will not be guaranteed. Query no1
does provide a sort criteria for invoices
. So for each customer, we return the invoices sorted by the provided criteria. However, the criteria is {customer:{lastName:ASC}}
. That does not mean "sort the customers by last name". It means "sort the invoices according to each invoice's associated customer's last name". Looking at the generated query, this is exactly what it does:
select `c1`.`CustomerId` as `id`,
`c1`.`FirstName` as `firstName`,
`c1`.`LastName` as `lastName`,
`c1`.`Email` as `email`,
(select json_group_array(json(`i1`.`o`))
from (select json_object('id', `i2`.`InvoiceId`, 'total', `i2`.`Total`, 'createdOn', `i2`.`InvoiceId`) as `o`
from `invoices` as `i2`
where `c1`.`CustomerId` = `i2`.`CustomerId`
order by (select `c2`.`LastName`
from `customers` as `c2`
where `i2`.`CustomerId` = `c2`.`CustomerId`) asc) as `i1`) as `invoices`
from `customers` as `c1`
Again, it's not that this query doesn't work -- it's just that the value we're sorting by (invoice.customer.lastName) happens to be the same for all the records we're sorting. It's no different than if the invoices all had the same total and we sorted by that -- that query also "wouldn't work" in the same sense.
from sqlmancer.
The extraneous columns are actually a bug so thanks for catching that! Looks like a regression introduced by a recent change. I opened #61 to track it. Will fix ASAP.
from sqlmancer.
Utilizing JSON aggregation and correlated subqueries allows us to easily apply ORDER BY, WHERE, LIMIT and OFFSET clauses to each set of related records independently. Just using joins isn't really viable unless we used lateral joins but those are only supported by Postgres.
JSON aggregation also ensures that the driver returns the data in the shape that we need, without needing to do additional processing after the fact. This is in contrast with the approach taken by many other libraries, like Sequelize, where the database returns something like
SELECT "User"."id",
"User"."createdAt",
"User"."updatedAt",
"Addresses"."id"
"Addresses"."createdAt"
"Addresses"."updatedAt"
"Addresses->UserAddressWithExtraNamePaddingHelloWorldWithExtraNamePadding"."createdAt",
"Addresses->UserAddressWithExtraNamePaddingHelloWorldWithExtraNamePadding"."updatedAt",
"Addresses->UserAddressWithExtraNamePaddingHelloWorldWithExtraNamePadding"."UserId",
"Addresses->UserAddressWithExtraNamePaddingHelloWorldWithExtraNamePadding"."AddressId"
and the library has to transform it into the appropriate JSON structure after the fact.
from sqlmancer.
from sqlmancer.
from sqlmancer.
I understand what you are saying. Any nested orderBy operates within the constraints of the parent context. You could achieve something similar by doing a correlated subquery in SQL. And yes, your right: the orderBy statement on the Invoice in no1 "works" but there's nothing really to order by; there's only one lastName (one customer) for each invoice. In a heavily nested query, this is a kind of thing that could easily be missed, though. It seems possible to throw an error if the orderBy references a field in a ancestor...maybe? I'd have to think about it. Okay, my head is a little clearer now. Thanks for detailed clarification.
This is also a good example of where you might start with the @orderBy
directive initially and then drop it and just provide your own modified type(s). This way you can get a MVP schema rolled out quickly, but then come back and tweak the input types and filter out options that don't really make sense in the context of your business logic.
from sqlmancer.
Related Issues (20)
- graphql-toolkit is now deprecated in favor of graphql-tools
- The Node interface and globally unique IDs HOT 1
- Missing directives (NestJS integration) HOT 6
- add sybase support
- [RFC] Changes to the existing API and workflow HOT 12
- Issue when using esModuleInterop option HOT 3
- Trouble adding `@paginate` directives HOT 3
- Allow passing a schema directly to createSqlmancerClient
- Hitting some issues with one to many relations and retreiving specific fields HOT 2
- Ability to specify default and maximum limit values
- Customizable argument and input field names
- Suggestion: Expose join API on Builder Class HOT 3
- Depend on not passing field to the resolver unless in the query HOT 3
- Add ability to mock database results
- tion
- CTE in WHERE clause is empty HOT 1
- Join by multiple fields HOT 1
- Enhancement: support for upserts HOT 1
- Allow composite primary keys HOT 1
- Is there a Roadmap? HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from sqlmancer.