@@ -349,7 +349,7 @@ table. For example, querying for all reminders lists along with an array of the
349349list. We'd like to be able to query for this data and decode it into a collection of values
350350from the following data type:
351351
352- ``` struct
352+ ``` swift
353353struct Row {
354354 let remindersList: RemindersList
355355 let reminders: [Reminder]
@@ -395,7 +395,7 @@ Another way to do this is to use the `@Selection` macro described above
395395(< doc:QueryCookbook#Custom-selections > ), along with a `` Swift/Decodable/JSONRepresentation `` of the
396396collection of reminders you want to load for each list:
397397
398- ``` struct
398+ ``` swift
399399@Selection
400400struct Row {
401401 let remindersList: RemindersList
@@ -414,17 +414,74 @@ columns of [primary keyed tables](<doc:PrimaryKeyedTables>):
414414
415415``` swift
416416RemindersList
417- .join (Reminder.all ) { $0 .id .eq ($1 .remindersListID ) }
417+ .leftJoin (Reminder.all ) { $0 .id .eq ($1 .remindersListID ) }
418418 .select {
419419 Row.Columns (
420420 remindersList : $0 ,
421- reminders : $1 .jsonGroupArray ()
421+ reminders : #sql ( " \( $1 .jsonGroupArray (filter : $1 . id . isNot ( nil )) ) " )
422422 )
423423 }
424424```
425425
426+ > Note: There are 3 important things to note about this query:
427+ >
428+ > * Since not every reminders list will have a reminder associated with it, we are using a
429+ > `` Select/leftJoin(_:on:) `` . That will make sure to select all lists no matter what.
430+ > * We are using ` jsonGroupArray ` to encode all reminders associated with a list into a JSON object
431+ > and bundle them into an array. And because it's possible to have no reminders in a list,
432+ > we further use the ` filter ` option to remove any NULL values from the array.
433+ > * And lastly, ` $1 ` represents an optionalized table due to the left join, and hence the
434+ > ` $1.jsonGroupArray(…) ` expression actually returns an _ optional_ array of reminders. But due
435+ > to how ` json_group_array ` works in SQL we can be guaranteed that it always returns an array,
436+ > and never NULL, and so we are using the ` #sql ` macro as a quick escape hatch to take
437+ > responsibility for the types in this expression.
438+
426439This allows you to fetch all of the data in a single SQLite query and decode the data into a
427440collection of ` Row ` values. There is an extra cost associated with decoding the JSON object,
428441but that cost may be smaller than executing multiple SQLite requests and transforming the data
429442into ` Row ` manually, not to mention the additional code you need to write and maintain to process
430443the data.
444+
445+ It is even possible to load multiple associations at once. For example, suppose that there is a
446+ ` Milestone ` table that is associated with a ` RemindersList ` :
447+
448+ ``` swift
449+ @Table
450+ struct Milestone : Identifiable , Codable {
451+ let id: Int
452+ var title = " "
453+ var remindersListID: RemindersList.ID
454+ }
455+ ```
456+
457+ And suppose you would like to fetch all ` RemindersList ` s along with the collection of all milestones
458+ and reminders associated with the list:
459+
460+ ``` struct
461+ @Selection
462+ struct Row {
463+ let remindersList: RemindersList
464+ @Column(as: [Milestone].JSONRepresentation.self)
465+ let milestons: [Milestone]
466+ @Column(as: [Reminder].JSONRepresentation.self)
467+ let reminders: [Reminder]
468+ }
469+ ```
470+
471+ It is possible to do this using two left joins and two ` jsonGroupArray ` s:
472+
473+ ``` swift
474+ RemindersList
475+ .leftJoin (Reminder.all ) { $0 .id .eq ($1 .remindersListID ) }
476+ .leftJoin (Milestone.all ) { $0 .id .eq ($2 .remindersListID ) }
477+ .select {
478+ Row.Columns (
479+ remindersList : $0 ,
480+ reminders : #sql (" \( $1 .jsonGroupArray (filter : $1 .id .isNot (nil ))" ),
481+ reminders: #sql(" \($2 .jsonGroupArray (filter : $2 .id .isNot (nil ))" )
482+ )
483+ }
484+ ```
485+
486+ This will now load all reminders lists with all of their reminders and milestones in one single
487+ SQL query.
0 commit comments