Performance of Relationship Comparisons
Relationship comparisons in GraphQL allow for powerful query filtering across related data. However, performance varies in Hasura DDN depending on how these comparisons are processed.
This page explains the performance implications of relationship comparisons, detailing both optimized query execution through data connectors and the fallback mechanisms at the query engine level when certain capabilities are unavailable.
How Relationship Comparisons Work
Relationship comparisons filter data based on fields in related objects. For example, filtering books by their author's name is a common relationship comparison:
query {
books(where: { author: { name: { _eq: "Alice Johnson" } } }) {
id
title
author {
name
}
}
}
This query filters books where the author’s name is "Alice Johnson", effectively creating a relationship comparison between books and authors.
Data Connector Capability: relation_comparisons
To achieve optimal performance, the query engine relies on the data connector’s ability to perform the comparison at the
data source level. This capability is known as relation_comparisons
(For more details see
the native data connector spec). When the data
connector supports this feature, the query engine pushes down the comparison logic to the underlying data connector
to handle the filtering at the data source layer.
Handling Missing Data Connector Capability
When the data connector lacks support for the relation_comparisons
capability, relationship comparisons cannot be
pushed down to the data connector for processing. Instead, the query engine must handle the comparison internally.
How It Works
Without relation_comparisons
, the query engine performs the following steps:
- Fetch Related Data: The query engine retrieves required fields from the related model.
- Construct Comparison Expressions: Using the fetched data, the engine constructs the necessary comparison expressions to filter the results.
- Fetch Data: The engine applies these expressions while fetching data from the primary model.
Performance Implications
- Increased Data Transfer: More data must be transferred from the data connector to the query engine for evaluation, which can lead to higher latency.
- Higher Query Engine Load: Performing comparisons in the query engine increases its workload, which can degrade performance, particularly with large datasets.
- Potential Bottlenecks: As the amount of related data increases, processing comparisons at the query engine level may become less efficient, which can result in slower query responses.
Example
Consider a query to filter books by the author's name when the relation_comparisons
capability is not available:
query {
books(where: { author: { name: { _eq: "Alice Johnson" } } }) {
id
title
author {
name
}
}
}
In the above query author
is an Object Relationship with book.author_id -> author.id
field mapping.
Without relation_comparisons
, the query engine will:
- Fetch the
id
s of all authors whosename
equals "Alice Johnson". - Create a comparison expression to check if
book.author_id
matches any of the author IDs obtained in step 1. - Query the books using the constructed comparison expression to filter based on
book.author_id
. - Return the filtered books to the client.
Monitoring Query Performance
To ensure your queries perform optimally, monitor the trace details for relationship comparisons. Specifically, look for
the span labeled Resolve relationship comparison expression: <name>
in the query trace. This span shows the time and
resources spent resolving the relationship comparison, which can help you identify performance issues. If the query
engine is handling the comparison internally due to missing relation_comparisons
capability, you might notice
increased execution times.
Trace Example: