Knex 是很優秀的 SQL Query Builder,唯 Knex 是以 Method Chaining 設計,是否有機會以 Function Pipeline 使用 Knex 呢 ?
Version
Knex 0.20.13
SQL
SELECT count(*)
FROM books
實務上常使用 count(*)
得知筆數。
Knex
import knex from 'knex'
import { log, tug } from 'wink-fp'
mySQL ('books')
.count ({ count: '*'})
.then (tug ('count'))
.then (log)
Knex 會使用 count
,再搭配 Wink-fp 的 tug
取出結果。
我們可發現 Knex 的
count
回傳 promise,而tug
為 synchronous function,因此需要用then
隔開
Functional
import knex from 'knex'
import { pipeP, tug, log } from 'wink-fp'
let count = (...v) => k => k.count (...v)
pipeP (
count ({ count:'*' }),
tug ('count'),
log
) (mySQL ('books'))
若將 count
包成 function,就可使用 pipeP
將 Knex 的 count
與 synchronous 的 tug
同時包在一起,不必再使用 then
隔開。
SQL
SELECT title, price
FROM books
SELECT
也為 SQL 基本應用。
Knex
import knex from 'knex'
import { map } from 'ramda'
import { format, log } from 'wink-fp'
mySQL ('books')
.select ('title', 'price')
.then (map (format ('{title}: {price}')))
.then (log)
Knex 會使用 select
,再搭配 Ramda 的 map
與 Wink-fp 的 format
整理出想要的格式。
我們可發現 Knex 的
select
回傳 promise,而map
為 synchronous function,因此需要用then
隔開
Functional
import knex from 'knex'
import { map } from 'ramda'
import { format, log, pipeP } from 'wink-fp'
let select = (...v) => k => k.select(...v)
pipeP (
select ('title', 'price'),
map (format ('{title}: {price}')),
log
) (mySQL ('books')
若將 select
包成 function,就可使用 pipeP
將 Knex 的 select
與 synchronous 的 map
同時包在一起,不必再使用 then
隔開。
SQL
SELECT title, price
FROM books
WHERE price = 100
SELECT
+ WHERE
為 SQL 最常見應用。
Knex
import knex from 'knex'
import { map } from 'ramda'
import { format, pipeP, log } from 'wink-fp'
mySQL ('books')
.select ('title', 'price')
.where ({ price: 100 })
.then (map (format ('{title}: {price}')))
.then (log)
Knex 會使用 select
與 where
,再搭配 Ramda 的 map
與 Wink-fp 的 format
整理出想要的格式。
Functional
import knex from 'knex'
import { map, pipe } from 'ramda'
import { format, log, pipeP } from 'wink-fp'
let select = (...v) => k => k.select (...v)
let where = (...v) => k => k.where (...v)
pipeP (pipe (
select ('title', 'price'),
where ({ price: 100 })),
map (format ('{title}: {price}')),
log
) (mySQL ('books'))
第 5 行
let select = (...v) => k => k.select (...v)
let where = (...v) => k => k.where (...v)
先將 select
與 where
包成 function。
第 8 行
pipeP (pipe (
select ('title', 'price'),
where ({ price: 100 })),
map (format ('{title}: {price}')),
log
) (mySQL ('books'))
若使用兩個以上的 Knex function,則要先使用 pipe
先組合,再搭配 pipeP
組合其他 synchronous function。
pipeK
import knex from 'knex'
import { map, pipe } from 'ramda'
import { format, log, pipeP } from 'wink-fp'
let select = (...v) => k => k.select (...v)
let where = (...v) => k => k.where (...v)
let pipeK = (...ks) => (...fs) => pipeP (pipe (...ks), ...fs)
pipeK (
select ('title', 'price'),
where ({ price: 100 })
)(
map (format ('{title}: {price}')),
log
) (mySQL ('books'))
當 Knex function 與 synchronous function 混用時,會出現 pipe
與 pipeP
同時混用的窘境,是否有優化空間呢 ?
第 7 行
let pipeK = (...ks) => (...fs) => pipeP (pipe (...ks), ...fs)
將 pipeP
與 pipe
組合成 pipeK
,其中 K
表示 K
nex,表專門適用於 Knex 的 function pipeline 使用。
Argument 分兩族群,第一個 argument 組合 Knex function,第二個 argument 組合 synchronous function。
第 9 行
pipeK (
select ('title', 'price'),
where ({ price: 100 })
)(
map (format ('{title}: {price}')),
log
) (mySQL ('books'))
如此只要使用單獨 pipeK
即可,上面是 Knex function,而下面為 synchronous function。
Conclusion
- 若只使用單一 Knex function,可直接使用
pipeP
組合 Knex function 與其他 function - 若要同時使用多個 Knex function,可使用
pipe
將 Knex function 組合,再使用pipeP
組合其他 synchronous function,亦可直接使用專屬的pipeK
一次解決 - 本文只是牛刀小試將 Knex 的
count
、select
、where
抽成 function,並搭配pipeK
同時組合 Knex function 與 synchronous function,實務上可將這些 function 整理成 library 供日後使用