Description
Description
Given the following type definitions:
typeDefs = gql`
union Publication = Book | Journal
type Author {
name: String!
publications: [Publication!]! @relationship(type: "WROTE", direction: OUT, properties: "Wrote")
}
type Book @queryOptions(limit: { default: 3, max: 5 }) {
title: String!
author: [Author!]! @relationship(type: "WROTE", direction: IN, properties: "Wrote")
}
type Journal @queryOptions(limit: { default: 3, max: 5 }) {
subject: String!
author: [Author!]! @relationship(type: "WROTE", direction: IN, properties: "Wrote")
}
interface Wrote {
words: Int!
}
`;
there is no way to impose limits on the query results involving union fields.
Below are the 2 cases in which Unions are involved: as connections, and as standard:
- case Connections
Both of:
const query1 = gql`
query {
authors {
name
publicationsConnection {
edges {
words
node {
... on Book {
title
}
... on Journal {
subject
}
}
}
}
}
}
`;
.. and ..
const query2 = gql`
query {
authors {
name
publicationsConnection(first: 2) {
edges {
words
node {
... on Book {
title
}
... on Journal {
subject
}
}
}
}
}
}
`;
Result in:
"MATCH (this:\`Author\`)
CALL {
WITH this
CALL {
WITH this
MATCH (this)-[this_connection_publicationsConnectionthis0:WROTE]->(this_Book:\`Book\`)
WITH { words: this_connection_publicationsConnectionthis0.words, node: { __resolveType: \\"Book\\", title: this_Book.title } } AS edge
RETURN edge
UNION
WITH this
MATCH (this)-[this_connection_publicationsConnectionthis1:WROTE]->(this_Journal:\`Journal\`)
WITH { words: this_connection_publicationsConnectionthis1.words, node: { __resolveType: \\"Journal\\", subject: this_Journal.subject } } AS edge
RETURN edge
}
WITH collect(edge) AS edges
WITH edges, size(edges) AS totalCount
RETURN { edges: edges, totalCount: totalCount } AS this_publicationsConnection
}
RETURN this { .name, publicationsConnection: this_publicationsConnection } as this"
! Notice he resulting Cypher does not contain any LIMIT.
It's fair to conclude that both @queryOptions
and first
are ignored.
- case standard:
const query3 = gql`
{
authors {
publications {
... on Book {
title
}
... on Journal {
subject
}
}
}
}
`;
Results in:
"MATCH (this:\`Author\`)
CALL {
WITH this
CALL {
WITH this
MATCH (this)-[thisthis0:WROTE]->(this_publications:\`Book\`)
WITH this_publications { __resolveType: \\"Book\\", .title } AS this_publications
RETURN this_publications AS this_publications
UNION
WITH this
MATCH (this)-[thisthis1:WROTE]->(this_publications:\`Journal\`)
WITH this_publications { __resolveType: \\"Journal\\", .subject } AS this_publications
RETURN this_publications AS this_publications
}
WITH this_publications
RETURN collect(this_publications) AS this_publications
}
RETURN this { publications: this_publications } as this"
However,
const query4 = gql`
{
authors {
publications(options: { limit: 1 }) {
... on Book {
title
}
... on Journal {
subject
}
}
}
}
`;
does create a LIMIT:
"MATCH (this:\`Author\`)
CALL {
WITH this
CALL {
WITH this
MATCH (this)-[thisthis0:WROTE]->(this_publications:\`Book\`)
WITH this_publications { __resolveType: \\"Book\\", .title } AS this_publications
RETURN this_publications AS this_publications
UNION
WITH this
MATCH (this)-[thisthis1:WROTE]->(this_publications:\`Journal\`)
WITH this_publications { __resolveType: \\"Journal\\", .subject } AS this_publications
RETURN this_publications AS this_publications
}
WITH this_publications
**LIMIT 1**
RETURN collect(this_publications) AS this_publications
}
RETURN this { publications: this_publications } as this"
Further on the topic,
options: { limit: 6 }
result in aLIMIT 6
- passing over typeBook
'smax
options: { limit: 12 }
result in aLIMIT 12
- passing over typeBook and Journal
's combinedmax
It's then fair to conclude that @queryOptions
is ignored, and only the options: { limit }
is really taken into account.
Problem
There should be a way to limit the nr of results returned by querying on Union types, and it should be uniform across both connection & standard cases.
Solution
- support the
@queryOptions
defined on each type that is part of a Union. ✅ - combine the
@queryOptions
of the types (default
andmax
) as expected from a Union of these types. - both types should maintain their own options in the Cypher subquery that resolves them.
options: {limit}
for standard andfirst
for connections should be compared and taken into account with the combinedqueryOptions
in the generation of the final limit.- Specifying a larger limit than the queryOptions.max of the Union should not result in a LIMIT of more than queryOptions.max.
- [Open Question: Smaller limit]
- [Open Question: Sorting]
Proposed Solution
Given the following simplified type definitions:
union Publication = Book | Journal
type Book @queryOptions(limit: { default: 2, max: 7 }) {...}
type Journal @queryOptions(limit: { default: 4, max: 9 }) {...}
The proposed approach (illustrated for the Union type Publication
):
default
= the sum of all type'sdefault
: 2 + 4 = 6max
= the sum of all type'smax
: 7 + 9 = 16
Each type limits at the subquery level with their owndefault
andmax
value the nr of results.
By default, the Publication would contain 2 Books and 4 Journals.
Open questions
- Do we allow sorting? How would that work if Book has title and Journal has subject?
- How would specifying a smaller limit than queryOptions.default of the Union type work? One of the types should be prioritized? In what order?