Relations define how entities are connected to each other. ChameleonDB supports one-to-many and many-to-one relationships using the via keyword.
Relation Types
ChameleonDB supports two primary relation patterns:
One-to-Many (Has Many)
Defines a collection relationship where one entity has multiple related entities:
entity User {
id : uuid primary ,
orders : [ Order ] via user_id , // User has many Orders
}
Syntax:
fieldName : [ TargetEntity ] via foreign_key_field
Many-to-One (Belongs To)
Defines a single relationship where one entity belongs to another:
entity Order {
id : uuid primary ,
user_id : uuid ,
user : User , // Order belongs to User
}
Syntax:
The via Keyword
The via keyword specifies the foreign key field in the related entity:
entity User {
id : uuid primary ,
email : string unique ,
// "via user_id" means: find Orders where order.user_id = user.id
orders : [ Order ] via user_id ,
}
entity Order {
id : uuid primary ,
total : decimal ,
user_id : uuid , // Foreign key field
user : User , // Reverse relation (no "via" needed)
}
The via keyword is only used for one-to-many relations (arrays). Many-to-one relations infer the foreign key automatically.
Bidirectional Relations
Most relationships are bidirectional - defined from both sides:
entity User {
id : uuid primary ,
email : string unique ,
orders : [ Order ] via user_id , // Forward: User → Orders
posts : [ Post ] via author_id , // Forward: User → Posts
}
entity Order {
id : uuid primary ,
total : decimal ,
user_id : uuid ,
user : User , // Reverse: Order → User
}
entity Post {
id : uuid primary ,
title : string ,
author_id : uuid ,
author : User , // Reverse: Post → User
}
Foreign Key Fields
Foreign key fields must be explicitly declared:
entity Order {
id : uuid primary ,
total : decimal ,
user_id : uuid , // ✓ Foreign key field explicitly declared
user : User , // Relation uses user_id implicitly
}
The foreign key field (user_id) must match the type of the target entity’s primary key.
Nested Relations
Relations can be traversed through multiple levels:
entity User {
id : uuid primary ,
email : string unique ,
orders : [ Order ] via user_id ,
}
entity Order {
id : uuid primary ,
total : decimal ,
user_id : uuid ,
user : User ,
items : [ OrderItem ] via order_id , // Nested relation
}
entity OrderItem {
id : uuid primary ,
quantity : int ,
price : decimal ,
order_id : uuid ,
order : Order ,
}
Querying nested relations:
// Eager load multiple levels
users := db . Query ( "User" ).
Include ( "orders" ).
Include ( "orders.items" ). // Nested include
Execute ( ctx )
Self-Referential Relations
Entities can reference themselves:
entity Category {
id : uuid primary ,
name : string ,
parent_id : uuid nullable ,
parent : Category nullable ,
children : [ Category ] via parent_id ,
}
Complete Example
Here’s a complete e-commerce schema demonstrating all relation patterns:
entity User {
id : uuid primary ,
email : string unique ,
name : string ,
created_at : timestamp default now (),
// One-to-many relations
orders : [ Order ] via user_id ,
posts : [ Post ] via author_id ,
}
entity Order {
id : uuid primary ,
total : decimal ,
status : string ,
created_at : timestamp default now (),
user_id : uuid ,
// Many-to-one relation
user : User ,
// One-to-many relation
items : [ OrderItem ] via order_id ,
}
entity OrderItem {
id : uuid primary ,
quantity : int ,
price : decimal ,
order_id : uuid ,
product_id : uuid ,
// Many-to-one relations
order : Order ,
product : Product ,
}
entity Product {
id : uuid primary ,
name : string ,
price : decimal ,
stock : int ,
// One-to-many relation
order_items : [ OrderItem ] via product_id ,
}
entity Post {
id : uuid primary ,
title : string ,
content : string ,
author_id : uuid ,
created_at : timestamp default now (),
// Many-to-one relation
author : User ,
}
Relation Validation
ChameleonDB validates relations at compile time:
Target Exists - The target entity must be defined in the schema
Foreign Key Type Match - Foreign key type must match the target’s primary key type
Foreign Key Exists - For via relations, the foreign key field must exist in the target entity
No Circular Ownership - Entities cannot form circular ownership dependencies
Querying Relations
Relations are traversed using the query API:
Eager Loading
Load related entities to avoid N+1 queries:
// Load users with their orders
users := db . Query ( "User" ).
Include ( "orders" ). // Eager load orders
Execute ( ctx )
for _ , user := range users {
fmt . Printf ( " %s has %d orders \n " , user . Email , len ( user . Orders ))
}
Nested Includes
// Load users → orders → items (3 levels)
users := db . Query ( "User" ).
Include ( "orders" ).
Include ( "orders.items" ). // Nested include
Execute ( ctx )
Multiple Relations
// Load multiple relations
users := db . Query ( "User" ).
Include ( "orders" ).
Include ( "posts" ). // Load both orders and posts
Execute ( ctx )
Best Practices
Name foreign keys consistently - Use {entity}_id pattern (e.g., user_id, order_id)
Define bidirectional relations - Define both sides for easier querying
Use meaningful relation names - Choose names that reflect the domain relationship
// Good - clear domain meaning
entity Order {
user_id : uuid ,
user : User , // "user" is clear
}
// Better - more specific
entity Post {
author_id : uuid ,
author : User , // "author" is more specific than "user"
}
Use eager loading - Always use Include() to avoid N+1 query problems
Common Patterns
Parent-Child
entity Category {
id : uuid primary ,
name : string ,
parent_id : uuid nullable ,
parent : Category nullable ,
children : [ Category ] via parent_id ,
}
Author-Content
entity User {
id : uuid primary ,
posts : [ Post ] via author_id ,
comments : [ Comment ] via author_id ,
}
entity Post {
id : uuid primary ,
author_id : uuid ,
author : User ,
}
Order-Line Items
entity Order {
id : uuid primary ,
items : [ OrderItem ] via order_id ,
}
entity OrderItem {
id : uuid primary ,
order_id : uuid ,
order : Order ,
}
Limitations
Many-to-many not yet supported - Use a join entity pattern instead:// Instead of direct many-to-many:
// tags: [Tag] many_to_many // ✗ Not supported
// Use a join entity:
entity PostTag {
id : uuid primary ,
post_id : uuid ,
tag_id : uuid ,
post : Post ,
tag : Tag ,
}
Next Steps
Field Types Learn about field types for foreign keys
Constraints Add validation to relations