count
是實務上常見需求,這在 Knex 該如何寫呢 ?
Version
Knex 0.20.2
Knex-fp 0.0.8
SQL
SELECT count(*)
FROM books
count
最常見的應用是只求筆數即可。
Knex
mySQL ('books')
.count ({ count: '*'})
.then (x => x[0].count)
但由於 SQL 是將 count
放在 result set 內,因此必須在 then
中先以 [0]
取得第一筆資料,再使用 count
取得。
Ramda
import { pluck, head } from 'ramda'
mySQL ('books')
.count ({ count: '*'})
.then (pluck ('count'))
.then (head)
也可在 then
中使用 Ramda 產生 pure function,首先 pluck
出 count
field 資料,再使用 head
將 [0]
取出。
import { pluck, head, compose } from 'ramda'
let transform = compose (head, pluck ('count'))
mySQL ('books')
.count ({ count: '*'})
.then (transform)
由於 Promise 支援 composition law,也可將 then
所有 pure function 都使用 pipe
組合出 next
一次交給 then
執行。
Wink-fp
import { tug } from 'wink-fp'
mySQL ('books')
.count ({ count: '*'})
.then (tug('count'))
.then (log)
由於 pluck
與 head
的組合經常使用,Wink-fp 提供了 tug
,只要傳入 count
field 即可。
Knex-fp
import { pipe, andThen as then } from 'ramda'
import { count } from 'knex-fp'
import { pipeP, tug } from 'wink-fp'
pipe (
count ({ count: '*'}),
then (tug ('count'))
) (mySQL ('books'))
也可使用 Knex-fp 的 count
,如此就可使用 pipe
同時組合 Knex-fp 與 Wink-fp 的 function。
SQL
select 'My Bookshelf' as title, count(*) as count
from books
有時我們在使用 count(*)
計算時,還想自己用 String 自訂 field。
Knex
import { map } from 'ramda'
import { format } from 'wink-fp'
mySQL ('books')
.select (knex.raw (`'My Bookshelf' as title`))
.count ({ count: '*'})
.then (map(format('{title}: {count}')))
Knex 有提供 count
不意外,但 Knex 並沒有提供內建方法實現 'My Bookshelf' as title
,必須使用 knex.raw
搭配 raw SQL。
map
為 Ramda 的 function,format
則屬 Wink-fp,由於 Knex 回傳 Promise,這些都必須寫在 .then
內
但 Knex 會 complain Knex.raw is deprecated
,但結果依然正確。
import { map } from 'ramda'
import { format } from 'wink-fp'
mySQL ('books')
.select (mySQL.raw (`'My Bookshelf' as title`))
.count ({ count: '*'})
.then (map (format ('{title}: {count}')))
比較正確方式是改用 Knex instance 的 raw
。
Knex-fp
import { pipe, map } from 'ramda'
import { format } from 'wink-fp'
import { select, count, raw, pipeK } from 'knex-fp'
pipeK (
select (raw (mySQL) (`'My Bookshelf' as title`)),
count ({ count:'*' })
)(
map (format('{title}: {count}'))
) (mySQL ('books'))
也可使用pipeK
同時使用 Knex-fp 的 select
、raw
與 count
組合與 Ramda 的 map
與 Wink-fp 的 format
組合。
Conclusion
- Knex 並沒有提供所有 SQL 功能,有些必須使用 raw SQL
- 由於 Knex 回傳 Promise,可繼續使用 Ramda 以 pure function 處理
- Promise 支援 composition law,也可將所有 pure function 組合後一次傳給
then
pluck
與head
組合經常使用,可改用 Wink-fp 的 `tug 只要一個 function 即可- 也可使用 Knex-fp 的
count
、select
與raw
,如此可使用pipeK
組合 Knex-fp 與 Ramda、Wink-fp 的 function