Ways to simplify dynamic query building
ivan-kleshnin opened this issue · comments
How would you shorten the following kind of code (which appears as soon as you start to build an API and accept filtering conditions via JSON)?
let filter = {
id: {eq: "123"},
username: {neq: "ivan-kleshnin"},
// etc
}
function makeWhere(filter) {
return sql`
WHERE 1 = 1
${
filter.id ? (
sql`AND "account"."id" ${
filter.id.eq ? sql`= ${filter.id.eq}` :
filter.id.neq ? sql`<> ${filter.id.neq}` : sql.empty
}`
) : sql.empty
}
${
filter.username ? (
sql`AND "account"."username" ${
filter.username.eq ? sql`= ${filter.username.eq}` :
filter.username.neq ? sql`<> ${filter.username.neq}` : sql.empty
}`
) : sql.empty
}
`
}
let account = await pg.query(sql`
SELECT * FROM "account"
${makeWhere(filter)}
LIMIT 1
`).firstRow()
For now, I ended up with this:
// TODO escape
function whereTable(tableName, filter) {
// simplified: only string/number values, only two operators
function whereField(fieldName, value) {
return sql`"${sql.raw(tableName)}"."${sql.raw(fieldName)}" ${
value.eq ? sql`= ${value.eq}` :
value.neq ? sql`<> ${value.neq}` : sql.empty
}`
}
return sql`
WHERE 1 = 1
${sql.join(
Object.keys(filter).map(k => sql` AND ${whereField(k, filter[k])}`)
, "")}
`
}
...
let account = await pg.query(sql`
SELECT * FROM "account"
${whereTable("account", filter)}
LIMIT 1
`).firstRow()
It would be possible to get rid of 1 = 1
placeholder if sql.join
accepted sql values as separators:
return sql`
WHERE 1 = 1
${sql.join(
Object.keys(filter).map(k => whereField(k, filter[k]))
, sql` AND `)}
`
if sql.join accepted sql values as separators
It should accept SQL values today, it supports the same thing as any values of the sql
tag. Using your last example, it'd look like this:
join(Object.keys(filter).map(k => whereField(k, filter[k])), " AND ")
You can't put SQL into the join value, only strings.
How would you shorten the following kind of code
You can also do Object.keys(filter).length === 0
return return empty
so you don't have any invalid WHERE
. Additionally, I think join
solves your last issue.
Yeah, right. It works like that. Thank you!