Knex.js で日付範囲指定の検索したり・バッチ処理で更新や削除したり

最近 (今年の2月ぐらいに) 興味深い記事を読みました。

クエリビルダを利用する明確な利点は存在しないという内容です。ここ3年ぐらいの間に割と聞かれるようになったやつです※。

Stop using Knex.js — Using SQL query builder is an anti-pattern
https://medium.com/@gajus/stop-using-knex-js-and-earn-30-bf410349856c

その趣旨を一言で述べると、動的に作成される必要があるクエリにのみ例外的に Knex.js を使用して、それ以外は直に SQL を書いたほうが良いというもの。

Knex.js というのは node.js において最もよく使われている SQL クエリビルダで、私も手を抜くときに使っています。

MySQL にも PostgreSQL にも SQLite3 にも (不完全ながら DB2 にも) 対応しており、SQL を知っている人にとっては直感的に使えます。

何が良いというわけではないのですけれども、無意識に自然と使用していることが多いです。 SQL を直書きするときでさえ、気がつくと knex.raw() 関数を使っています。

しかし、データベースの一部の機能が使用できない、あるいは使用できないという誤解に基づき酷評されている場面を見たことがあります。

ここからが上の記事の内容なのですが、SQL で実現可能なことをクエリビルダで実現するために二度の学習が必要となり、knex.js に詳しい人も (SQLに詳しい人に比べれば) 少ないので問題が起こった際に調べる手間が大きいことが難点です。

そして、バッチ更新のように利用頻度が高いものが、公式ドキュメントに書かれていない (いま再確認したら batchInsert は書かれていましたけど) ので、それも誤解の一因になっているのかと思いました。

Knex.js – A SQL Query Builder for Javascript / Latest Release: 0.16.3
https://knexjs.org/




ドキュメントに書かれていないので、誤解されても仕方がないですが、データの更新 (UPDATE) や削除 (DELETE) を knex.js でバッチ処理で実行することは可能です。

もとのソースコードの SQL 文の生成あたりを読んでいて、トランザクションの中にクエリ (Array objects) を入れてみたら実行できるような気がしたので、ためしに入れてみたところ… 実際に動作することを確認できました。

できる根拠を尋ねられても、もう当時の考えを覚えてないので困ってしまうのですけれども、やってみたら実行できたのですよ。

const express = require('express');
const router = express.Router();
const knex = require('knex')({
  client: 'sqlite3',
  connection: {
    filename: "./myname.sqlite"
  }
});

/* Connect to (MySQL|PSQL|SQLite) */
router.get('/', (req, res) => {
  let tableName = 'name';
  let query = [{
      id: 1,
      name: 'Janie Doe'
    },
    {
      id: 2,
      name: 'Richard Roe'
    },
    {
      id: 3,
      name: 'John Schmoe'
    }];
  knex.transaction(trx => {
      let built = query.map(elem => {
        return knex(tableName)
          .where('id', elem.id)
          .update(elem)
          .transacting(trx);
      });
      return Promise.all(built)
        .then(trx.commit)
        .catch(trx.rollback);
    })
    .then()
    .catch();
});
module.exports = router;

このソースコードは myname という DB に name というテーブルを作成して、 hoge とか huga とか piyo とか foo とか waldo みたいな適当な初期値を入れておけば実行できます。

削除したい場合には、update(elem) の部分を del() に書き換えると同様に実行できます。もちろん、insert() に書き換えても動きます。

実行環境は以下のとおりです。

$ uname -a
Linux Ganymede 4.15.0-50-generic #54-Ubuntu SMP Mon May 6 18:46:08 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

$ node -v
v12.2.0

$ grep -i knex package.json 
    "knex": "^0.16.7",

$ sqlite3 --version
3.22.0 2018-01-22 18:45:57 0c55d179733b46d8d0ba4d88e01a25e10677046ee3da1d5b1581e86726f2171d

同じように公式ドキュメントには書かれていないのですけれども、日付の範囲指定なども whereBetween() 関数の中に入りそうに見えたので、入れてしまうとやっぱり動きます。

こうすると簡単かつ柔軟に範囲を指定できるようになって便利なんですよね。

const express = require('express');
const router = express.Router();
const knex = require('knex')({
  client: 'sqlite3',
  connection: {
    filename: "./mylog.sqlite"
  }
});

