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}