Skip to content

Commit cd1cb2e

Browse files
committed
wip
1 parent 259a501 commit cd1cb2e

File tree

3 files changed

+22
-20
lines changed

3 files changed

+22
-20
lines changed

Sources/StructuredQueriesCore/Documentation.docc/Articles/QueryCookbook.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ let rows = remindersLists.map { remindersList in
392392

393393
This can work, but it's incredibly inefficient, a lot of boilerplate, and prone to mistakes. And
394394
this is doing work that SQL actually excels at. In fact, the condition inside the `filter` looks
395-
suspiciously like a join constraint, which should give us a hint that what we are doing is not
395+
suspiciously like a join constraint, which should give us a hint that what we are doing is not
396396
quite right.
397397

398398
A better way to do this is to use the `@Selection` macro described above
@@ -411,23 +411,23 @@ struct Row {
411411
> Note: `Reminder` must conform to `Codable` to be able to use ``JSONRepresentation``.
412412
413413
This allows the query to serialize the associated rows into JSON, which are then deserialized into
414-
a `Row` type. To construct such a query you can use the
415-
``PrimaryKeyedTableDefinition/jsonGroupArray`` property that is defined on the columns of
416-
[primary keyed tables](<doc:PrimaryKeyedTables>):
414+
a `Row` type. To construct such a query you can use the
415+
``PrimaryKeyedTableDefinition/jsonGroupArray(order:filter:)`` property that is defined on the
416+
columns of [primary keyed tables](<doc:PrimaryKeyedTables>):
417417

418418
```swift
419419
RemindersList
420420
.join(Reminder.all) { $0.id.eq($1.remindersListID) }
421-
.select {
421+
.select {
422422
Row.Columns(
423423
remindersList: $0,
424-
reminders: $1.jsonGroupArray
424+
reminders: $1.jsonGroupArray()
425425
)
426426
}
427427
```
428428

429-
This allows you to fetch all of the data in a single SQLite query and decode the data into a
430-
collection of `Row` values. There is an extra cost associated with decoding the JSON object,
429+
This allows you to fetch all of the data in a single SQLite query and decode the data into a
430+
collection of `Row` values. There is an extra cost associated with decoding the JSON object,
431431
but that cost may be smaller than executing multiple SQLite requests and transforming the data
432432
into `Row` manually, not to mention the additional code you need to write and maintain to process
433433
the data.

Sources/StructuredQueriesCore/SQLite/JSONFunctions.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ extension PrimaryKeyedTableDefinition where QueryValue: Codable & Sendable {
5959
/// .select {
6060
/// Row.Columns(
6161
/// remindersList: $0,
62-
/// reminders: $1.jsonGroupArray
62+
/// reminders: $1.jsonGroupArray()
6363
/// )
6464
/// }
6565
/// ```
@@ -85,13 +85,15 @@ extension PrimaryKeyedTableDefinition where QueryValue: Codable & Sendable {
8585
/// }
8686
///
8787
/// > Note: If the primary key of the row is NULL, then the object is omitted from the array.
88-
public var jsonGroupArray: some QueryExpression<JSONRepresentation<[QueryValue]>> {
89-
SQLQueryExpression(
90-
"json_group_array(\(jsonObject)) filter(where \(self.primaryKey.isNot(nil)))"
91-
)
88+
public func jsonGroupArray(
89+
order: (some QueryExpression)? = Bool?.none,
90+
filter: (some QueryExpression<Bool>)? = Bool?.none
91+
) -> some QueryExpression<JSONRepresentation<[QueryValue]>> {
92+
let filter = filter.map { primaryKey.isNot(nil) && $0 } ?? primaryKey.isNot(nil)
93+
return jsonObject.jsonGroupArray(order: order, filter: filter)
9294
}
9395

94-
private var jsonObject: some QueryExpression<JSONRepresentation<QueryValue>> {
96+
private var jsonObject: some QueryExpression<QueryValue> {
9597
func open<TableColumn: TableColumnExpression>(_ column: TableColumn) -> QueryFragment {
9698
switch TableColumn.QueryValue.self {
9799
case is Bool.Type:

Tests/StructuredQueriesTests/JSONFunctionsTests.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,21 +112,21 @@ extension SnapshotTests {
112112
ReminderRow.Columns(
113113
assignedUser: user,
114114
reminder: reminder,
115-
tags: #sql("\(tag.jsonGroupArray)")
115+
tags: #sql("\(tag.jsonGroupArray())")
116116
)
117117
}
118118
.limit(2)
119119
) {
120120
"""
121-
SELECT "users"."id", "users"."name" AS "assignedUser", "reminders"."id", "reminders"."assignedUserID", "reminders"."dueDate", "reminders"."isCompleted", "reminders"."isFlagged", "reminders"."notes", "reminders"."priority", "reminders"."remindersListID", "reminders"."title" AS "reminder", json_group_array(iif(("tags"."id" IS NULL), NULL, json_object('id', json_quote("tags"."id"), 'name', json_quote("tags"."name")))) filter(where ("tags"."id" IS NOT NULL)) AS "tags"
121+
SELECT "users"."id", "users"."name" AS "assignedUser", "reminders"."id", "reminders"."assignedUserID", "reminders"."dueDate", "reminders"."isCompleted", "reminders"."isFlagged", "reminders"."notes", "reminders"."priority", "reminders"."remindersListID", "reminders"."title" AS "reminder", json_group_array(iif(("tags"."id" IS NULL), NULL, json_object('id', json_quote("tags"."id"), 'name', json_quote("tags"."name")))) FILTER (WHERE coalesce(NULL, ("tags"."id" IS NOT NULL))) AS "tags"
122122
FROM "reminders"
123123
LEFT JOIN "reminderTags" ON ("reminders"."id" = "reminderTags"."reminderID")
124124
LEFT JOIN "tags" ON ("reminderTags"."tagID" = "tags"."id")
125125
LEFT JOIN "users" ON ("reminders"."assignedUserID" = "users"."id")
126126
GROUP BY "reminders"."id"
127127
LIMIT 2
128128
"""
129-
}results: {
129+
} results: {
130130
"""
131131
┌──────────────────────────────────────────────┐
132132
│ ReminderRow( │
@@ -194,20 +194,20 @@ extension SnapshotTests {
194194
.select { remindersList, reminder in
195195
RemindersListRow.Columns(
196196
remindersList: remindersList,
197-
reminders: #sql("\(reminder.jsonGroupArray)")
197+
reminders: #sql("\(reminder.jsonGroupArray())")
198198
)
199199
}
200200
.limit(1)
201201
) {
202202
"""
203-
SELECT "remindersLists"."id", "remindersLists"."color", "remindersLists"."name" AS "remindersList", json_group_array(iif(("reminders"."id" IS NULL), NULL, json_object('id', json_quote("reminders"."id"), 'assignedUserID', json_quote("reminders"."assignedUserID"), 'dueDate', json_quote("reminders"."dueDate"), 'isCompleted', iif("reminders"."isCompleted" = 0, json('false'), json('true')), 'isFlagged', iif("reminders"."isFlagged" = 0, json('false'), json('true')), 'notes', json_quote("reminders"."notes"), 'priority', json_quote("reminders"."priority"), 'remindersListID', json_quote("reminders"."remindersListID"), 'title', json_quote("reminders"."title")))) filter(where ("reminders"."id" IS NOT NULL)) AS "reminders"
203+
SELECT "remindersLists"."id", "remindersLists"."color", "remindersLists"."name" AS "remindersList", json_group_array(iif(("reminders"."id" IS NULL), NULL, json_object('id', json_quote("reminders"."id"), 'assignedUserID', json_quote("reminders"."assignedUserID"), 'dueDate', json_quote("reminders"."dueDate"), 'isCompleted', iif("reminders"."isCompleted" = 0, json('false'), json('true')), 'isFlagged', iif("reminders"."isFlagged" = 0, json('false'), json('true')), 'notes', json_quote("reminders"."notes"), 'priority', json_quote("reminders"."priority"), 'remindersListID', json_quote("reminders"."remindersListID"), 'title', json_quote("reminders"."title")))) FILTER (WHERE coalesce(NULL, ("reminders"."id" IS NOT NULL))) AS "reminders"
204204
FROM "remindersLists"
205205
LEFT JOIN "reminders" ON ("remindersLists"."id" = "reminders"."remindersListID")
206206
WHERE NOT ("reminders"."isCompleted")
207207
GROUP BY "remindersLists"."id"
208208
LIMIT 1
209209
"""
210-
}results: {
210+
} results: {
211211
"""
212212
┌────────────────────────────────────────────────┐
213213
│ RemindersListRow( │

0 commit comments

Comments
 (0)