Skip to content

Limiting number of results on Union types is not possible #2197

Open
@a-alle

Description

@a-alle

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:

  1. 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.

  1. 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 a LIMIT 6 - passing over type Book's max
  • options: { limit: 12 } result in a LIMIT 12 - passing over type Book and Journal's combined max

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

  1. support the @queryOptions defined on each type that is part of a Union. ✅
  2. combine the @queryOptions of the types (default and max) as expected from a Union of these types.
  3. both types should maintain their own options in the Cypher subquery that resolves them.
  4. options: {limit} for standard and first for connections should be compared and taken into account with the combined queryOptions in the generation of the final limit.
  5. Specifying a larger limit than the queryOptions.max of the Union should not result in a LIMIT of more than queryOptions.max.
  6. [Open Question: Smaller limit]
  7. [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's default: 2 + 4 = 6
  • max = the sum of all type's max: 7 + 9 = 16
    Each type limits at the subquery level with their own default and max value the nr of results.
    By default, the Publication would contain 2 Books and 4 Journals.

Open questions

  1. Do we allow sorting? How would that work if Book has title and Journal has subject?
  2. How would specifying a smaller limit than queryOptions.default of the Union type work? One of the types should be prioritized? In what order?

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingconfirmedConfirmed bug

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions