以下の内容はhttps://let.blog.jp/tag/knexより取得しました。


Knex が 1.0 になってた
久々にみたら 1.0 になってた
https://github.com/knex/knex/blob/master/CHANGELOG.md

0.21 だったり 0.95 だったりでずっと 1 を超えなくて 0.X 系バージョニングを採用してるものとして取り上げられてたりもしたけど とうとう 1.0
大きく変わったことを期待したけど変化はそれほどなさそう
0.95 のときのほうが大きかった気がする
そういえば forever のときも大したことなかった気がするし 0.X 系プロジェクトの 1.0 にそこまで大きな意味はないのかも

影響しそうな破壊的変更はこれ
「Changed data structure from RETURNING operation to be consistent with SELECT」

以前書いたこともある returning を使った時の返り値の仕様

select メソッドだと .select("col1", "col2") のように可変長引数
returning は第 2 引数がオプションなので列名の指定は .returning(["col1", "col2"]) のように第 1 引数で配列形式
配列じゃなくて文字列単体で .returning("col1") とも書ける
この場合は ひとつだけなので返り値の配列の中の要素がオブジェクトではなく列の値になってる

await pg("table").insert([{ value: "a" }, { value: "b" }]).returning(["id"])
// [{ id: 1 }, { id: 2 }]

await pg("table").insert([{ value: "a" }, { value: "b" }]).returning("id")
// [1, 2]

慣れないと分かりづらいところはあるけど 引数の型が違うし 一つしか無いのにオブジェクトにする必要もなく合理的と思ってた
returning に id だけを指定してから id の配列にするために

const ids = rows.map(row => row.id)

というのはよく必要になるわけでこの手間をなくせる
だけど文字列でも "*" みたいなのが来ればオブジェクトになるし 気をつけないといけないポイントみたいになってた
それが 1.0 では変更されて常にオブジェクトになるようになった

await pg("table").insert([{ value: "a" }, { value: "b" }]).returning(["id"])
// [{ id: 1 }, { id: 2 }]

await pg("table").insert([{ value: "a" }, { value: "b" }]).returning("id")
// [{ id: 1 }, { id: 2 }]

id だけ指定して id の配列を受け取るようにしてる人は割といるんじゃないかと思うし 影響の大きな変更といえるかも
mysql/mariadb はこの機能がそもそもなかったと思うから影響受けるのは PostgreSQL/Oracle/MSSQL ユーザだけのはず
knex で insert のときだけ空配列にならない
pg("table")
.select("*")
.where("id", 9999)
.then(console.log)
// []

pg("table")
.update({ name: "FOO" })
.where("id", 9999)
.returning("*")
.then(console.log)
// []

pg("table")
.delete()
.where("id", 9999)
.returning("*")
.then(console.log)
// []

select で見つからない場合や update, delete で returning して対象がない場合には空配列

だけど insert で空配列を insert して returning した場合は

pg("table")
.insert([])
.returning("*")
.then(console.log)
// Result {
// command: null,
// rowCount: null,
// oid: null,
// rows: [],
// fields: [],
// _parsers: [],
// _types: TypeOverrides {
// _types: {
// getTypeParser: [Function: getTypeParser],
// setTypeParser: [Function: setTypeParser],
// arrayParser: [Object],
// builtins: [Object]
// },
// text: {},
// binary: {}
// },
// RowCtor: null,
// rowAsArray: false
// }

空配列じゃなくて knex の Result 型
SQL の insert で何も insert しないが作れないので この条件で toQuery() すると出力される SQL は空文字
なので実行結果も特殊で Result 型のオブジェクト
この場合でも rows プロパティに空配列があるから then で受け取る場合には rows プロパティがあればそっちを見るようにするのが良さそう

query.then(rows => {
rows = rows.rows || rows

// something
})
knex で partition by
over 句は対応してないみたいなので そこの select 列だけ knex.raw で書く
あとは普通に knex で作れる

