-
Notifications
You must be signed in to change notification settings - Fork 326
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?
doc: add dynamic filtering examples #1128
Conversation
Added examples for filtering and combining conditions in SQL queries using JavaScript.
Use a combination of `sql()` and ` sql`` ` to filter against JSON(B): | ||
|
||
```js | ||
const filter = someCondition |
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 guess the condition is not required to illustrate this method of accessing a property in JSONB, and distracts from the example
- Maybe you can try inlining the SQL in the interpolation, rather than creating another variable - this would probably also make it easier to follow
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
const field = someCondition
? 'notifications'
: '2FA'
await sql`
select * from users
where ${sql`${sql(field)}->>'email' = TRUE`};
`
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:
const field = 'notifications';
const subField = 'email';
await sql`
select * from users
where ${sql`${sql(field)}->>${sql(subField)} = TRUE`};
`
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 be sql`subField`
), but otherwise, sure, that looks fine to me :)
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.
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Improve the clarity of the reduce mechanics
where ${conditions.reduce((clause, condition) => sql`${clause} AND ${condition}`)}; | |
where ${conditions.reduce((accumulatedCondition, condition) => sql`${accumulatedCondition} AND ${condition}`)}; |
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 this just makes it wordier
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.
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 clause
(which doesn't seem expressive enough to me), open to suggestions. I looked at 10 alternatives to clause
, and this is the one that looked the best.
When I read clause
:
- I didn't immediately understand how this was being used in your reduce example
- it also made me think "why is it now called
clause
, without any reference tocondition
anymore? everything else was calledcondition
? what is the difference between a clause and a condition?"
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 used clause
because that's its official name in SQL. A quick search for "sql clause" gives 10 near-identical and succinct definitions. From DuckDuckGo's search assistant:
A SQL clause is a part of a SQL statement that specifies a particular condition or action, such as filtering data with the
WHERE
clause or sorting results with theORDER BY
clause.
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 comment
The 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
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 comment
The 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
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`: | |
To dynamically combine multiple conditions, use [`Array.prototype.reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce): |
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 disagree since this is what almost everyone will try, and it mysteriously fails despite looking perfectly fine in the output from postgres.js's debug
. That nonsense cost me 1.5 days of wtf, and you yourself did not realise it was the problem 😜
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.
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 comment
The 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 join
doesn't work and reduce
does.
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 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, cc @porsager your decision here.
My perspective is that the docs should not list all (or any) of the things that do not work
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.
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 sql.join(sql` and `, fragments)
. I'll follow up there too to get it moving
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.
Mm, I agree. I was thinking of this as an interim solution (and caveat) since sql.join()
isn't available yet—I would much prefer sql.join()
🙂
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 comment
The 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 sql.join(..., sql` or `)
.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
I have also been thinking of using between
instead to prevent the ambiguity around js join and sql join. That would also reverse the yodaness you mention.. But I agree that join is the goto keyword someone would think of so we'd loose that..
sql` and `.between(...)
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.
Seems pretty good, thanks for the docs addition!
I added some small notes above.
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 comment
The 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 debug
. That nonsense cost me 1.5 days of wtf, and you yourself did not realise it was the problem 😜
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
I think this just makes it wordier
Use a combination of `sql()` and ` sql`` ` to filter against JSON(B): | ||
|
||
```js | ||
const filter = someCondition |
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
const field = someCondition
? 'notifications'
: '2FA'
await sql`
select * from users
where ${sql`${sql(field)}->>'email' = TRUE`};
`
Co-authored-by: Karl Horky <[email protected]>
Added examples for filtering and combining conditions in SQL queries.
Resolves #1126