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}