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 {
106///             id: 1,
107///             name: "Sean".into(),
108///         },
109///         vec![
110///             Post {
111///                 id: 1,
112///                 user_id: 1,
113///                 title: "My first post".into(),
114///             },
115///             Post {
116///                 id: 2,
117///                 user_id: 1,
118///                 title: "About Rust".into(),
119///             },
120///         ],
121///     ),
122///     (
123///         User {
124///             id: 2,
125///             name: "Tess".into(),
126///         },
127///         vec![Post {
128///             id: 3,
129///             user_id: 2,
130///             title: "My first post too".into(),
131///         }],
132///     ),
133/// ];
134///
135/// assert_eq!(expected_data, data);
136/// #     Ok(())
137/// # }
138/// ```
139///
140/// See [the module documentation] for more examples
141///
142/// [the module documentation]: super
143pub trait GroupedBy<'a, Parent>: IntoIterator + Sized {
144    /// See the trait documentation.
145    fn grouped_by(self, parents: &'a [Parent]) -> Vec<Vec<Self::Item>>;
146
147    /// A fallible alternative to [`GroupedBy::grouped_by`].
148    ///
149    /// If any child record could not be grouped,
150    /// either because of a `NULL` foreign key,
151    /// or a parent record with a matching key could not be found,
152    /// this function should return `Err`,
153    /// with all successfully grouped records, as well as any ungrouped records.
154    ///
155    /// # Errors
156    ///
157    /// If a parent record could not be found for any of the child records,
158    /// this function should return the `TryGroupedByError`.
159    /// Every supplied record should be contained in the returned error,
160    /// either in the `grouped` field, if it was successfully grouped,
161    /// or the `ungrouped` field, if it was not possible to associate
162    /// with a parent record.
163    ///
164    /// # Examples
165    ///
166    /// ```rust
167    /// # include!("../doctest_setup.rs");
168    /// # use diesel::associations::TryGroupedByError;
169    /// # use schema::{posts, users};
170    /// #
171    /// # #[derive(Identifiable, Queryable, PartialEq, Debug)]
172    /// # pub struct User {
173    /// #     id: i32,
174    /// #     name: String,
175    /// # }
176    /// #
177    /// # #[derive(Debug, PartialEq)]
178    /// # #[derive(Identifiable, Queryable, Associations)]
179    /// # #[diesel(belongs_to(User))]
180    /// # pub struct Post {
181    /// #     id: i32,
182    /// #     user_id: i32,
183    /// #     title: String,
184    /// # }
185    /// #
186    /// # fn main() {
187    /// #     run_test();
188    /// # }
189    /// #
190    /// # fn run_test() -> QueryResult<()> {
191    /// #     let connection = &mut establish_connection();
192    /// let users = users::table.load::<User>(connection)?;
193    /// let mut posts = Post::belonging_to(&users).load::<Post>(connection)?;
194    /// posts.push(Post {
195    ///     id: 9,
196    ///     user_id: 42,
197    ///     title: "A post returned from another query".into(),
198    /// });
199    /// let TryGroupedByError {
200    ///     grouped, ungrouped, ..
201    /// } = posts.try_grouped_by(&users).unwrap_err();
202    ///
203    /// let grouped_data = users.into_iter().zip(grouped).collect::<Vec<_>>();
204    ///
205    /// let expected_grouped_data = vec![
206    ///     (
207    ///         User {
208    ///             id: 1,
209    ///             name: "Sean".into(),
210    ///         },
211    ///         vec![
212    ///             Post {
213    ///                 id: 1,
214    ///                 user_id: 1,
215    ///                 title: "My first post".into(),
216    ///             },
217    ///             Post {
218    ///                 id: 2,
219    ///                 user_id: 1,
220    ///                 title: "About Rust".into(),
221    ///             },
222    ///         ],
223    ///     ),
224    ///     (
225    ///         User {
226    ///             id: 2,
227    ///             name: "Tess".into(),
228    ///         },
229    ///         vec![Post {
230    ///             id: 3,
231    ///             user_id: 2,
232    ///             title: "My first post too".into(),
233    ///         }],
234    ///     ),
235    /// ];
236    ///
237    /// let expected_ungrouped_data = vec![Post {
238    ///     id: 9,
239    ///     user_id: 42,
240    ///     title: "A post returned from another query".into(),
241    /// }];
242    ///
243    /// assert_eq!(expected_grouped_data, grouped_data);
244    /// assert_eq!(expected_ungrouped_data, ungrouped);
245    /// #     Ok(())
246    /// # }
247    /// ```
248    fn try_grouped_by(
249        self,
250        parents: &'a [Parent],
251    ) -> Result<Vec<Vec<Self::Item>>, TryGroupedByError<Self::Item>> {
252        Ok(self.grouped_by(parents))
253    }
254}
255
256/// A type of error which can be returned when attempting to group
257/// a list of records.
258///
259/// If a child record has a nullable foreign key, or is being grouped
260/// using a different relationship to the one that was used to query it,
261/// it may not be possible to find a parent record it should be grouped by.
262///
263/// When encountering these missing relationships,
264/// it may still be possible to group remaining records,
265/// but would affect the contents of the returned values.
266/// By extracting the contents of this struct, it is still possible
267/// to use these resulting groups, as well as any records
268/// that could not be grouped.
269#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
270#[non_exhaustive]
271pub struct TryGroupedByError<Child> {
272    /// The collection of records which were successfully indexed
273    /// against a parent record.
274    pub grouped: Vec<Vec<Child>>,
275    /// The collection of records that could not be indexed
276    /// against a parent record.
277    pub ungrouped: Vec<Child>,
278}
279
280type Id<T> = <T as Identifiable>::Id;
281
282impl<Child> TryGroupedByError<Child> {
283    /// Creates a `TryGroupedByError`.
284    ///
285    /// This is generally used by methods like [`GroupedBy::try_grouped_by`].
286    pub fn new(grouped: Vec<Vec<Child>>, ungrouped: Vec<Child>) -> Self {
287        Self { grouped, ungrouped }
288    }
289}
290
291impl<'a, Parent: 'a, Child, Iter> GroupedBy<'a, Parent> for Iter
292where
293    Iter: IntoIterator<Item = Child>,
294    Child: BelongsTo<Parent>,
295    &'a Parent: Identifiable,
296    Id<&'a Parent>: Borrow<Child::ForeignKey>,
297{
298    fn grouped_by(self, parents: &'a [Parent]) -> Vec<Vec<Child>> {
299        use std::collections::HashMap;
300        use std::iter;
301
302        let mut grouped: Vec<_> = iter::repeat_with(Vec::new).take(parents.len()).collect();
303
304        let id_indices: HashMap<_, _> = parents
305            .iter()
306            .enumerate()
307            .map(|(i, u)| (u.id(), i))
308            .collect();
309
310        self.into_iter()
311            .filter_map(|child| {
312                let fk = child.foreign_key()?;
313                let i = id_indices.get(fk)?;
314
315                Some((i, child))
316            })
317            .for_each(|(i, child)| grouped[*i].push(child));
318
319        grouped
320    }
321
322    fn try_grouped_by(
323        self,
324        parents: &'a [Parent],
325    ) -> Result<Vec<Vec<Child>>, TryGroupedByError<Child>> {
326        use std::collections::HashMap;
327        use std::iter;
328
329        let mut grouped: Vec<_> = iter::repeat_with(Vec::new).take(parents.len()).collect();
330        let mut ungrouped: Vec<_> = Vec::new();
331
332        let id_indices: HashMap<_, _> = parents
333            .iter()
334            .enumerate()
335            .map(|(i, u)| (u.id(), i))
336            .collect();
337
338        for child in self {
339            child
340                .foreign_key()
341                .and_then(|i| id_indices.get(i))
342                .map_or(&mut ungrouped, |i| &mut grouped[*i])
343                .push(child);
344        }
345
346        if ungrouped.is_empty() {
347            Ok(grouped)
348        } else {
349            Err(TryGroupedByError::new(grouped, ungrouped))
350        }
351    }
352}
353
354impl<'a, Parent, Child> BelongingToDsl<&'a Parent> for Child
355where
356    &'a Parent: Identifiable,
357    Child: HasTable + BelongsTo<Parent>,
358    Id<&'a Parent>: AsExpression<<Child::ForeignKeyColumn as Expression>::SqlType>,
359    Child::Table: FilterDsl<Eq<Child::ForeignKeyColumn, Id<&'a Parent>>>,
360    Child::ForeignKeyColumn: ExpressionMethods,
361    <Child::ForeignKeyColumn as Expression>::SqlType: SqlType,
362{
363    type Output = FindBy<Child::Table, Child::ForeignKeyColumn, Id<&'a Parent>>;
364
365    fn belonging_to(parent: &'a Parent) -> Self::Output {
366        FilterDsl::filter(Child::table(), Child::foreign_key_column().eq(parent.id()))
367    }
368}
369
370impl<'a, Parent, Child> BelongingToDsl<&'a [Parent]> for Child
371where
372    &'a Parent: Identifiable,
373    Child: HasTable + BelongsTo<Parent>,
374    Vec<Id<&'a Parent>>: AsInExpression<<Child::ForeignKeyColumn as Expression>::SqlType>,
375    <Child as HasTable>::Table: FilterDsl<EqAny<Child::ForeignKeyColumn, Vec<Id<&'a Parent>>>>,
376    Child::ForeignKeyColumn: ExpressionMethods,
377    <Child::ForeignKeyColumn as Expression>::SqlType: SqlType,
378{
379    type Output = Filter<Child::Table, EqAny<Child::ForeignKeyColumn, Vec<Id<&'a Parent>>>>;
380
381    fn belonging_to(parents: &'a [Parent]) -> Self::Output {
382        let ids = parents.iter().map(Identifiable::id).collect::<Vec<_>>();
383        FilterDsl::filter(Child::table(), Child::foreign_key_column().eq_any(ids))
384    }
385}
386
387impl<'a, Parent, Child> BelongingToDsl<(&'a [Parent], &'a [Parent])> for Child
388where
389    &'a Parent: Identifiable,
390    Child: HasTable + BelongsTo<Parent>,
391    Vec<Id<&'a Parent>>: AsInExpression<<Child::ForeignKeyColumn as Expression>::SqlType>,
392    <Child as HasTable>::Table: FilterDsl<EqAny<Child::ForeignKeyColumn, Vec<Id<&'a Parent>>>>,
393    Child::ForeignKeyColumn: ExpressionMethods,
394    <Child::ForeignKeyColumn as Expression>::SqlType: SqlType,
395{
396    type Output = Filter<Child::Table, EqAny<Child::ForeignKeyColumn, Vec<Id<&'a Parent>>>>;
397
398    fn belonging_to(parents: (&'a [Parent], &'a [Parent])) -> Self::Output {
399        let ids = parents
400            .0
401            .iter()
402            .chain(parents.1.iter())
403            .map(Identifiable::id)
404            .collect::<Vec<_>>();
405        FilterDsl::filter(Child::table(), Child::foreign_key_column().eq_any(ids))
406    }
407}
408
409impl<'a, Parent, Child> BelongingToDsl<&'a Vec<Parent>> for Child
410where
411    Child: BelongingToDsl<&'a [Parent]>,
412{
413    type Output = Child::Output;
414
415    fn belonging_to(parents: &'a Vec<Parent>) -> Self::Output {
416        Self::belonging_to(&**parents)
417    }
418}