【GraphQL】何らかの条件で何らかを集めてくるフィールドの設計、どうする?

何を言っているかわからないと思うけど、例えば「1週間以内に新規登録したユーザー一覧を返す」みたいなフィールドをどう設計するかという話。いくつかパターンがありそう。

1. 専用のフィールドを用意する

query {
  recentlyRegisteredUsers: [User!]!
}

クライアントからはこのフィールド呼べばいいだけなので楽。ただし、新たに「最近更新があったユーザー一覧を返したい」のような要望がでてきたら、似たようなフィールドが無尽蔵に増えていく可能性がある。

2. 全部返すフィールドを特定の条件でフィルタできるようにする

query {
  users(
    filter: UserFilter
  ): [User!]!
}

enum UserFilter {
    "1週間以内に新規登録したユーザー"
    RECENTLY_REGISTERED
}

1 よりはフィールドの治安は保たれそうだけど、代わりにフィルタ条件が増えていくことになる。

3. 検索用のフィールドを用意する

query {
  searchUser(
    registeredSince: DateTime,
    registeredUntil: DateTime
  ): [User!]!
}

2 と似てるけど、クライアント側から検索条件を指定するタイプ。シンプルな検索条件ならこれでいいけど、複雑になってくるとクライアント側からのクエリが大変なことになりそうだし、検索条件のオプションも増えまくって大変になりそう。

4. queryString で検索できるようにする

query {
  searchUser(
    query: String
  ): [User!]!
}

クエリ文字列でなんでも検索する方式。こちらもシンプルな検索条件ならいいけど、複雑になってくるとクエリを組み立てるのが大変になりそう。queryString をパースする必要があるのでバックエンドの実装は他の案と比べて複雑になるかも。

結局どれがいい?

以下のような方針が良い気がしている。

  • 検索条件がシンプルなら、検索用フィールドから検索できるようにする

    今回の例なら、ユーザーの登録時期で検索とかは汎用的な要件なのでこちらがいいのかも?

  • 検索条件が複雑なら、専用のフィールドを用意する

    例えば「1ヶ月以内に登録して、なおかつ1週間以内に5回以上更新があったプレミアム登録済みのユーザー一覧」みたいな条件だと特殊な要件すぎるのでこちらがいいのかも?

他に何かオススメあったら教えてください!

キーボードとマウスを新しくした

今まで普通のテンキーレスのキーボードを使っていたんだけど、FPS をプレイするときにマウスが当たってしまってエイムの邪魔になるので60%キーボードを買った。

Ducky One 3 Mini 60% keyboard Classic Pure White

エンターキーの色が違うのがおしゃれ。めっちゃ光ってるけど、ライトパターン10個くらいあって控えめなのも選べるのでよい。

せっかくいいキーボード買ったので、仕事用の Mac Book Pro でも使っている。今までは周りが HHKB とか使っているのを見ても「キーボード買ったところでタイピング速度なんてそんな変わらなくね?」と冷めた目で見て頑なに Mac Book の付属のキーボードを使っていたけど、しばらく使ったところ、確かにタイピング速度は別に変わらないけど気に入ったガジェット使ってると気分は上がっていいな、となった(手のひら高速回転)。

マウスもついでに新しくしてG PRO X SUPERLIGHT にしたんだけど、Mac で使うときにマウス加速が邪魔すぎるので LinearMouse を入れて切っている。Mac 使ってると、こういうのわざわざアプリケーション入れないと設定できないのイマイチだよな〜って思う。アプリケーションごとに音量をいじれないとか、Windows ならそういうのは OS 標準でできるのに…

なお FPS は全然上手くなってない様子。

ChatGPT にホームページを作ってもらう

独自ドメイン取ったので自己紹介サイトでも作ろうかな〜と思ったけど、急にめんどくさくなったので ChatGPT に作ってもらった。

こんな感じになった。

ヘッダにリンクがあるのが嫌なので変えてもらう。

ちゃんと変えてくれた。優秀。

タイトルを変えてもらったり、chatGPT で作っていますという注釈を入れたり、フッタを消してもらったり、アイコンを設置してもらったりした。

最終的に出来上がったのがこちら。

Font Awesome のロードの仕方がちょっと間違っててアイコン読み込まかかったのでそこは手で直したけど、なんかいい感じになった気がする。5分くらいでできたので、たたき台作りとしてはいいかも。

https://magaming.net/

cookie に同意するポップアップを消したい

GDPR の関連で、特に海外のページにアクセスすると「お前のブラウザの cookie 使うけどええか?」というポップアップ来るけど勝手に使ってくれ…という気持ちになる。

同じように考えている人は多いようで、 I don't care about cookies 3.4.5 というブラウザ拡張機能が存在している様子。試しに使ってみたけど、一部のサイトではうまく動かなかった。

実装が気になって見てみると、サイトごとにルールが列挙されていて、このサイトはDOMを隠す、このサイトは Cookie に値をセットする… みたいなことが書かれていた。そしてこのリストに追加したい場合は、リクエストを送ると追加される(可能性がある)世界観のよう。