router.get('/', (req, res) => {
  let tableName = 'access';

  const offset = 8; // UTC time offset HKT 
  //const offset = 9;  
  let currentDate = new Date();
  currentDate.setTime(currentDate.getTime() + 60 * 60 * 1000 * offset);

  let nextDate = new Date();
  nextDate.setTime(nextDate.getTime() + 60 * 60 * 1000 * offset);
  nextDate.setDate(nextDate.getDate() + 1);

  let cd = currentDate.toISOString().split('T')[0];
  let nd = nextDate.toISOString().split('T')[0];

  knex(tableName)
    .select()
    .whereBetween('date', [cd, nd])
    .orderBy('date', 'desc')
    .then(rows => {rows.forEach(elem => {console.log(elem);});})
    .catch();
});
module.exports = router;

このように knex.js には、公式には謳われていなくても、既知の関数の組み合わせで実現できることがたくさんあります。

SQL で実行できることは、何かしらの方法で実行できることが多いです。

できそうに思えたのに実行できなかったのは OrderBy() の中に Array や連想配列を入れて、複数の条件で並び替えを行うことです (Knex.js ver 0.16.7 までの話で、今後は対応されるかもしれません)。

この場合はデータベース内に VIEW を作成しておいて tableName に VIEW の名前を代入してやると思い通りの結果を得ることができます。

SELECTの中で条件分岐したい場合などにも、あらかじめ VIEW を作成しておいて tableName に VIEW の名前を代入してやると簡単です (これもドキュメントに書いてあったかどうか…) 。

でも、それは「SQL で同じことができることを経験的に知っているからではないか」と言われてしまうと、冒頭の話題を考えざるを得なくなるわけです。

SQLで直にクエリを書くことを考えると、どちらが良いのでしょうね。


SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)


※ 一例をあげると

Why you should avoid ORMs (with examples in Node.js)
https://blog.logrocket.com/why-you-should-avoid-orms-with-examples-in-node-js-e0baab73fa5

Breaking Free From the ORM: Why Move On? Time to escape object relational mapping
https://medium.com/building-the-system/dont-be-a-sucker-and-stop-using-orms-190add65add4

これから始める登山

唐突ですが登山を始めます。いつも訪れているトレイルや舗装路の峠ではなくて、登山靴がないと行けないような、本当の意味での「登山」です。

と言っても 10 年以上も昔に何度も登山に行っていたので、既に登山靴やら雨具やらは手元にありますし、しなければならないこと、してはならないことは既に頭に入っています。

友人に誘われて始めたのですが、たしか高校生ぐらいのときに転校により環境が変わってしまったのを境に疎遠になってしまいました。

さすがに、その時の古い装備のままでは信頼性に不安があるので、幾つかの道具を更新します。

冬に山に登る場合には多くの装備品と経験と入念な準備が必要となります。それに対して夏の場合にはそれほど多くの荷物を要求されることはありません。

  1. 登山靴
  2. レインウェア
  3. ヘッドライト
  4. グローブ
  5. 地図
  6. 方位磁石
  7. 折り畳み傘
  8. ストック
  9. 携帯食
  10. 入山届・筆記用具

ぐらいが最低限の必要なものです。

リュックサックは自転車用途に使用している deuter を、腕時計はランニングに使用している GARMIN を流用します。

本当は一番大切なのは、引率できる知識を有する経験者なのですが、まったくの入門者が一から始める場合には、どうするのが良いのでしょうね。

増水、落石、崩落、雷と地味に気をつけることが多いので、可能ならば最初は経験者に教わるのが最良なのですが。




登山用品の中で最初に調達すると良いのは、防災用品としても使用できるレインウェアとヘッドライトです (登山靴は最新のものに詳しくないので割愛) 。

非常時にも役に立ちますし、登山では必須の装備品となります。

いざというときに頼りになることが肝心なので、ここは値段よりも信頼性と機能で専用品を選択したほうが良いです。

私は撥水と即乾機能でウェアを選んでいます。

重ね着することも考えて、ご自身の体に対して、やや大きめのものを選択しておくと間違いがありません。

内側にダウンジャケットを重ねてウィンドブレーカーとしても利用できるので、こういうのが一着あると寒冷地に出張に行く際にも流用できて便利ですね。

専用の衣服と靴が準備できましたら、次に重要なのは視界を確保することです。

泊りがけの場合にはヘッドライトは必須になります。日帰りの場合にも備えとして有ったほうが良いです。

日が傾くと急に暗くなりますし、夜間は何も見えないことを前提にして行動しないと、どこに危険があるか分かりません。

スマートフォンのライトでは照明範囲が調整できませんし、片手が塞がってしまうと行動が制限されるので、頭に着けるライトが最良です。


PETZL(ペツル) ACTIK (アクティック) ブラック [アクティブシリーズ] E99AA A [並行輸入品]

