hotoolong's blog

プログラムのことやエンジニアリングに関することを記事にしています。

ActiveRecordでOR文を作るときのエラー対処

少し複雑なSQLのOR文をActiveRecordで作るときにはすこし億劫になりますね。

ArgumentError: Relation passed to #or must be structurally compatible. Incompatible values: [:joins, :references]

とエラーが出てしまったのでいろいろ調べてみました。

今回のエラーはRails5.2の以下のコードで ArgumentError が発生しています。

rails/query_methods.rb at c81a7fcf76663e6d189792d6eed57b1162199635 · rails/rails · GitHub

structurally_incompatible_values_for_or が何なのかと見てみると

rails/query_methods.rb at c81a7fcf76663e6d189792d6eed57b1162199635 · rails/rails · GitHub

      STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
      def structurally_incompatible_values_for_or(other)
        STRUCTURAL_OR_METHODS.reject do |method|
          get_value(method) == other.get_value(method)
        end
      end

Relation::VALUE_METHODS が 以下で登録されているのですが、

rails/relation.rb at ac1efe7a947ba04b276a7109f1a86e559a6ab683 · rails/rails · GitHub

[:includes, :eager_load, :preload, :select, :group, :order, :joins, :left_joins, :left_outer_joins, :references, :extending, :unscope, :limit, :offset, :lock, :readonly, :reordering, :reverse_order, :distinct, :create_with, :where, :having, :from]

ここから [:extending, :where, :having, :unscope, :references] を省いた項目が一致してることをチェックしてるので
[:includes, :eager_load, :preload, :select, :group, :order, :joins, :left_joins, :left_outer_joins, :limit, :offset, :lock, :readonly, :reordering, :reverse_order, :distinct, :create_with, :from] の項目が一致してないと ArgumentError になるということですね。
これを個別に対応していくのは大変なのですね。。

これらの項目を設定するメソッドはいろいろ用意されてはいますが、

ActiveRecord::QueryMethods#methods:
  extending_values   group_values     includes_values=  left_joins_values        left_outer_joins_values=  lock_value    offset_value=  preload_values   readonly_value=     reordering_value     reverse_order_value=  set_value
  eager_load_values   extending_values=  group_values=    joins_values      left_joins_values=       limit_value               lock_value=   order_values   preload_values=  references_values   reordering_value=    select_values         unscope_values
  eager_load_values=  get_value          includes_values  joins_values=     left_outer_joins_values  limit_value=              offset_value  order_values=  readonly_value   references_values=  reverse_order_value  select_values=        unscope_values=

a の オブジェクトから b のオブジェクトに移すみたいなことは項目数から考えても面倒ですね。

b.joins(a.joins_values)

項目を並べてget_value と set_value で移すこともできますが、、、

[:includes, :eager_load, :preload, :select, :group, :order, :joins, :left_joins, :left_outer_joins, :limit, :offset, :lock, :readonly, :reordering, :reverse_order, :distinct, :create_with, :from].each do |method|
  b.set_value(a.get_value(method))
end

流石に面倒そうです。

User.
  joins(:accounts).
  group(:id).
  having('count(accounts.id) > 2').
  whrere(created_at: Time.zone.today.all_day)

このSQLにORを付けたいのですが、
当日と5日前に追加されたユーザを調べたいとします。

today = Time.zone.today
relation = User.
          joins(:accounts).
          group(:id).
          having('count(accounts.id) > 2')
relation.where(created_at: today.all_day).or(scope.whrere(created_at: today.days_ago(5).all_day))

このようにローカル変数にor前のRelationを渡してしまってwhereをつくればいいということですね。 少し面倒ですが、これで大丈夫そうです。

スコープを使ったケースを見てみます。

  scope :hogehoge, -> {
     where(created_at: Time.zone.today.all_day)
  }

このようなscopeがあるとします。まだ条件は当日のみです。

User.
  joins(:accounts).
  references(:accounts).
  having('count(accounts.id) > 2').
  group(:id).
  extending(Pagination).
  unscoped.
  hogehoge

