點燈坊

失くすものさえない今が強くなるチャンスよ

使用 Builder 動態組合 SQL

Sam Xiao's Avatar 2021-10-22

Knex 的 where 也可使用 Builder,藉此我們可動態組合 SQL。

Version

Knex 0.20.13
Knex-fp 0.0.10

Knex

import { log } from 'wink-fp'

let wherePrice = true
let whereTitle = true

mySQL ('books')
  .where (x => {
    if (wherePrice) x.where ('price', '>', 100)
    if (whereTitle) x.where ('title', 'like', '%JavaScript%')
    return x
  })
  .then (log)

Knex 的 where 非常靈活,能傳入 string,也能傳入 function。

若傳入 function 則視為 builder 使用,可根據條件動態組合 SQL。

Extract Function

import { log } from 'wink-fp'

let wherePrice = true
let whereTitle = true

let buildSQL = x => {
  if (wherePrice) x.where('price', '>', 100)
  if (whereTitle) x.where('title', 'like', '%JavaScript%')
  return x
}

mySQL ('books')
  .where (buildSQL)
  .then (log)

可將 where 的 callback 抽成 named function 增加可讀性。

Higher Order Function

import { pipe } from 'ramda'
import { log } from 'wink-fp'

let wherePrice = true
let whereTitle = true

let genPrice = v => x => v ? x.where ('price', '>', 100) : x
let genTitle = v => x => v ? x.where ('title', 'like', '%JavaScript%') : x

let buildSQL = pipe (
  genPrice (wherePrice),
  genTitle (whereTitle)
)

mySQL ('books')
  .where (buildSQL)
  .then (log)

可將動態組合 SQL 部分抽成 genPricegenTitle higher order function,buildSQL 改以 pipe 組合,如此寫法可讀性更高,且也更容易重複使用。

Knex-fp

import { pipeK, where } from 'knex-fp'
import { identity } from 'ramda'
import { log } from 'wink-fp'

let wherePrice = true
let whereTitle = true

let genPrice = v => v ? where ('price', '>', 100) : identity
let genTitle = v => v ? where ('title', 'like', '%JavaScript%') : identity

let buildSQL = pipe (
  genPrice (wherePrice),
  genTitle (whereTitle)
)

pipeK (
  where (buildSQL)
) (log) (mySQL ('books'))

也可使用 Knex-fp 的 pipeKwhere 組合。

import { pipeK, where } from 'knex-fp'
import { identity } from 'ramda'
import { log } from 'wink-fp'

let wherePrice = true
let whereTitle = true

let genPrice = v => v ? where ('price', '>', 100) : identity
let genTitle = v => v ? where ('title', 'like', '%JavaScript%') : identity

pipeK (
  genPrice (wherePrice),
  genTitle (whereTitle)
) (log) (mySQL ('books'))

事實上若使用 Knex-fp,連 buildSQL 也不需要了,直接將 genPricegenTitle 寫在 pipeK 即可,這就是 Function Pipeline 強悍之處。

builder000

Conclusion

  • Knex 因為是 Method Chaining,所以特別在 where 提供 builder 動態建立 SQL,若使用 Knex-fp,因為 Function Pipeline 本身就是組合,可直接取代 builder