I-Dont-Care-About-Cookies/rules.js at master · appeasementPolitik/I-Dont-Care-About-Cookies · GitHub

サイトによって実装はバラバラなのでこうするしかないよな〜と思いつつも、なかなかワイルドで面白い。

Qrio Lock から SwitchBot スマートロック + 指紋認証パッドに変えたら最高になった

Qrio Lock を1年半くらい使っていたんだけど、リモート解錠の精度が悪くて機能しなかったり、解錠がシンプルに遅くて最悪10秒くらいかかったりしてイライラしていた。Amazon 見てたら、SwitchBot スマートロック + 指紋認証パッドが Qrio 単体より安いことに気づいてしまったので買ってみた。

感想

  • 指紋解錠は3秒くらいでかかる。もうちょい早ければ嬉しいけどストレスはそんなに無い
  • オートロックの閉まるまでの時間を設定できるので、ちょっとポストに郵便を取りに行くみたいなケースでいちいち閉まらなくて最高
    • Qrio は設定できないのでこういう場合はいちいちオートロックを無効にしていたが、有効にし忘れがあったので微妙だった
  • Qrio にあるリモート解錠機能はないけど、Qrio は精度が微妙だったので、無くてもあんまり不便には感じない
  • 何より指紋だけで開けられるのでスマホ忘れ、電池切れを気にしなくて良いのが楽

逆に Qrio にいい点があるか…というと正直デザインに高級感があるくらいしか思いつかない。あとは電池持ちが悪いという話もあるけどそこはもう少し使ってみないとわからない、という感じ。

MySQL で 複合キーを貼る時は explain の key_len をちゃんと見る

複合キーを貼っている以下のようなテーブルで、index が効かなくてクエリがめちゃ重くなる現象が発生してしまった。

CREATE TABLE `example` (
    `id` BIGINT UNSIGNED NOT NULL,
    `column1` BIGINT UNSIGNED NOT NULL,
    `column2` BIGINT UNSIGNED NOT NULL,
    `column3` VARCHAR(36) NOT NULL,
    PRIMARY KEY (`id`),
    INDEX `index1` (column1, column2),
    INDEX `index2` (column1, column3)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

実際に発行したクエリは以下のような感じ。

SELECT * FROM example
WHERE column1 = 1
AND (column2 = 1 OR column3 = "hoge");

OR 使ってるので index 効かないのはそれはそうという感じなんだけど、explain 結果を見た時possible_keys, key に index 名が入っているので効いてるじゃんと思ってしまった。

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE example ref index1,index2 index1 8 const 2 75 Using where


しかし、key_len8 になっているので、これははちゃんと複合 index が効いていない状態。ちゃんと効いてるのであれば、key_lenBIGINT の 8byte *2 = 16 または BIGINT + VARCHAR(36) の 146 = 154 *1 になるはず。実際に index が効くようにクエリを発行すると、そのような結果になる。

index1 が効くクエリ

EXPLAIN  SELECT * FROM example
WHERE column1 = 1
AND column2 = 1;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE example ref index1,index2 index1 16 const,const 2 100

index2 が効くクエリ

EXPLAIN  SELECT * FROM example
WHERE column1 = 1
AND column3 = "hoge";
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE example ref index1,index2 index2 154 const,const 1 100

結局上記のようにクエリを2つに分割することで解決した。


key_len がおかしければ複合 index が効いてないことがわかるので、explain するときは key_len をちゃんと見ましょうという教訓。

そしてこれらのクエリを試すために SQL の Playground であるところの DB Fiddle 使ったけど手元に環境を用意する必要がなくて便利。Makdown で出力できる機能もあるので、ブログとかに貼りやすくて助かる。 https://www.db-fiddle.com/f/4yd3igqfkESZBG6dtT85hL/0

*1:VARCHAR の byte 数は最大長。utf8mb4 なので 36 * 4 byte に加えて、さらに varstring だとさらに2バイト常に確保される様子

git 上で削除したファイルがコード内で利用されているかどうか調べるためのワンライナー

削除したファイルが 1 つ 2 つだったら使われているかどうかを調べるために素朴に grep すればいいんだけど、大量にあるときつい。ので手軽に調べられるワンライナーを書いてみた。

git log <commit_hash>...<commit_hash> --diff-filter=D --name-only --oneline | grep -Eo '[^/]+$' | xargs -I arg git grep arg

細かく解説すると以下のような動き

  • git log <commit_hash>...<commit_hash> --diff-filter=D --name-only --oneline でコミット間で削除されたファイルの一覧を出す
  • ↑だとフルパスが出てくるけど、コード内で相対パスで参照されることがあるのでgrep -Eo '[^/]+$' でファイル名のみを取り出す
  • xargs -I arg git grep arg でファイル名ごとに git grep

git grep しまくるのでファイル数が増えるとかかる時間が指数関数的に増加していくけど、手元で試したかぎりは300ファイルくらいならまぁめちゃくちゃ時間がかかるという感じでもなかった。(コードの規模にもよるけど…)