-
Notifications
You must be signed in to change notification settings - Fork 325
doc: add dynamic filtering examples #1128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -342,6 +342,37 @@ select * from users | |||||
select * from users where user_id = $1 | ||||||
``` | ||||||
|
||||||
Use a combination of `sql()` and ` sql`` ` to access fields in `json` or `jsonb`: | ||||||
|
||||||
```js | ||||||
const filter = someCondition | ||||||
? sql`${sql('notifications')}->>'email' = TRUE` | ||||||
: sql`${sql('2FA')}->>'email' = TRUE`; | ||||||
|
||||||
await sql` | ||||||
select * from users | ||||||
where ${filter}; | ||||||
` | ||||||
|
||||||
// Which results in: | ||||||
select * from users where "notifications"->>'email' = TRUE; | ||||||
// OR | ||||||
select * from users where "2FA"->>'email' = TRUE; | ||||||
``` | ||||||
|
||||||
To dynamically combine multiple conditions, use [Array `reduce`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce); [Array `join`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join) does not work because its output is a string (an interpolated value) instead of a `Fragment`: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need to exhaustively list the things that don't work
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I disagree since this is what almost everyone will try, and it mysteriously fails despite looking perfectly fine in the output from postgres.js's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately, others may try something else - we won't be able to cover all possible incorrect cases / methods, and it blows up the size of the docs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They may well try something else. This will catch the vast majority and also, most importantly, explains why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think either of us can decide what the majority would be - I disagree with having these notes in the docs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks a lot for the improvements. I think an addition of something like this to the docs is a smell because the real fix is to add a proper solution for joining fragments. My wish for the join method is for it to look like this: sql` and `.join(...) The current pr to add join is close, but tests aren't passing and the solution is too verbose There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mm, I agree. I was thinking of this as an interim solution (and caveat) since That syntax though initially looks like Yoda 🤪 I think something like this would be much more intuitive: await sql`
SELECT * FROM "Example"
WHERE ${sql.join(
[
sql`foo = ${foo}`,
sql`bar = ${bar}`,
],
'OR', // default to AND?
)};
` There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haha :P Yeah I can see your point. The trouble is that we need to use a fragment for the joiner if we want to keep the safety. That makes yours and the PRs example into Your sample also adds another question. Do we just willy nilly add whitespace on each side of the join string? That would probably help after few, but also be annoying for someone where the whitespace breaks some intended functionality There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have also been thinking of using
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I guarantee you requiring users to include whitespace around the joiner arg will trip up almost, if not every, user 😬
I'm ~95% sure extra whitespace does not create a problem—AFAIK, SQL collapses it under the hood. You can even do something like
Six of one, half a dozen of the other: SQL also has I'm currently migrating from await sql`
UPDATE "Example"
SET ${sql({ foo: 1 })}
WHERE ${sql({ bar: 2 })};
`
Buuut, on the flip-side, P.S. Whatever the ultimate solution, please don't make it a promise—that makes debugging such a headache. |
||||||
|
||||||
```js | ||||||
const conditions = [] | ||||||
|
||||||
if (foo) conditions.push(sql`foo = ${foo}`) | ||||||
|
||||||
await sql` | ||||||
select * from users | ||||||
where ${conditions.reduce((clause, condition) => sql`${clause} AND ${condition}`)}; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Improve the clarity of the reduce mechanics
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this just makes it wordier There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Long variable names are good to improve comprehension and expressiveness - see as an reference some of the variables that Facebook/Meta uses in various React codebases But if you have a shorter (non-abbreviated) alternative to When I read
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used
That's exactly what this is, and it even cites the specific filter operator used in this example. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think introducing the new terminology makes it worse. My opinion would be to change it, for the reasons I already expressed above |
||||||
` | ||||||
``` | ||||||
|
||||||
### Dynamic ordering | ||||||
|
||||||
```js | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kept it separated because that is likely how it would be written IRL, and because this library is so finicky with string templates and what utils are needed when (which is literally the problem necessitating this example).
Perhaps a better way to write it would be
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What my point is: the ternary is not needed for dynamic field access and introduces some business logic in the example which is only a distraction
But maybe keeping 2 variables would be illustrative, to show that both can be dynamic:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that
sql(subField)
is actually wrong (I think it needs to besql`subField`
), but otherwise, sure, that looks fine to me :)Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, try it out to confirm that it works - I thought that would be the syntax but maybe I'm wrong.