先程の処理にhogehogeを追加してみました。 hogehogeのなかでOR文を作ってみます。

  scope :hogehoge, -> {
    today = Time.zone.today
    where(created_at: today.all_day).or(where(created_at: today.days_ago(5).all_day))
  }

ここではローカル変数なしに設定できました。

スコープではない場合は一旦変数に入れないといけないというのは不便ではあります。

これ自体が面倒な場合は String で直接SQLを記載するのがいいかもしれないです。

今回のケースはDateTime型で日付に対する条件になるので BETWEENが使われます。

day1 = today.all_day
day2 = today.days_ago(5).all_day
where('(users.created_at BETWEEN ? AND ?) OR (users.created_at BETWEEN ? AND ?)', day1.first, day1.last, day2.first, day2.last)

このような感じなります。
? に 設定する値がnilだった場合はこれだと面倒になったりするので
ケースによって使い方を選択してもいい気がします。

以下、参考にしたサイトです。

Rails 5 の or を色々試してみた - Qiita

sql - Relation passed to #or must be structurally compatible. Incompatible values: [:references] - Stack Overflow

Unicode エンコードの競合 というファイル名になってしまう

20180419_プロダクションレディマイクロサービス.txt

というテキストファイルをDropboxディレクトリに作っていたのですが、

20180419_プロダクションレディマイクロサービス (Unicode エンコードの競合).txt

という名前に変換されてしまっていました。 何なのかと思い、調べてみると

リンクされている別のパソコンで同期されないファイルがある場合 – Dropbox のヘルプ

ここの中に ユニコード エンコード問題 という項目があり、 Unicode Encoding Conflict とファイル名につくとありますがこれの日本語バージョンのようです。 とりあえず日本語を使うのをやめると解消されていました。 困ったものですね。。

fish shell の時に プロセスを起動し直す

source ~/.config/fish/config.fish

でconfigファイルの再読込はできるようなのですが、

bashzsh のときのように

exec -l $SHELL

を使いたいなと思っていましたがそのままではできないようです。

$SHELLがそのままでは実行できないようで

exec fish

すると上手く読み込んでくれました。 他にいいやり方がある気がしますが、configにfunctionを登録しておくと便利です。

function reload
  exec fish
end

Nodeのバージョンが10.0.0に

Nodeが早くも10.0.0です。 パフォーマンス改善も著しく注目も高いですね。 以下、CHANGELOGの内容です。

github.com

ちょうどNode学園の30時限目がありお邪魔してきました。

speakerdeck.com

speakerdeck.com

Nodeの改善に取り組んできた内容や今回の改善点、今後の展望?のようなことが聞けて為になりました。 HTTP GET のサンプルコードは async await書くことでコード量もスッキリしてとても良さそうです。 ローカルのNodeのバージョンも10.0.0を入れてみて楽しんでみます。

tmux が 2.7 にバージョンが上がっていたので brew で アップデートする。

⋊> ~ brew info tmux                                                                                                                                                                                                                                                                                                                                                 21:47:47
tmux: stable 2.7 (bottled), HEAD
Terminal multiplexer
https://tmux.github.io/
/usr/local/Cellar/tmux/2.3_2 (10 files, 651.9KB)
  Poured from bottle on 2017-01-04 at 10:50:53
/usr/local/Cellar/tmux/2.5 (10 files, 660.3KB)
  Poured from bottle on 2017-09-19 at 00:14:09
/usr/local/Cellar/tmux/2.6 (10 files, 688.6KB) *
  Poured from bottle on 2018-02-11 at 15:02:02
From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/tmux.rb
==> Dependencies
Build: pkg-config ✔
Required: libevent ✔
Optional: utf8proc ✘
==> Options
--with-utf8proc
        Build with utf8proc support
--HEAD
        Install HEAD version
==> Caveats
Example configuration has been installed to:
  /usr/local/opt/tmux/share/tmux

Bash completion has been installed to:
  /usr/local/etc/bash_completion.d

