Skip to main content

schema_core/config/
join.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{Field, common};
4
5use super::Filter;
6
7/// Folds rows from a related `table` into the document. The [`kind`](Self::kind)
8/// names the relationship — which side carries the key, and whether one row or
9/// many fold in; `filters`, `order_by`, and `limit` narrow and shape the rows
10/// that come back.
11#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
12pub struct Join {
13    pub table: common::TableName,
14    pub kind: JoinKind,
15    pub primary_key: common::ColumnName,
16    #[serde(default, skip_serializing_if = "Option::is_none")]
17    pub filters: Option<Vec<Filter>>,
18    #[serde(default, skip_serializing_if = "Option::is_none")]
19    pub order_by: Option<Vec<OrderBy>>,
20    #[serde(default, skip_serializing_if = "Option::is_none")]
21    pub limit: Option<u64>,
22    pub fields: Vec<Field>,
23}
24
25/// The relationship a join expresses. The verb carries both the cardinality
26/// and — the part that matters — **which table holds the key**:
27///
28/// - [`BelongsTo`](Self::BelongsTo): *this* row points at the related row
29///   (`column` is on the parent table) → a single object.
30/// - [`HasOne`](Self::HasOne) / [`HasMany`](Self::HasMany): the related rows
31///   point back at this one (`foreign_key` is on the related table) → a single
32///   object / a nested array.
33/// - [`ManyToMany`](Self::ManyToMany): both sides are keyed through a junction
34///   table → a nested array.
35#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
36#[serde(rename_all = "snake_case")]
37pub enum JoinKind {
38    BelongsTo { column: common::ColumnName },
39    HasOne { foreign_key: common::ColumnName },
40    HasMany { foreign_key: common::ColumnName },
41    ManyToMany { through: Through },
42}
43
44impl JoinKind {
45    /// Whether this join folds in many rows (a nested array) rather than one
46    /// (an object).
47    pub fn is_to_many(&self) -> bool {
48        matches!(self, JoinKind::HasMany { .. } | JoinKind::ManyToMany { .. })
49    }
50}
51
52/// How an aggregate's related rows tie back to the parent — a direct FK on the
53/// aggregated table, or a junction table. (Joins carry their key inside
54/// [`JoinKind`]; aggregates are inherently over-many, so `belongs_to` has no
55/// aggregate counterpart.)
56#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
57#[serde(rename_all = "snake_case")]
58pub enum AggregateKey {
59    Direct(common::ColumnName),
60    Through(Through),
61}
62
63/// A junction table linking two sides of a many-to-many relation.
64#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
65pub struct Through {
66    pub table: common::TableName,
67    pub left_key: common::ColumnName,
68    pub right_key: common::ColumnName,
69}
70
71#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
72pub struct OrderBy {
73    pub column: common::ColumnName,
74    #[serde(default, skip_serializing_if = "Option::is_none")]
75    pub direction: Option<Direction>,
76}
77
78#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
79#[serde(rename_all = "snake_case")]
80pub enum Direction {
81    Asc,
82    Desc,
83}