私は信頼性重視で PETZL を選んでいます。地味に 10 年以上も使い続けていて、未だに動き続けているという一点だけでも凄いです。

しかし、手元にある旧モデルは時代が時代だけに光源が豆電球なので、光量が心許ないです。

新製品では光源が LED 2つに増えて軽量化されました。

ゴムかシリコンを使用してそうな旧製品と比較して、経年使用でも防水性が落ち無さそうなところも良いです。

衣服と視界の心配が無くなったら、次は現在地と方角を知ることを考えます。

地図は図書館でコピーできたり、古書でも買えたりしますけど、可能であれば最新のものの方が間違いありません。登山専用の地図というものが刊行されていて、書店で購入できます。

地図を入手できましたら、必ず予備のコピーをとって、防水ケースに入れておいてください。濡れたり破れたりして読めなくなることがあります。

地図だけが有っても、似たような景色ばかりで居場所が分からなくなるので、コンパスも必要です。

携帯電話は市街地を離れると圏外になって、現在地表示の精度が落ちますので、あまり頼りすぎないほうが安全です。


スント(SUUNTO) コンパス A-30 [日本正規品 メーカー保証] SS012095013

ストックは持っていると下山のときに役に立ちます。

ずっと坂道を下り続けていると膝が痛くなるので、その対策です。

携帯食は血糖値が下がりすぎて動けなくなることを予防するために必要です。食べたくなくても、安全確保のために持っていってください。

羊羹とかクラッカーで良いので、それほど困ることはありません。

どちらかと言うと飲料水のほうが重くて持ち運びに困ります。

いつもプラスチックボトル4本から5本ぐらいを持ち運んでいた思い出がありますが、行き先と季節によっては更に本数を増やす必要があるかもしれません。

登山靴も重いですけど、飲料水は本当は重いです。


【Amazon.co.jp限定】BAND-AID(バンドエイド) キズパワーパッド 大きめサイズ 12枚+ケース付 絆創膏

最後に日よけの帽子、サングラス、タオル、スプーンやフォーク、絆創膏、虫除けスプレー、ライター、ピンセット(棘抜き)、予備の下着などは持っていると役に立ちます。

反対に調理器具や食材などは持って歩きたくないです。

テントも一人または少人数での登山では持ち運びたくありません (日帰り、または山小屋に泊まれるところを探していきます) 。

テントを持っていても、好きなところに設置できるわけではないので、個人的にはあまり魅力や利点を感じ無いというのがその理由です。

持っていかれる場合はどうやって運搬するかに加えて、どこに設置するかまで考えておくと良いと思われます。

無くても楽しいんですけどね。

あとは車を止めた駐車場から登山口まで荷物を満載して走れるミニベロ、いや、これも無くてもいいかな

自転車とカメラは相性が悪い

今更、言うまでもないことですが、自転車とカメラは互いに相性最悪です。

自転車では (最低質量が設定されているレース以外では) 軽ければ軽いほど良いという世界であり、修理道具すらサポートカーに携行を任せて、自身では何も持たないことが理想です。

カメラの世界は反対に性能が良いものほど、大きく、重たく、携帯性が悪くなっていく傾向があります。

自転車の振動や風雨、汗や蒸気、直射日光や寒暖差など、精密機械であるカメラにとって良いことは一つもありません。

自転車にとっても、撮影の度に停止していたのでは機材に余計な負担が掛かるばかりですし、いちいち止まっていたのでは爽快感も何もないですし、撮影していないときはカメラは余計な荷物です。

はっきり言うと邪魔です。

カメラを持ち出すのは ただの私のエゴ です。

本当にカメラと相性が良いのは旅であり、私の場合は旅に自転車を持ち込んでいるだけです。

自転車は国際免許証がなくても乗れるし、転居の際にも荷物として飛行機に載せられるので便利だなというのが私の自転車生活の原点ですが、それは今も大きくは変わりません。

かつて「転勤族の子ども」として考えていたことを、大人になって出張の名目で繰り返しているに過ぎません。

私にとって都合が良い面もあって、基本的にはやりたいからやっているので、まじめに相性を考えると実は最悪です。

もし仮に自転車とカメラが相性がいいという意見があったとしたら、それは事実関係の整理が不十分なだけの疑似相関のようなものなので、真に受けてはいけません。

別にコウノトリが増えたからと言って出生率が上がるわけではないですし、雨が降るのはカエルが鳴いたからではありません。

相性が悪いけれども、使いたいからこそ、みんな工夫しているわけです。




自転車で遊ぶには荷物が少ないほうが良いので、基本的には小型で軽量であることが一番です。