tmuxの2.7が既に出ていたのでバージョンアップしてみる。

⋊> ~ brew update tmux                                                                                                                                                                                                                                                                                                                                               21:47:59
Error: This command updates brew itself, and does not take formula names.
Use 'brew upgrade tmux' instead.
⋊> ~ brew upgrade tmux                                                                                                                                                                                                                                                                                                                                              22:03:53
Updating Homebrew...
==> Auto-updated Homebrew!
Updated Homebrew from 760a4601 to 27ec9dae.
Updated 2 taps (homebrew/core, caskroom/cask).
==> New Formulae
angle-grinder                                                                                                             libde265                                                                                                                  petsc
==> Updated Formulae
ansible ✔                   aws-shell                   dartsim                     exim                        git-annex                   jenkins-job-builder         libre                       moc                         osquery                     picard-tools                scrcpy                      tgui                        vtk
libuv ✔                     awscli                      dcd                         faas-cli                    git-fixup                   jetty                       librem                      mongo-orchestration         osrm-backend                pipenv                      scw                         thefuck                     watson
node ✔                      baresip                     dehydrated                  ffmbc                       gitlab-runner               joplin                      libsass                     mpd                         ott                         ppsspp                      sdl2_gfx                    tika                        webp
nvm ✔                       basex                       diamond                     ffmpeg                      gmt                         jruby                       libtensorflow               mpv                         packer                      pqiv                        sfcgal                      tile38                      wireshark
perl ✔                      bdw-gc                      dnscrypt-proxy              ffmpeg2theora               gmt@4                       json-fortran                libwps                      mpw                         paket                       presto                      sfk                         tippecanoe                  wp-cli
ruby-build ✔                bedops                      docfx                       ffmpegthumbnailer           gollum                      juju                        liquibase                   mruby                       pam-u2f                     primesieve                  shogun                      tokei                       wpcli-completion
vim ✔                       bento4                      docker-compose              ffms2                       gpgme                       jump                        logtalk                     nano                        pam_yubico                  pspg                        shyaml                      travis                      xmrig
abcmidi                     bettercap                   docker-compose-completion   field3d                     grafana                     jvgrep                      makeself                    nco                         pandoc                      puzzles                     silk                        twarc                       xonsh
acpica                      bluepill                    dockviz                     flann                       grib-api                    kallisto                    mame                        ncview                      pandoc-citeproc             pypy3                       siril                       txr                         xrootd
adplug                      bzt                         dscanner                    flow                        gws                         knot-resolver               math-comp                   netcdf                      parallel                    qcli                        skaffold                    uftp                        yaz
annie                       caffe                       dvm                         fluent-bit                  hadolint                    kubernetes-helm             maxwell                     nexus                       pcl                         qemu                        skafos                      unpaper                     ykpers
apache-drill                cake                        dynare                      fn                          haskell-stack               libbi                       meson                       nfdump                      pdal                        quex                        snakemake                   unrar                       youtube-dl
apktool                     cayley                      elasticsearch@2.4           fobis                       hdf5                        libdill                     mg                          nomad                       percona-server              r                           snapcraft                   urh                         zanata-client
arangodb                    cgal                        elasticsearch@5.6           folly                       heroku                      libebml                     mgba                        octave                      percona-server-mongodb      radare2                     sphinx-doc                  utf8proc
armadillo                   checkbashisms               emscripten                  fq                          hugo                        libetonyek                  mighttpd2                   odpi                        percona-toolkit             rmlint                      sratoolkit                  vagrant-completion
artifactory                 cockroach                   encfs                       freeciv                     ice                         libfaketime                 mikutter                    opencv                      php                         robot-framework             subversion                  valabind
aspectj                     convox                      erlang                      freeling                    inspircd                    libmatio                    minidlna                    opencv@2                    php@5.6                     rocksdb                     swimat                      varnish@4
atari800                    coq                         erlang@18                   freeradius-server           instead                     libmatroska                 mkl-dnn                     openfortivpn                php@7.0                     rom-tools                   tee-clc                     vault
aubio                       corsixth                    etcd                        get-flash-videos            iozone                      libmwaw                     mkvtoolnix                  openimageio                 php@7.1                     rtf2latex2e                 telegraf                    vim@7.4
audacious                   cpanminus                   etsh                        gifski                      jenkins                     libraw                      mlt                         openrtsp                    pianobar                    scale2x                     texmath                     visp

