Skip to content

Conversation

stephencelis
Copy link
Member

@stephencelis stephencelis commented Jun 2, 2025

This PR weakens query builder predicates (where and having) to optional booleans. This allows some expressions to avoid extra coalescing:

 Reminder.where {
-  $0.dueDate.map { ($0 < Date()) ?? false }
+  $0.dueDate.map { $0 < Date() }
 }

  * Predicates (`where` and `having`) can return optional booleans.
  * Add `QueryExpression<Optional>.map` for optionally building queries
    on non-optional values.

For example, a comparison that might use the `#sql` macro as an escape
hatch can more safely and succinctly use `map`:

```diff
 Reminder.where {
-  #sql("\($0.dueDate) < \(Date())")
+  $0.dueDate.map { $0 < Date() }
 }
```
@stephencelis stephencelis requested a review from mbrandonw June 2, 2025 06:12
stephencelis added a commit that referenced this pull request Jun 16, 2025
This PR adds helpers that make it a little easier to work with optional
query expressions in a builder.

For example, if you want to execute a `LIKE` operator on an optional
string, you currently have to resort to one of the following
workarounds:

```swift
.where { ($0.title ?? "").like("%foo%") }
// or:
.where { #sql("\($0.title) LIKE '%foo%') }
```

This PR introduces `map` and `flatMap` operations on optional
`QueryExpression`s that unwraps the expression, giving you additional
flexibility in how you express your builder code:

```swift
.where { $0.title.map { $0.like("%foo%") } ?? false }
```

While this is more code than the above options, some may prefer its
readability, and should we merge the other optional helpers from #61, it
could be further shortened:

```swift
.where { $0.title.map { $0.like("%foo%") } }
```
stephencelis added a commit that referenced this pull request Jun 16, 2025
* Add `QueryExpression<Optional>.map,flatMap`

This PR adds helpers that make it a little easier to work with optional
query expressions in a builder.

For example, if you want to execute a `LIKE` operator on an optional
string, you currently have to resort to one of the following
workarounds:

```swift
.where { ($0.title ?? "").like("%foo%") }
// or:
.where { #sql("\($0.title) LIKE '%foo%') }
```

This PR introduces `map` and `flatMap` operations on optional
`QueryExpression`s that unwraps the expression, giving you additional
flexibility in how you express your builder code:

```swift
.where { $0.title.map { $0.like("%foo%") } ?? false }
```

While this is more code than the above options, some may prefer its
readability, and should we merge the other optional helpers from #61, it
could be further shortened:

```swift
.where { $0.title.map { $0.like("%foo%") } }
```

* tests
stephencelis added a commit that referenced this pull request Jun 16, 2025
* Add `QueryExpression<Optional>.map,flatMap`

This PR adds helpers that make it a little easier to work with optional
query expressions in a builder.

For example, if you want to execute a `LIKE` operator on an optional
string, you currently have to resort to one of the following
workarounds:

```swift
.where { ($0.title ?? "").like("%foo%") }
// or:
.where { #sql("\($0.title) LIKE '%foo%') }
```

This PR introduces `map` and `flatMap` operations on optional
`QueryExpression`s that unwraps the expression, giving you additional
flexibility in how you express your builder code:

```swift
.where { $0.title.map { $0.like("%foo%") } ?? false }
```

While this is more code than the above options, some may prefer its
readability, and should we merge the other optional helpers from #61, it
could be further shortened:

```swift
.where { $0.title.map { $0.like("%foo%") } }
```

* tests
@stephencelis stephencelis merged commit 6bd8700 into main Jun 17, 2025
3 checks passed
@stephencelis stephencelis deleted the optional-helpers branch June 17, 2025 20:10
stephencelis added a commit that referenced this pull request Jun 23, 2025
* Temporary triggers

* wip

* touch triggers

* fix

* wip

* wip

* wip

* wip

* wip

* Remove trailing comma (#75)

* Remove trailing comma while we support Swift 6.0

* compile for swift 6.0

* Don't require decodable fields in `GROUP BY` (#79)

This PR allows the following to work without qualifying the expression
type:

```diff
 Reminder.group {
-  #sql("date(\($0.dueDate))", as: Date?.self)
+  #sql("date(\($0.dueDate))")
 }
```

* Add `QueryExpression<Optional>.map,flatMap` (#80)

* Add `QueryExpression<Optional>.map,flatMap`

This PR adds helpers that make it a little easier to work with optional
query expressions in a builder.

For example, if you want to execute a `LIKE` operator on an optional
string, you currently have to resort to one of the following
workarounds:

```swift
.where { ($0.title ?? "").like("%foo%") }
// or:
.where { #sql("\($0.title) LIKE '%foo%') }
```

This PR introduces `map` and `flatMap` operations on optional
`QueryExpression`s that unwraps the expression, giving you additional
flexibility in how you express your builder code:

```swift
.where { $0.title.map { $0.like("%foo%") } ?? false }
```

While this is more code than the above options, some may prefer its
readability, and should we merge the other optional helpers from #61, it
could be further shortened:

```swift
.where { $0.title.map { $0.like("%foo%") } }
```

* tests

* wip

* wip

* wip

* wip

* wip

* find update remove later

* Revert "find update remove later"

This reverts commit a3de95c.

* wip

* wip

* wip

* more overloads

* wip

* wip

* wip

* Support multiple statements in triggers

* Reuse query fragment builder

* wip

* wip

* wip

* wip

---------

Co-authored-by: Brandon Williams <[email protected]>
Co-authored-by: Brandon Williams <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants