Relational Queries
Relevant source files
The following files were used as context for generating this wiki page:
Relational Queries in Seaography provide a mechanism to traverse and filter data across database relationships within a GraphQL environment. By bridging SeaORM entities and async-graphql, Seaography automatically maps database relations—such as 1-to-1, 1-to-N, and M-to-N—into nested GraphQL fields. This allows users to perform deep, complex queries that fetch related data in a single request while maintaining the type safety and constraints defined in the underlying database schema.
Sources: README.md:14-23, src/lib.rs:25-35
Architecture and Core Components
The relational query system is built on top of the dynamic schema engine of async-graphql. It utilizes sea-orm entities as the source of truth for relationships. When a schema is built, the Builder inspects the relationships defined in the SeaORM models and registers them as GraphQL fields.
Key Components
| Component | Description |
|---|---|
Builder | The central coordinator that registers entities and their relationships into the GraphQL schema. |
RelationBuilder | Responsible for generating GraphQL fields for specific SeaORM relations. |
DataLoader | Optimizes relational queries by batching requests to the database, preventing N+1 query problems. |
RelatedEntityFilter | Allows applying filters specifically to related entities during a query. |
Sources: examples/sea-draw/src/schema.rs:260-275, CHANGELOG.md:145-155, src/lib.rs:185-200
Relationship Mapping Flow
The following diagram illustrates how Seaography processes a relational query from a GraphQL request down to the database execution.
Supported Relation Types
Seaography supports all standard relational patterns provided by SeaORM. These are automatically exposed as nested fields within the GraphQL schema.
- 1-to-1: Direct relationship where one record in Table A relates to exactly one record in Table B.
- 1-to-Many (1-to-N): A record in Table A can relate to multiple records in Table B.
- Many-to-Many (M-to-N): Records in Table A relate to multiple records in Table B through a junction table.
- Self-Referencing: Relationships where an entity relates to itself (e.g., a
Staffrecord reporting to anotherStaffrecord).
Sources: README.md:65-70, CHANGELOG.md:110-120, examples/sqlite/tests/plural_query_tests.rs:275-300
Filtering and Pagination in Relations
One of the powerful features of Seaography is the ability to apply filters and pagination parameters to nested relational fields. This means you can fetch an entity and only include related records that meet specific criteria.
Filtering by Related Entities (having)
Version 2.0 introduced the ability to filter a parent entity based on the properties of its related entities using a having clause.
{
country(having: { city: { city: { eq: "London" } } }) {
nodes {
country
city {
nodes {
city
}
}
}
}
}
Sources: CHANGELOG.md:12-25, examples/sqlite/tests/plural_query_tests.rs:560-580
Nested Field Parameters
Relational fields accept the same filters, orderBy, and pagination arguments as top-level query fields.
{
customer {
nodes {
payment(
filters: { amount: { gt: "7" } }
orderBy: { amount: ASC }
pagination: { page: { limit: 1, page: 1 } }
) {
nodes {
amount
}
}
}
}
}
Sources: README.md:140-165, examples/postgres/tests/query_tests.rs:360-385
Implementation Details
The implementation of relational queries relies on the register_entity macro (or functional equivalent) which sets up the data loaders.
macro_rules! register_entity {
($builder:expr, $module_path:ident $(, $custom_fields:expr)? ) => {
// ... mapping logic ...
$builder =
$builder.register_entity_dataloader_one_to_one($module_path::Entity, tokio::spawn);
$builder =
$builder.register_entity_dataloader_one_to_many($module_path::Entity, tokio::spawn);
};
}
Sources: examples/sea-draw/src/schema.rs:260-275, generator/src/writer.rs:85-110
Naming Conventions
Seaography allows for both camelCase and snake_case field names via feature flags (field-camel-case and field-snake-case). It also supports singular and plural naming for fields to distinguish between single-record lookups and collection lookups.
Sources: CHANGELOG.md:58-75, Cargo.toml:48-52
Summary
Relational queries in Seaography enable seamless traversal of database schemas through GraphQL. By leveraging SeaORM's metadata and async-graphql's dynamic capabilities, the system provides advanced features like nested filtering, batch loading via DataLoader, and complex relationship mapping (1:1, 1:N, M:N) with minimal boilerplate. This architecture ensures high performance and technical accuracy by keeping the GraphQL schema strictly in sync with the database definitions.