LoginSignup
81
78

More than 3 years have passed since last update.

ActiveRecordのarel_tableから作れる条件式まとめ

Last updated at Posted at 2015-05-26
$VERBOSE = true
require "active_record"
ActiveRecord::VERSION::STRING   # => "6.0.0"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Migration.verbose = false
ActiveRecord::Schema.define do
  create_table :users do |t|
  end
end
class User < ActiveRecord::Base; end
def _(v) v.to_sql.remove('"', "users.") end

User.arel_table[:x].instance_eval do
  _ eq(nil)                        # => "x IS NULL"
  _ eq(0)                          # => "x = 0"
  _ eq_any([0, 1])                 # => "(x = 0 OR x = 1)"
  _ eq_all([0, 1])                 # => "(x = 0 AND x = 1)"

  _ not_eq(nil)                    # => "x IS NOT NULL"
  _ not_eq(0)                      # => "x != 0"
  _ not_eq_any([0, 1])             # => "(x != 0 OR x != 1)"
  _ not_eq_all([0, 1])             # => "(x != 0 AND x != 1)"

  _ gt(0)                          # => "x > 0"
  _ gt_any([0, 1])                 # => "(x > 0 OR x > 1)"
  _ gt_all([0, 1])                 # => "(x > 0 AND x > 1)"
  _ gteq(0)                        # => "x >= 0"
  _ gteq_any([0, 1])               # => "(x >= 0 OR x >= 1)"
  _ gteq_all([0, 1])               # => "(x >= 0 AND x >= 1)"

  _ lt(0)                          # => "x < 0"
  _ lt_any([0, 1])                 # => "(x < 0 OR x < 1)"
  _ lt_all([0, 1])                 # => "(x < 0 AND x < 1)"
  _ lteq(0)                        # => "x <= 0"
  _ lteq_any([0, 1])               # => "(x <= 0 OR x <= 1)"
  _ lteq_all([0, 1])               # => "(x <= 0 AND x <= 1)"

  _ self.in(0)                     # => "x IN (0)"
  _ self.in([0, 1])                # => "x IN (0, 1)"

  _ in_any([[0, 1], [2, 3]])       # => "(x IN (0, 1) OR x IN (2, 3))"
  _ in_all([[0, 1], [2, 3]])       # => "(x IN (0, 1) AND x IN (2, 3))"

  _ not_in(0)                      # => "x NOT IN (0)"
  _ not_in([0, 1])                 # => "x NOT IN (0, 1)"
  _ not_in_any([[0, 1], [2, 3]])   # => "(x NOT IN (0, 1) OR x NOT IN (2, 3))"
  _ not_in_all([[0, 1], [2, 3]])   # => "(x NOT IN (0, 1) AND x NOT IN (2, 3))"

  _ matches("a")                   # => "x LIKE 'a'"
  _ matches_all(["a", "b"])        # => "(x LIKE 'a' AND x LIKE 'b')"
  _ matches_any(["a", "b"])        # => "(x LIKE 'a' OR x LIKE 'b')"

  _ does_not_match("a")            # => "x NOT LIKE 'a'"
  _ does_not_match_all(["a", "b"]) # => "(x NOT LIKE 'a' AND x NOT LIKE 'b')"
  _ does_not_match_any(["a", "b"]) # => "(x NOT LIKE 'a' OR x NOT LIKE 'b')"

  _ between(0..1)                  # => "x BETWEEN 0 AND 1"
  _ between(0...1)                 # => "x >= 0 AND x < 1"

  _ not_between(0..1)              # => "(x < 0 OR x > 1)"
  _ not_between(0...1)             # => "(x < 0 OR x >= 1)"

  # Float::INFINITY を使ったときに意図した通りの SQL になるのは嬉しい
  I = Float::INFINITY
  _ between(0..I)                  # => "x >= 0"
  _ between(-I..0)                 # => "x <= 0"
  _ between(-I...0)                # => "x < 0"
  _ between(-I..I)                 # => "1=1"

  _ not_between(0..I)              # => "x < 0"
  _ not_between(-I..0)             # => "x > 0"
  _ not_between(-I...0)            # => "x >= 0"
  _ not_between(-I..I)             # => "1=0"

  # 次の2つは動くけど非推奨です。警告がでます。
  _ self.in(0..1)                  # => "x BETWEEN 0 AND 1"
  _ not_in(0..1)                   # => "(x < 0 OR x > 1)"
  # Passing a range to `#in`     is deprecated. Call `#between`, instead.
  # Passing a range to `#not_in` is deprecated. Call `#not_between`, instead.

  # なんで動かない!?
  _ eq([0, 1]) rescue $!           # => #<TypeError: can't quote Array>
  _ eq(0..1)   rescue $!           # => #<TypeError: can't quote Range>
end

たまにやってしまう間違い

eq(nil) なら IS NULL にしてくれるので eq は万能だと勘違いして Array や Range を渡して次のように怒られることがあります。

_ eq([0, 1]) rescue $!           # => #<TypeError: can't quote Array>
_ eq(0..1)   rescue $!           # => #<TypeError: can't quote Range>

この場合、生成されるSQLを意識しつつ、配列なら in に、範囲なら between にする必要があります。

_ self.in([0, 1])                # => "x IN (0, 1)"
_ between(0..1)                  # => "x BETWEEN 0 AND 1"

簡単に書くなら arel_table ではなく where を使って普通に where(x: [0, 1])where(x: 0..1) と書けば良いです。 配列なら in で、範囲なら between に切り替えてくれます。

81
78
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
81
78