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