const knex = require("knex")({ client: "pg" })

const query = knex.select("id", "group_id")
.from(
knex.select("id", "group_id", knex.raw("row_number() OVER (PARTITION BY group_id ORDER BY id DESC) as n"))
.from("table1")
.as("sub")
)
.where("n", 1)
.toQuery()

console.log(query)
// select "id", "group_id" from (select "id", "group_id", row_number() OVER (PARTITION BY group_id ORDER BY id DESC) as n from "table1") as "sub" where "n" = 1
knex の null と undefined の扱い
SQL じゃないライブラリだと null や undefined とか特殊な値の扱いが気になるところ
ちゃんと理解してないと思いもよらないことになってそうだし

試した感じ キーなしでも明示的に undefined でも更新対象にはならない
inert の場合は undefined 問わず全レコードのオブジェクトに対してキー一覧を取り出して undefined になってれば DEFAULT が設定される
DEFAULT は接続時のオプションで null に置き換えできるみたい

pg("foo").update({a: 1, b: null, c: undefined, d: pg.raw("DEFAULT")}).toString()
// "update "foo" set "a" = 1, "b" = NULL, "d" = DEFAULT"

pg("foo").insert([{a: 1, b: null, c: undefined}, {}]).toString()
// "insert into "foo" ("a", "b", "c") values (1, NULL, DEFAULT), (DEFAULT, DEFAULT, DEFAULT)"

update で DEFAULT 値にしたいなら raw で DEFAULT 指定必要

inert 構文ではカラム名を列挙するので 配列で複数レコードがあるとどれかのレコードにあるキーは値問わずカラムとして追加される
それで各レコードに対してカラムの値を列挙してる感じ

事前に DB 接続してると言ってもテーブル名からテーブル定義を参照したりはしないので実際に存在しないキーがあればカラム名になるし 実行すればエラーが起きる
上の例を実行したときは foo テーブルなんて作ってないし
await は then を呼び出してる
await すると内部で then メソッドを呼び出してる

!async function(){
console.log(await { then(resolve, reject){ resolve(10) } })
}()
// 10
// false

!async function(){
console.log(await { then(resolve, reject){ reject(10) } })
}()
// false
// Uncaught (in promise) 10

ということは knex で .then(x => x) をわざわざ追加しなくても

console.log(await knex("table").select("*"))

でクエリが実行されるはず
やってみたら

!async function () {
const [a, b, c] = await Promise.all([1, 2, 3].map(x => knex.select(pg.raw(`${x} + 100`))))
console.log(a, b, c)
}()
// [ { '?column?': 101 } ] [ { '?column?': 102 } ] [ { '?column?': 103 } ]

うまくいった
knex の実行がわかりづらい
knex って then を呼び出したらクエリ実行だから 実行して Promise を取得したいと

knex("table").select("*").then(e => e)

みたいな then が必要ぽい
普通の Promise なら then はなくてもすでに実行済みで 非同期処理が終わって結果が来たら実行するための関数を指定するためにつかうのが then
だからそのまま返すだけの then ならつけなくても一緒
だけど knex は then が実行も兼用してるから then がないと実行されない

knex("table").select("*").execute()

みたいのがあればいいのに

基本的には then で結果を処理すればいいかもだけど 全部終わってから処理したい場合もあるし そのまま返して Promise 化するだけの then が必要になる

const [t1, t2, t3] = await Promise.all([
knex("table1").select("*").then(e => e),
knex("table2").select("*").then(e => e),
knex("table3").select("*").then(e => e),
])

かと言って これをまとめる関数を作るのものなぁ

const execAll = builders => Promise.all(builders.map(e => e.then(x => x)))

const [t1, t2, t3] = await execAll([
knex("table1").select("*"),
knex("table2").select("*"),
knex("table3").select("*"),
])



以上の内容はhttps://let.blog.jp/tag/knexより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14