==> Upgrading 1 outdated package, with result:
tmux 2.6 -> 2.7
==> Upgrading tmux
==> Downloading https://homebrew.bintray.com/bottles/tmux-2.7.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring tmux-2.7.high_sierra.bottle.tar.gz
==> Caveats
Example configuration has been installed to:
  /usr/local/opt/tmux/share/tmux

Bash completion has been installed to:
  /usr/local/etc/bash_completion.d
==> Summary
🍺  /usr/local/Cellar/tmux/2.7: 10 files, 700.7KB

以前痛い目に合ってたので念のためなくなっているもしくは名前が変更されているFormulaeがないことを確認しておいたほうがいい。

今回の2.7での変更点はパフォーマンス改善などが行われている以下のURLで確認できる。

https://raw.githubusercontent.com/tmux/tmux/2.7/CHANGES

尚、以下の一文

* Pass PWD to new panes as a hint to shells, as well as calling chdir().

が少しに気になっていてペインにディレクトリ階層を遅れたりしないのかなと思ったのだけどどうやってやったらいいのか。 そもそもそういうことでは無いってことなのか、調べてもよくわからなかったので、詳しく知りたいところ。。

brew で MySQL8.0をインストールする

MySQL8.0がリリースされましたね。

MySQL8.0の機能

MySQL :: MySQL 8.0: Up to 2x Faster

新機能と以下が上げられています。

  • Document Store
  • Transactional Data Dictionary
  • SQL Roles
  • utf8mb4 character set が Default に
  • 共通テーブル式の追加
  • Window関数の追加
  • 不可視化インディックスの追加
  • 降順インディックスでのパフォーマンス改善?

機能改善として以下が上げられています。

Role設定ができることや、不可視化インディックスなど興味深いですね。

文字コードに関してはデフォルト設定が日本語には少し優しくない状態ですが、 以前よりは改善されてます。

blog.kamipo.net

以前からこの問題に関しては かみぽわーる さんが詳しく取り上げてくれていますね。

デフォルトの collationが utf8mb4_0900_ai_ci になってます。

utf8mb4_bin にしておけば概ね大丈夫ですね。 「MySQL」と「MySQL」を区別したくない場合は utf8mb4_0900_as_cs
私が扱っているサイトでは utf8mb4_bin 一択です。

以下のスライドで collation に関して詳しく書かれているので参考にされたい方は見て下さい。

www.slideshare.net

brew で インストール

かなり高速化されていると謳っているので Macで早速試してみたい方はbrewからインストールしてみてください。 brew optionsを見てみると

⋊>  brew options mysql
--with-debug
        Build with debug support
--with-embedded
        Build the embedded server
--with-local-infile
        Build with local infile loading support
--with-memcached
        Build with InnoDB Memcached plugin
--with-test
        Build with unit tests
--devel
        Install development version 8.0.4-rc

になっているので

⋊>  brew install mysql --devel

で8.0.4-rcがインストール可能です。 よいMySQLライフを!

vim-gitgutterでGitコマンドを使う

以前から行単位で変更箇所を戻すのにGitGutterRevertHunkを使ってたのですが、 vim-gitgutterのバージョンを上げるとコマンド名が変更になっていました。

github.com

.config/nvim/init.vim には以下の用にGitGutterRevertHunk だったのを GitGutterUndoHunk に直しておきました。 快適ですね。

" gitgutter {{{
  " nmap <silent>,gr :<C-u>GitGutterRevertHunk<CR>
  nmap <silent>,gr :<C-u>GitGutterUndoHunk<CR>
" }}}