Skip to content

Commit 047856b

Browse files
committed
Publicize jsonObject helper
1 parent 2d07e03 commit 047856b

File tree

4 files changed

+167
-12
lines changed

4 files changed

+167
-12
lines changed

Sources/StructuredQueriesCore/SQLite/JSONFunctions.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,15 @@ extension PrimaryKeyedTableDefinition where QueryValue: _OptionalProtocol & Coda
188188
return AggregateFunction(
189189
"json_group_array",
190190
isDistinct: isDistinct,
191-
[jsonObject.queryFragment],
191+
[QueryValue.columns.jsonObject.queryFragment],
192192
order: order?.queryFragment,
193193
filter: filterQueryFragment
194194
)
195195
}
196196
}
197197

198-
extension PrimaryKeyedTableDefinition {
199-
200-
fileprivate var jsonObject: some QueryExpression<QueryValue> {
198+
extension TableDefinition {
199+
public var jsonObject: some QueryExpression<QueryValue> {
201200
func open<TableColumn: TableColumnExpression>(_ column: TableColumn) -> QueryFragment {
202201
typealias Value = TableColumn.QueryValue._Optionalized.Wrapped
203202

@@ -240,8 +239,12 @@ extension PrimaryKeyedTableDefinition {
240239
let fragment: QueryFragment = Self.allColumns
241240
.map { open($0) }
242241
.joined(separator: ", ")
243-
return SQLQueryExpression(
244-
"CASE WHEN \(primaryKey.isNot(nil)) THEN json_object(\(fragment)) END"
245-
)
242+
return QueryFunction("json_object", SQLQueryExpression(fragment))
243+
}
244+
}
245+
246+
extension Optional.TableColumns {
247+
public var jsonObject: some QueryExpression<QueryValue> {
248+
Case().when(rowid.isNot(nil), then: Wrapped.columns.jsonObject)
246249
}
247250
}

Tests/StructuredQueriesTests/JSONFunctionsTests.swift

Lines changed: 154 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ extension SnapshotTests {
142142
.limit(2)
143143
) {
144144
"""
145-
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", "reminders"."updatedAt" AS "reminder", json_group_array(CASE WHEN ("tags"."id" IS NOT NULL) THEN json_object('id', json_quote("tags"."id"), 'title', json_quote("tags"."title")) END) FILTER (WHERE ("tags"."id" IS NOT NULL)) AS "tags"
145+
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", "reminders"."updatedAt" AS "reminder", json_group_array(CASE WHEN ("tags"."rowid" IS NOT NULL) THEN json_object('id', json_quote("tags"."id"), 'title', json_quote("tags"."title")) END) FILTER (WHERE ("tags"."id" IS NOT NULL)) AS "tags"
146146
FROM "reminders"
147147
LEFT JOIN "remindersTags" ON ("reminders"."id" = "remindersTags"."reminderID")
148148
LEFT JOIN "tags" ON ("remindersTags"."tagID" = "tags"."id")
@@ -228,7 +228,7 @@ extension SnapshotTests {
228228
.limit(1)
229229
) {
230230
"""
231-
SELECT "remindersLists"."id", "remindersLists"."color", "remindersLists"."title", "remindersLists"."position" AS "remindersList", json_group_array(DISTINCT CASE WHEN ("milestones"."id" IS NOT NULL) THEN json_object('id', json_quote("milestones"."id"), 'remindersListID', json_quote("milestones"."remindersListID"), 'title', json_quote("milestones"."title")) END) FILTER (WHERE ("milestones"."id" IS NOT NULL)) AS "milestones", json_group_array(DISTINCT CASE WHEN ("reminders"."id" IS NOT NULL) THEN json_object('id', json_quote("reminders"."id"), 'assignedUserID', json_quote("reminders"."assignedUserID"), 'dueDate', json_quote("reminders"."dueDate"), 'isCompleted', json(CASE "reminders"."isCompleted" WHEN 0 THEN 'false' WHEN 1 THEN 'true' END), 'isFlagged', json(CASE "reminders"."isFlagged" WHEN 0 THEN 'false' WHEN 1 THEN 'true' END), 'notes', json_quote("reminders"."notes"), 'priority', json_quote("reminders"."priority"), 'remindersListID', json_quote("reminders"."remindersListID"), 'title', json_quote("reminders"."title"), 'updatedAt', json_quote("reminders"."updatedAt")) END) FILTER (WHERE ("reminders"."id" IS NOT NULL)) AS "reminders"
231+
SELECT "remindersLists"."id", "remindersLists"."color", "remindersLists"."title", "remindersLists"."position" AS "remindersList", json_group_array(DISTINCT CASE WHEN ("milestones"."rowid" IS NOT NULL) THEN json_object('id', json_quote("milestones"."id"), 'remindersListID', json_quote("milestones"."remindersListID"), 'title', json_quote("milestones"."title")) END) FILTER (WHERE ("milestones"."id" IS NOT NULL)) AS "milestones", json_group_array(DISTINCT CASE WHEN ("reminders"."rowid" IS NOT NULL) THEN json_object('id', json_quote("reminders"."id"), 'assignedUserID', json_quote("reminders"."assignedUserID"), 'dueDate', json_quote("reminders"."dueDate"), 'isCompleted', json(CASE "reminders"."isCompleted" WHEN 0 THEN 'false' WHEN 1 THEN 'true' END), 'isFlagged', json(CASE "reminders"."isFlagged" WHEN 0 THEN 'false' WHEN 1 THEN 'true' END), 'notes', json_quote("reminders"."notes"), 'priority', json_quote("reminders"."priority"), 'remindersListID', json_quote("reminders"."remindersListID"), 'title', json_quote("reminders"."title"), 'updatedAt', json_quote("reminders"."updatedAt")) END) FILTER (WHERE ("reminders"."id" IS NOT NULL)) AS "reminders"
232232
FROM "remindersLists"
233233
LEFT JOIN "milestones" ON ("remindersLists"."id" = "milestones"."remindersListID")
234234
LEFT JOIN "reminders" ON ("remindersLists"."id" = "reminders"."remindersListID")
@@ -339,6 +339,158 @@ extension SnapshotTests {
339339
"""
340340
}
341341
}
342+
343+
@Test func jsonObject() {
344+
assertQuery(
345+
Reminder
346+
.join(RemindersList.all) { $0.remindersListID.eq($1.id) }
347+
.select { ($0, $1.jsonObject) }
348+
) {
349+
"""
350+
SELECT "reminders"."id", "reminders"."assignedUserID", "reminders"."dueDate", "reminders"."isCompleted", "reminders"."isFlagged", "reminders"."notes", "reminders"."priority", "reminders"."remindersListID", "reminders"."title", "reminders"."updatedAt", json_object('id', json_quote("remindersLists"."id"), 'color', json_quote("remindersLists"."color"), 'title', json_quote("remindersLists"."title"), 'position', json_quote("remindersLists"."position"))
351+
FROM "reminders"
352+
JOIN "remindersLists" ON ("reminders"."remindersListID" = "remindersLists"."id")
353+
"""
354+
} results: {
355+
#"""
356+
┌─────────────────────────────────────────────┬───────────────────┐
357+
│ Reminder( │ RemindersList( │
358+
│ id: 1, │ id: 0, │
359+
│ assignedUserID: 1, │ color: 4889071, │
360+
│ dueDate: Date(2001-01-01T00:00:00.000Z), │ title: "", │
361+
│ isCompleted: false, │ position: 0 │
362+
│ isFlagged: false, │ ) │
363+
│ notes: "Milk, Eggs, Apples", │ │
364+
│ priority: nil, │ │
365+
│ remindersListID: 1, │ │
366+
│ title: "Groceries", │ │
367+
│ updatedAt: Date(2040-02-14T23:31:30.000Z) │ │
368+
│ ) │ │
369+
├─────────────────────────────────────────────┼───────────────────┤
370+
│ Reminder( │ RemindersList( │
371+
│ id: 2, │ id: 0, │
372+
│ assignedUserID: nil, │ color: 4889071, │
373+
│ dueDate: Date(2000-12-30T00:00:00.000Z), │ title: "", │
374+
│ isCompleted: false, │ position: 0 │
375+
│ isFlagged: true, │ ) │
376+
│ notes: "", │ │
377+
│ priority: nil, │ │
378+
│ remindersListID: 1, │ │
379+
│ title: "Haircut", │ │
380+
│ updatedAt: Date(2040-02-14T23:31:30.000Z) │ │
381+
│ ) │ │
382+
├─────────────────────────────────────────────┼───────────────────┤
383+
│ Reminder( │ RemindersList( │
384+
│ id: 3, │ id: 0, │
385+
│ assignedUserID: nil, │ color: 4889071, │
386+
│ dueDate: Date(2001-01-01T00:00:00.000Z), │ title: "", │
387+
│ isCompleted: false, │ position: 0 │
388+
│ isFlagged: false, │ ) │
389+
│ notes: "Ask about diet", │ │
390+
│ priority: .high, │ │
391+
│ remindersListID: 1, │ │
392+
│ title: "Doctor appointment", │ │
393+
│ updatedAt: Date(2040-02-14T23:31:30.000Z) │ │
394+
│ ) │ │
395+
├─────────────────────────────────────────────┼───────────────────┤
396+
│ Reminder( │ RemindersList( │
397+
│ id: 4, │ id: 0, │
398+
│ assignedUserID: nil, │ color: 4889071, │
399+
│ dueDate: Date(2000-06-25T00:00:00.000Z), │ title: "", │
400+
│ isCompleted: true, │ position: 0 │
401+
│ isFlagged: false, │ ) │
402+
│ notes: "", │ │
403+
│ priority: nil, │ │
404+
│ remindersListID: 1, │ │
405+
│ title: "Take a walk", │ │
406+
│ updatedAt: Date(2040-02-14T23:31:30.000Z) │ │
407+
│ ) │ │
408+
├─────────────────────────────────────────────┼───────────────────┤
409+
│ Reminder( │ RemindersList( │
410+
│ id: 5, │ id: 0, │
411+
│ assignedUserID: nil, │ color: 4889071, │
412+
│ dueDate: nil, │ title: "", │
413+
│ isCompleted: false, │ position: 0 │
414+
│ isFlagged: false, │ ) │
415+
│ notes: "", │ │
416+
│ priority: nil, │ │
417+
│ remindersListID: 1, │ │
418+
│ title: "Buy concert tickets", │ │
419+
│ updatedAt: Date(2040-02-14T23:31:30.000Z) │ │
420+
│ ) │ │
421+
├─────────────────────────────────────────────┼───────────────────┤
422+
│ Reminder( │ RemindersList( │
423+
│ id: 6, │ id: 0, │
424+
│ assignedUserID: nil, │ color: 4889071, │
425+
│ dueDate: Date(2001-01-03T00:00:00.000Z), │ title: "", │
426+
│ isCompleted: false, │ position: 0 │
427+
│ isFlagged: true, │ ) │
428+
│ notes: "", │ │
429+
│ priority: .high, │ │
430+
│ remindersListID: 2, │ │
431+
│ title: "Pick up kids from school", │ │
432+
│ updatedAt: Date(2040-02-14T23:31:30.000Z) │ │
433+
│ ) │ │
434+
├─────────────────────────────────────────────┼───────────────────┤
435+
│ Reminder( │ RemindersList( │
436+
│ id: 7, │ id: 0, │
437+
│ assignedUserID: nil, │ color: 4889071, │
438+
│ dueDate: Date(2000-12-30T00:00:00.000Z), │ title: "", │
439+
│ isCompleted: true, │ position: 0 │
440+
│ isFlagged: false, │ ) │
441+
│ notes: "", │ │
442+
│ priority: .low, │ │
443+
│ remindersListID: 2, │ │
444+
│ title: "Get laundry", │ │
445+
│ updatedAt: Date(2040-02-14T23:31:30.000Z) │ │
446+
│ ) │ │
447+
├─────────────────────────────────────────────┼───────────────────┤
448+
│ Reminder( │ RemindersList( │
449+
│ id: 8, │ id: 0, │
450+
│ assignedUserID: nil, │ color: 4889071, │
451+
│ dueDate: Date(2001-01-05T00:00:00.000Z), │ title: "", │
452+
│ isCompleted: false, │ position: 0 │
453+
│ isFlagged: false, │ ) │
454+
│ notes: "", │ │
455+
│ priority: .high, │ │
456+
│ remindersListID: 2, │ │
457+
│ title: "Take out trash", │ │
458+
│ updatedAt: Date(2040-02-14T23:31:30.000Z) │ │
459+
│ ) │ │
460+
├─────────────────────────────────────────────┼───────────────────┤
461+
│ Reminder( │ RemindersList( │
462+
│ id: 9, │ id: 0, │
463+
│ assignedUserID: nil, │ color: 4889071, │
464+
│ dueDate: Date(2001-01-03T00:00:00.000Z), │ title: "", │
465+
│ isCompleted: false, │ position: 0 │
466+
│ isFlagged: false, │ ) │
467+
│ notes: """ │ │
468+
│ Status of tax return │ │
469+
│ Expenses for next year │ │
470+
│ Changing payroll company │ │
471+
│ """, │ │
472+
│ priority: nil, │ │
473+
│ remindersListID: 3, │ │
474+
│ title: "Call accountant", │ │
475+
│ updatedAt: Date(2040-02-14T23:31:30.000Z) │ │
476+
│ ) │ │
477+
├─────────────────────────────────────────────┼───────────────────┤
478+
│ Reminder( │ RemindersList( │
479+
│ id: 10, │ id: 0, │
480+
│ assignedUserID: nil, │ color: 4889071, │
481+
│ dueDate: Date(2000-12-30T00:00:00.000Z), │ title: "", │
482+
│ isCompleted: true, │ position: 0 │
483+
│ isFlagged: false, │ ) │
484+
│ notes: "", │ │
485+
│ priority: .medium, │ │
486+
│ remindersListID: 3, │ │
487+
│ title: "Send weekly emails", │ │
488+
│ updatedAt: Date(2040-02-14T23:31:30.000Z) │ │
489+
│ ) │ │
490+
└─────────────────────────────────────────────┴───────────────────┘
491+
"""#
492+
}
493+
}
342494
}
343495
}
344496

0 commit comments

Comments
 (0)