防水や耐衝撃はあったら嬉しいのですが、防水性を追求すると大型化する傾向にあるので、優先順位はそれほど高くはありません。

ただし、小型軽量を追求しすぎると「何も持たないほうが良い」という極論に行き着いて、最終的にはスマートフォンで良いとなるので画質と操作性も重要です。

スマートフォンのカメラでは何がいけないのかというと、見返したときにつまらないので撮る気力がなくなります。

個人的な意見では、スマホで撮影した絶景写真ほど悲しいものはないです。

二度と訪れない瞬間の記録なので消すに消せません。かと言って、見ていて楽しくないので、見返す気にもならずに扱いに困ります。

もちろん、スマホで十分に満足という人は、それでいいと思います。

満足しているというのは幸せなこと (求めても得られるとは限らないこと) なので、わざわざ相性の悪いカメラに手を出して不幸になることはありません。 手を出したいという人は止めません

スマホが真に偉大だったのは「誰もが常にカメラを持ち歩く」ことを当たり前にして、デジカメを進化させたことの一点に尽きます。

スマホに対抗するためにメーカー各社が開発した高級コンデジは、その大きさと画質から自転車に持ち出すカメラの最有力候補になります。

ポケットに収納できて、高画質で、ズームもできて、ある意味、理想的ではありますが、どれも一眼レフなどと比べると性能のわりに高額で、操作性も犠牲になっている面もあり、使うほどに不便に感じるというデメリットもあります。

個人的には電動ズームレンズは嫌いです。タッチパネルは濡れた手では扱いにくいので、物理的なダイアルのほうが良いです。ファインダーがないと晴天時の屋外は何が写っているのか見えません。

そして、コンデジであっても画質を追求するほど大型化することは避けられず、本体だけならミラーレスカメラと大きさや重さが変わらないこともあります。

これがミラーレスカメラになると、今度は基本的にポケットに収納することができなくなります。

ストラップでたすき掛け状態にして吊るすか、リュックサックに収納して持ち運ぶことになります。

どうせ運搬方法は変わらないので、ミラーレスが選択肢に入るのであれば、お好みで一眼レフを選択されてもいいと思います。

一眼レフの方がバッテリーの持続時間が長く、悪環境に強く、今現在ではレンズも割安です。

ミラーレスは小型軽量で、露出の設定が容易で、オールドから最新の超高性能レンズまでレンズを選び放題という利点があります。

どちらもコンデジに比べると圧倒的に操作性が良く、使い続けていてもストレスが少なく、頑丈で故障に強いモデルもあり、しかも、安く購入できる方法がたくさんあるなど良いことづくめですが、持ち運びには苦労します。

(自転車では濡らしたり、落としたりする頻度が飛躍的に高まるので安いことは重要です。反対に高速オートフォーカスなどの最新の機能は必須ではありません。)

まずは自身が持ち運べる限界を知っておく必要があります。

最初の 30km 程度は大丈夫でも、距離を走り続けていると徐々に辛くなってきたり、首や肩に痛みがでてきたりすることがあります。

私の場合は、経験上、カメラとレンズを合わせて 920g × 1日あたり 300km までの走行は許容できますけど、これ以上は嫌です。

平地しか走らない場合でも持ちたくありません。

しかも、これも余裕があるわけではなく、翌日に肩こりが出るなどの支障が出るけど、安全に持ち運べなくはないという限界に近い重さです。

そして、重さの次に羞恥心に打ち克つ必要があります。

ジャージ姿に大きなカメラを持っている己の姿が滑稽で、いまだに笑えてしまいます。

誰もいない山の中、あるいは誰もがカメラを持ち歩いているような名所なら構わないのですが、サイクリングロードのようなただの人が多いだけの場所では、気恥ずかしい思いをすることも少なくありません。

持っていても不自然ではない大きさで、どこにでも持ち運べて、グループライドのときにも不自然ではないものとなると、当然ながらコンデジのほうが携行性が良いです。

しかし、後から見たときに思い返す、空気の冷たさ、生ぬるさ、蒸し暑さ、雨や森や枯れ草の臭い、風を切る感触を強烈に思い出させるのは大きいカメラの方です。

苦労して持ち運び、きちんと設定を考えているということもあるのでしょうけれども。

つまり、何が言いたいかと言えば、相性が良くないからこそ、いろいろなカメラを用意して、どの場面で何が使えるのかを試したくなります。

運搬方法と羞恥心の苦労を乗り越えても、何を持っていくかという選択の苦労が待っているわけです。

その苦労を乗り越えれば、撮影の楽しさと喜びが待っているわけでもありますが。

では、何をどう選べば良いのか、というのはまた別の機会にでも。