diesel/associations/
belongs_to.rs

1use super::HasTable;
2use crate::dsl::{Eq, EqAny, Filter, FindBy};
3use crate::expression::array_comparison::AsInExpression;
4use crate::expression::AsExpression;
5use crate::prelude::*;
6use crate::query_dsl::methods::FilterDsl;
7use crate::sql_types::SqlType;
8
9use std::borrow::Borrow;
10use std::hash::Hash;
11
12/// Indicates that a type belongs to `Parent`
13///
14/// Specifically, this means that this struct has fields
15/// which correspond to the primary key of `Parent`.
16/// This implies that a foreign key relationship exists on the tables.
17///
18/// This trait is not capable of supporting composite foreign keys
19pub trait BelongsTo<Parent> {
20    /// The foreign key of this struct
21    type ForeignKey: Hash + ::std::cmp::Eq;
22    /// The database column representing the foreign key
23    /// of the table this struct represents
24    type ForeignKeyColumn: Column;
25
26    /// Returns the foreign key for `self`
27    fn foreign_key(&self) -> Option<&Self::ForeignKey>;
28    /// Returns the foreign key column of this struct's table
29    fn foreign_key_column() -> Self::ForeignKeyColumn;
30}
31
32/// The `grouped_by` function groups records by their parent.
33///
34/// `grouped_by` is called on a `Vec<Child>` with a `&[Parent]`.
35/// The return value will be `Vec<Vec<Child>>` indexed to match their parent.
36/// Or to put it another way, the returned data can be passed to `zip`,
37/// and it will be combined with its parent.
38/// This function does not generate a `GROUP BY` SQL statement,
39/// as it operates on data structures already loaded from the database
40///
41/// **Child** refers to the "many" part of a "one to many" relationship. It "belongs to" its parent
42/// **Parent** refers to the "one" part of a "one to many" relationship and can "have many" children.
43/// The child always has a foreign key, which refers to its parent's primary key.
44/// In the following relationship, User has many Posts,
45/// so User is the parent and Posts are children.
46///
47/// # Unrelated Rows
48///
49/// When using [`GroupedBy::grouped_by`], if the child rows were not queried
50/// using the provided parent rows, it is not guaranteed a parent row
51/// will be found for a given child row.
52/// This is possible, if the foreign key in the relationship is nullable,
53/// or if a child row's parent was not present in the provided slice,
54/// in which case, unrelated child rows will be discarded.
55///
56/// If discarding these rows is undesirable, it may be preferable to use
57/// [`GroupedBy::try_grouped_by`].
58///
59/// # Handling Duplicate Parent Rows
60///
61/// Both [`GroupedBy::grouped_by`] and [`GroupedBy::try_grouped_by`]
62/// expect all of the elements of `parents` to produce a unique value
63/// when calling [`Identifiable::id`].
64/// If this is not true, child rows may be added to an unexpected index.
65///
66/// As a result, it is recommended to use [`QueryDsl::distinct`]
67/// or [`slice::sort`] and [`Vec::dedup`],
68/// to ensure the elements of `parents` are unique.
69///
70/// # Example
71///
72/// ```rust
73/// # include!("../doctest_setup.rs");
74/// # use schema::{posts, users};
75/// #
76/// # #[derive(Identifiable, Queryable, PartialEq, Debug)]
77/// # pub struct User {
78/// #     id: i32,
79/// #     name: String,
80/// # }
81/// #
82/// # #[derive(Debug, PartialEq)]
83/// # #[derive(Identifiable, Queryable, Associations)]
84/// # #[diesel(belongs_to(User))]
85/// # pub struct Post {
86/// #     id: i32,
87/// #     user_id: i32,
88/// #     title: String,
89/// # }
90/// #
91/// # fn main() {
92/// #     run_test();
93/// # }
94/// #
95/// # fn run_test() -> QueryResult<()> {
96/// #     let connection = &mut establish_connection();
97/// let users = users::table.load::<User>(connection)?;
98/// let posts = Post::belonging_to(&users)
99///     .load::<Post>(connection)?
100///     .grouped_by(&users);
101/// let data = users.into_iter().zip(posts).collect::<Vec<_>>();
102///
103/// let expected_data = vec![
104///     (
105///         User { id: 1, name: "Sean".into() },
106///         vec![
107///             Post { id: 1, user_id: 1, title: "My first post".into() },
108///             Post { id: 2, user_id: 1, title: "About Rust".into() },
109///         ],
110///     ),
111///     (
112///         User { id: 2, name: "Tess".into() },
113///         vec![
114///             Post { id: 3, user_id: 2, title: "My first post too".into() },
115///         ],
116///     ),
117/// ];
118///
119/// assert_eq!(expected_data, data);
120/// #     Ok(())
121/// # }
122/// ```
123///
124/// See [the module documentation] for more examples
125///
126/// [the module documentation]: super
127pub trait GroupedBy<'a, Parent>: IntoIterator + Sized {
128    /// See the trait documentation.
129    fn grouped_by(self, parents: &'a [Parent]) -> Vec<Vec<Self::Item>>;
130
131    /// A fallible alternative to [`GroupedBy::grouped_by`].
132    ///
133    /// If any child record could not be grouped,
134    /// either because of a `NULL` foreign key,
135    /// or a parent record with a matching key could not be found,
136    /// this function should return `Err`,
137    /// with all successfully grouped records, as well as any ungrouped records.
138    ///
139    /// # Errors
140    ///
141    /// If a parent record could not be found for any of the child records,
142    /// this function should return the `TryGroupedByError`.
143    /// Every supplied record should be contained in the returned error,
144    /// either in the `grouped` field, if it was successfully grouped,
145    /// or the `ungrouped` field, if it was not possible to associate
146    /// with a parent record.
147    ///
148    /// # Examples
149    ///
150    /// ```rust
151    /// # include!("../doctest_setup.rs");
152    /// # use diesel::associations::TryGroupedByError;
153    /// # use schema::{posts, users};
154    /// #
155    /// # #[derive(Identifiable, Queryable, PartialEq, Debug)]
156    /// # pub struct User {
157    /// #     id: i32,
158    /// #     name: String,
159    /// # }
160    /// #
161    /// # #[derive(Debug, PartialEq)]
162    /// # #[derive(Identifiable, Queryable, Associations)]
163    /// # #[diesel(belongs_to(User))]
164    /// # pub struct Post {
165    /// #     id: i32,
166    /// #     user_id: i32,
167    /// #     title: String,
168    /// # }
169    /// #
170    /// # fn main() {
171    /// #     run_test();
172    /// # }
173    /// #
174    /// # fn run_test() -> QueryResult<()> {
175    /// #     let connection = &mut establish_connection();
176    /// let users = users::table.load::<User>(connection)?;
177    /// let mut posts = Post::belonging_to(&users)
178    ///     .load::<Post>(connection)?;
179    /// posts.push(
180    ///     Post { id: 9, user_id: 42, title: "A post returned from another query".into() }
181    /// );
182    /// let TryGroupedByError { grouped, ungrouped, .. } = posts.try_grouped_by(&users).unwrap_err();
183    ///
184    /// let grouped_data = users.into_iter().zip(grouped).collect::<Vec<_>>();
185    ///
186    /// let expected_grouped_data = vec![
187    ///     (
188    ///         User { id: 1, name: "Sean".into() },
189    ///         vec![
190    ///             Post { id: 1, user_id: 1, title: "My first post".into() },
191    ///             Post { id: 2, user_id: 1, title: "About Rust".into() },
192    ///         ],
193    ///     ),
194    ///     (
195    ///         User { id: 2, name: "Tess".into() },
196    ///         vec![
197    ///             Post { id: 3, user_id: 2, title: "My first post too".into() },
198    ///         ],
199    ///     ),
200    /// ];
201    ///
202    /// let expected_ungrouped_data = vec![
203    ///     Post { id: 9, user_id: 42, title: "A post returned from another query".into() }
204    /// ];
205    ///
206    /// assert_eq!(expected_grouped_data, grouped_data);
207    /// assert_eq!(expected_ungrouped_data, ungrouped);
208    /// #     Ok(())
209    /// # }
210    /// ```
211    fn try_grouped_by(
212        self,
213        parents: &'a [Parent],
214    ) -> Result<Vec<Vec<Self::Item>>, TryGroupedByError<Self::Item>> {
215        Ok(self.grouped_by(parents))
216    }
217}
218
219/// A type of error which can be returned when attempting to group
220/// a list of records.
221///
222/// If a child record has a nullable foreign key, or is being grouped
223/// using a different relationship to the one that was used to query it,
224/// it may not be possible to find a parent record it should be grouped by.
225///
226/// When encountering these missing relationships,
227/// it may still be possible to group remaining records,
228/// but would affect the contents of the returned values.
229/// By extracting the contents of this struct, it is still possible
230/// to use these resulting groups, as well as any records
231/// that could not be grouped.
232#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
233#[non_exhaustive]
234pub struct TryGroupedByError<Child> {
235    /// The collection of records which were successfully indexed
236    /// against a parent record.
237    pub grouped: Vec<Vec<Child>>,
238    /// The collection of records that could not be indexed
239    /// against a parent record.
240    pub ungrouped: Vec<Child>,
241}
242
243type Id<T> = <T as Identifiable>::Id;
244
245impl<Child> TryGroupedByError<Child> {
246    /// Creates a `TryGroupedByError`.
247    ///
248    /// This is generally used by methods like [`GroupedBy::try_grouped_by`].
249    pub fn new(grouped: Vec<Vec<Child>>, ungrouped: Vec<Child>) -> Self {
250        Self { grouped, ungrouped }
251    }
252}
253
254impl<'a, Parent: 'a, Child, Iter> GroupedBy<'a, Parent> for Iter
255where
256    Iter: IntoIterator<Item = Child>,
257    Child: BelongsTo<Parent>,
258    &'a Parent: Identifiable,
259    Id<&'a Parent>: Borrow<Child::ForeignKey>,
260{
261    fn grouped_by(self, parents: &'a [Parent]) -> Vec<Vec<Child>> {
262        use std::collections::HashMap;
263        use std::iter;
264
265        let mut grouped: Vec<_> = iter::repeat_with(Vec::new).take(parents.len()).collect();
266
267        let id_indices: HashMap<_, _> = parents
268            .iter()
269            .enumerate()
270            .map(|(i, u)| (u.id(), i))
271            .collect();
272
273        self.into_iter()
274            .filter_map(|child| {
275                let fk = child.foreign_key()?;
276                let i = id_indices.get(fk)?;
277
278                Some((i, child))
279            })
280            .for_each(|(i, child)| grouped[*i].push(child));
281
282        grouped
283    }
284
285    fn try_grouped_by(
286        self,
287        parents: &'a [Parent],
288    ) -> Result<Vec<Vec<Child>>, TryGroupedByError<Child>> {
289        use std::collections::HashMap;
290        use std::iter;
291
292        let mut grouped: Vec<_> = iter::repeat_with(Vec::new).take(parents.len()).collect();
293        let mut ungrouped: Vec<_> = Vec::new();
294
295        let id_indices: HashMap<_, _> = parents
296            .iter()
297            .enumerate()
298            .map(|(i, u)| (u.id(), i))
299            .collect();
300
301        for child in self {
302            child
303                .foreign_key()
304                .and_then(|i| id_indices.get(i))
305                .map_or(&mut ungrouped, |i| &mut grouped[*i])
306                .push(child);
307        }
308
309        if ungrouped.is_empty() {
310            Ok(grouped)
311        } else {
312            Err(TryGroupedByError::new(grouped, ungrouped))
313        }
314    }
315}
316
317impl<'a, Parent, Child> BelongingToDsl<&'a Parent> for Child
318where
319    &'a Parent: Identifiable,
320    Child: HasTable + BelongsTo<Parent>,
321    Id<&'a Parent>: AsExpression<<Child::ForeignKeyColumn as Expression>::SqlType>,
322    Child::Table: FilterDsl<Eq<Child::ForeignKeyColumn, Id<&'a Parent>>>,
323    Child::ForeignKeyColumn: ExpressionMethods,
324    <Child::ForeignKeyColumn as Expression>::SqlType: SqlType,
325{
326    type Output = FindBy<Child::Table, Child::ForeignKeyColumn, Id<&'a Parent>>;
327
328    fn belonging_to(parent: &'a Parent) -> Self::Output {
329        FilterDsl::filter(Child::table(), Child::foreign_key_column().eq(parent.id()))
330    }
331}
332
333impl<'a, Parent, Child> BelongingToDsl<&'a [Parent]> for Child
334where
335    &'a Parent: Identifiable,
336    Child: HasTable + BelongsTo<Parent>,
337    Vec<Id<&'a Parent>>: AsInExpression<<Child::ForeignKeyColumn as Expression>::SqlType>,
338    <Child as HasTable>::Table: FilterDsl<EqAny<Child::ForeignKeyColumn, Vec<Id<&'a Parent>>>>,
339    Child::ForeignKeyColumn: ExpressionMethods,
340    <Child::ForeignKeyColumn as Expression>::SqlType: SqlType,
341{
342    type Output = Filter<Child::Table, EqAny<Child::ForeignKeyColumn, Vec<Id<&'a Parent>>>>;
343
344    fn belonging_to(parents: &'a [Parent]) -> Self::Output {
345        let ids = parents.iter().map(Identifiable::id).collect::<Vec<_>>();
346        FilterDsl::filter(Child::table(), Child::foreign_key_column().eq_any(ids))
347    }
348}
349
350impl<'a, Parent, Child> BelongingToDsl<(&'a [Parent], &'a [Parent])> for Child
351where
352    &'a Parent: Identifiable,
353    Child: HasTable + BelongsTo<Parent>,
354    Vec<Id<&'a Parent>>: AsInExpression<<Child::ForeignKeyColumn as Expression>::SqlType>,
355    <Child as HasTable>::Table: FilterDsl<EqAny<Child::ForeignKeyColumn, Vec<Id<&'a Parent>>>>,
356    Child::ForeignKeyColumn: ExpressionMethods,
357    <Child::ForeignKeyColumn as Expression>::SqlType: SqlType,
358{
359    type Output = Filter<Child::Table, EqAny<Child::ForeignKeyColumn, Vec<Id<&'a Parent>>>>;
360
361    fn belonging_to(parents: (&'a [Parent], &'a [Parent])) -> Self::Output {
362        let ids = parents
363            .0
364            .iter()
365            .chain(parents.1.iter())
366            .map(Identifiable::id)
367            .collect::<Vec<_>>();
368        FilterDsl::filter(Child::table(), Child::foreign_key_column().eq_any(ids))
369    }
370}
371
372impl<'a, Parent, Child> BelongingToDsl<&'a Vec<Parent>> for Child
373where
374    Child: BelongingToDsl<&'a [Parent]>,
375{
376    type Output = Child::Output;
377
378    fn belonging_to(parents: &'a Vec<Parent>) -> Self::Output {
379        Self::belonging_to(&**parents)
380    }
381}