⛓ Query Builder
Aphrodite generates rich and type safe query builders (example) from your schemas. The query builders not only query for nodes of a given type but also traverse edges, apply filters, do pagination and more.
As an example, we can traverse from a user to their photos uploaded after 2022-01-01
to users tagged in those photos named Jeff
like so:
const jeffQuery = user
.queryPhotos()
.whereUploadDate(P.greaterThan(new Date('2022-01-01')))
.queryTaggedUsers()
.whereName(P.equals('Jeff'));
Given that each hop can return a new type of query (e.g., PhotoQuery
or UserQuery
) on which to apply filters or take more hops, how do we enable such an API?
The solution is pretty simple. Every invocation to a method on the query builder returns a new query builder that holds:
- A reference the previous query
- A reference to the expression (filter/map/limit/etc) being applied
I.e.,
class UserQuery implements Query {
constructor(
private sourceQuery: Query,
private expression: Expression | null,
) {}
queryPhotos() {
return new PhotoQuery(this, null);
}
whereName(predicate) {
return new UserQuery(this, new FilterExpression(predicate));
}
}
This forms a linked list which represents all the invocations made against the query builder, creating a structure that looks like:
The first node in the list represents the last query builder method that was invoked (whereName(P.equals('Jeff'))
) and the last node in the list represents the first query builder method that was invoked (queryPhotos
).
Walking the linked list to the end will get you to the root that starts the query.
Running each node in the list from the end back to the start will run the full query. In practice, however, we convert the query to a plan
and optimize the plan
before running it. See Query Planning.
The above structure shows the basic idea but a more faithful representation of what is created by the query builder API is reproduced below.
Base query classes: https://github.com/tantaman/aphrodite/blob/main/packages/query-runtime-ts/src/Query.ts
Example generated query builder: https://github.com/tantaman/aphrodite/blob/main/packages/integration-tests-ts/src/generated/UserQuery.ts#L17-L66