跳至內容 跳至搜尋
命名空間
方法
A
C
D
E
F
G
H
I
J
L
N
O
P
R
S
U
W

常數

FROZEN_EMPTY_ARRAY = [].freeze
 
FROZEN_EMPTY_HASH = {}.freeze
 
VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock, :limit, :offset, :joins, :left_outer_joins, :annotate, :includes, :eager_load, :preload, :from, :readonly, :having, :optimizer_hints])
 

執行個體公開方法

and(other)

傳回新的關聯,這是此關聯與傳入引數關聯的邏輯交集。

這兩個關聯必須在結構上相容:它們必須涵蓋相同的模型,且它們之間的差異只在於 where(如果沒有定義 group)或 having(如果存在 group)。

Post.where(id: [1, 2]).and(Post.where(id: [2, 3]))
# SELECT `posts`.* FROM `posts` WHERE `posts`.`id` IN (1, 2) AND `posts`.`id` IN (2, 3)
# File activerecord/lib/active_record/relation/query_methods.rb, line 1034
def and(other)
  if other.is_a?(Relation)
    spawn.and!(other)
  else
    raise ArgumentError, "You have passed #{other.class.name} object to #and. Pass an ActiveRecord::Relation object instead."
  end
end

annotate(*args)

將 SQL 註解新增至由此關聯產生的查詢。例如

User.annotate("selecting user names").select(:name)
# SELECT "users"."name" FROM "users" /* selecting user names */

User.annotate("selecting", "user", "names").select(:name)
# SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */

SQL 區塊註解分隔符號「/*」和「*/」會自動新增。

會執行一些跳脫,但不得使用不可信的使用者輸入。

# File activerecord/lib/active_record/relation/query_methods.rb, line 1428
def annotate(*args)
  check_if_method_has_arguments!(__callee__, args)
  spawn.annotate!(*args)
end

create_with(value)

設定在從關聯物件建立新記錄時要使用的屬性。

users = User.where(name: 'Oscar')
users.new.name # => 'Oscar'

users = users.create_with(name: 'DHH')
users.new.name # => 'DHH'

您可以傳遞 nilcreate_with 來重設屬性

users = users.create_with(nil)
users.new.name # => 'Oscar'
# File activerecord/lib/active_record/relation/query_methods.rb, line 1245
def create_with(value)
  spawn.create_with!(value)
end

distinct(value = true)

指定記錄是否應為唯一。例如

User.select(:name)
# Might return two records with the same name

User.select(:name).distinct
# Returns 1 record per distinct name

User.select(:name).distinct.distinct(false)
# You can also remove the uniqueness
# File activerecord/lib/active_record/relation/query_methods.rb, line 1309
def distinct(value = true)
  spawn.distinct!(value)
end

eager_load(*args)

指定要使用 LEFT OUTER JOIN 急切載入的關聯 args。執行單一查詢,加入所有指定的關聯。例如

users = User.eager_load(:address).limit(5)
users.each do |user|
  user.address.city
end

# SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ... FROM "users"
#   LEFT OUTER JOIN "addresses" ON "addresses"."id" = "users"."address_id"
#   LIMIT 5

所有地址都是使用單一加入查詢載入,而不是使用 5 個個別查詢載入 5 個地址。

可以使用雜湊和陣列載入多個和巢狀關聯,類似於 includes

User.eager_load(:address, friends: [:address, :followers])
# SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ... FROM "users"
#   LEFT OUTER JOIN "addresses" ON "addresses"."id" = "users"."address_id"
#   LEFT OUTER JOIN "friends" ON "friends"."user_id" = "users"."id"
#   ...

注意:在加入中載入關聯可能會導致許多包含重複資料的列,而且在擴充時效能不佳。

# File activerecord/lib/active_record/relation/query_methods.rb, line 267
def eager_load(*args)
  check_if_method_has_arguments!(__callee__, args)
  spawn.eager_load!(*args)
end

排除(*記錄)

從結果關聯中排除指定的記錄(或記錄集合)。例如

Post.excluding(post)
# SELECT "posts".* FROM "posts" WHERE "posts"."id" != 1

Post.excluding(post_one, post_two)
# SELECT "posts".* FROM "posts" WHERE "posts"."id" NOT IN (1, 2)

這也可以在關聯中呼叫。與上述範例一樣,可以指定單一記錄或集合

post = Post.find(1)
comment = Comment.find(2)
post.comments.excluding(comment)
# SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 AND "comments"."id" != 2

這是 .where.not(id: post.id).where.not(id: [post_one.id, post_two.id]) 的簡寫。

如果未指定任何記錄,或集合中的任何記錄(如果傳入集合)不是與關聯範圍相同的模型實例,則會引發 ArgumentError

別名為:without
# File activerecord/lib/active_record/relation/query_methods.rb, line 1470
def excluding(*records)
  records.flatten!(1)
  records.compact!

  unless records.all?(klass)
    raise ArgumentError, "You must only pass a single or collection of #{klass.name} objects to ##{__callee__}."
  end

  spawn.excluding!(records)
end

擴充(*模組,&區塊)

用於透過提供的模組或區塊來擴充範圍,並提供其他方法。

傳回的物件是關聯,可以進一步擴充。

使用模組

module Pagination
  def page(number)
    # pagination code goes here
  end
end

scope = Model.all.extending(Pagination)
scope.page(params[:page])

您也可以傳遞模組清單

scope = Model.all.extending(Pagination, SomethingElse)

使用區塊

scope = Model.all.extending do
  def page(number)
    # pagination code goes here
  end
end
scope.page(params[:page])

您也可以使用區塊和模組清單

scope = Model.all.extending(Pagination) do
  def per_page(number)
    # pagination code goes here
  end
end
# File activerecord/lib/active_record/relation/query_methods.rb, line 1355
def extending(*modules, &block)
  if modules.any? || block
    spawn.extending!(*modules, &block)
  else
    self
  end
end

提取關聯(關聯)

從關聯中提取指定的 關聯。首先預載入指定的關聯,然後從關聯中收集個別關聯記錄。如下所示

account.memberships.extract_associated(:user)
# => Returns collection of User records

這是

account.memberships.preload(:user).collect(&:user)
# File activerecord/lib/active_record/relation/query_methods.rb, line 318
def extract_associated(association)
  preload(association).collect(&association)
end

來自(值,子查詢名稱 = nil)

指定將從中擷取記錄的資料表。例如

Topic.select('title').from('posts')
# SELECT title FROM posts

可以接受其他關聯物件。例如

Topic.select('title').from(Topic.approved)
# SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery

傳遞第二個參數(字串或符號),會建立 SQL from 子句的別名。否則會使用別名「subquery」

Topic.select('a.title').from(Topic.approved, :a)
# SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a

它不會將多個參數新增到 SQL from 子句。最後鏈結的 from 會是使用中的

Topic.select('title').from(Topic.approved).from(Topic.inactive)
# SELECT title FROM (SELECT topics.* FROM topics WHERE topics.active = 'f') subquery

對於 SQL from 子句的多個參數,你可以傳遞一個字串,其中包含 SQL from 清單中的確切元素

color = "red"
Color
  .from("colors c, JSONB_ARRAY_ELEMENTS(colored_things) AS colorvalues(colorvalue)")
  .where("colorvalue->>'color' = ?", color)
  .select("c.*").to_a
# SELECT c.*
# FROM colors c, JSONB_ARRAY_ELEMENTS(colored_things) AS colorvalues(colorvalue)
# WHERE (colorvalue->>'color' = 'red')
# File activerecord/lib/active_record/relation/query_methods.rb, line 1290
def from(value, subquery_name = nil)
  spawn.from!(value, subquery_name)
end

group(*args)

允許指定群組屬性

User.group(:name)
# SELECT "users".* FROM "users" GROUP BY name

傳回一個陣列,其中包含根據 group 屬性區分的記錄

User.select([:id, :name])
# => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">]

User.group(:name)
# => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]

User.group('name AS grouped_name, age')
# => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]

也支援傳入一個屬性陣列來進行群組。

User.select([:id, :first_name]).group(:id, :first_name).first(3)
# => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
# File activerecord/lib/active_record/relation/query_methods.rb, line 512
def group(*args)
  check_if_method_has_arguments!(__callee__, args)
  spawn.group!(*args)
end

having(opts, *rest)

允許指定 HAVING 子句。請注意,你無法在未指定 GROUP 子句的情況下使用 HAVING。

Order.having('SUM(price) > 30').group('user_id')
# File activerecord/lib/active_record/relation/query_methods.rb, line 1096
def having(opts, *rest)
  opts.blank? ? self : spawn.having!(opts, *rest)
end

in_order_of(column, values)

允許指定特定值集的順序。

User.in_order_of(:id, [1, 5, 3])
# SELECT "users".* FROM "users"
#   WHERE "users"."id" IN (1, 5, 3)
#   ORDER BY CASE
#     WHEN "users"."id" = 1 THEN 1
#     WHEN "users"."id" = 5 THEN 2
#     WHEN "users"."id" = 3 THEN 3
#   END ASC
# File activerecord/lib/active_record/relation/query_methods.rb, line 620
def in_order_of(column, values)
  klass.disallow_raw_sql!([column], permit: connection.column_name_with_order_matcher)
  return spawn.none! if values.empty?

  references = column_references([column])
  self.references_values |= references unless references.empty?

  values = values.map { |value| type_caster.type_cast_for_database(column, value) }
  arel_column = column.is_a?(Arel::Nodes::SqlLiteral) ? column : order_column(column.to_s)

  where_clause =
    if values.include?(nil)
      arel_column.in(values.compact).or(arel_column.eq(nil))
    else
      arel_column.in(values)
    end

  spawn
    .order!(build_case_for_value_position(arel_column, values))
    .where!(where_clause)
end

includes(*args)

指定要急切載入的關聯 args 以防止 N + 1 查詢。會針對每個關聯執行個別查詢,除非條件需要聯結。

例如

users = User.includes(:address).limit(5)
users.each do |user|
  user.address.city
end

# SELECT "users".* FROM "users" LIMIT 5
# SELECT "addresses".* FROM "addresses" WHERE "addresses"."id" IN (1,2,3,4,5)

與使用 5 個個別查詢載入 5 個地址不同,所有地址都會使用單一查詢載入。

在個別查詢中載入關聯通常會比簡單聯結帶來更好的效能,因為聯結可能會產生許多包含重複資料的列,而且在擴充時效能不佳。

你也可以指定多個關聯。每個關聯都會產生一個額外的查詢

User.includes(:address, :friends).to_a
# SELECT "users".* FROM "users"
# SELECT "addresses".* FROM "addresses" WHERE "addresses"."id" IN (1,2,3,4,5)
# SELECT "friends".* FROM "friends" WHERE "friends"."user_id" IN (1,2,3,4,5)

可以使用雜湊載入巢狀關聯

User.includes(:address, friends: [:address, :followers])

條件

如果您想對所包含的模型加入字串條件,您必須明確地參照它們。例如

User.includes(:posts).where('posts.name = ?', 'example').to_a

會擲出錯誤,但這會執行

User.includes(:posts).where('posts.name = ?', 'example').references(:posts).to_a
# SELECT "users"."id" AS t0_r0, ... FROM "users"
#   LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
#   WHERE "posts"."name" = ?  [["name", "example"]]

由於 LEFT OUTER JOIN 已包含文章,因此不再執行文章的第二次查詢。

請注意,includes 可與關聯名稱搭配使用,而 references 需要實際的表格名稱。

如果您透過 Hash 傳遞條件,您不需要明確呼叫 references,因為 where 會為您參照表格。例如,這會正確執行

User.includes(:posts).where(posts: { name: 'example' })

注意:條件會影響關聯的兩側。例如,上述程式碼只會傳回具有名稱為「範例」的文章的使用者,而且只會包含名稱為「範例」的文章,即使符合條件的使用者有其他額外的文章。

# File activerecord/lib/active_record/relation/query_methods.rb, line 231
def includes(*args)
  check_if_method_has_arguments!(__callee__, args)
  spawn.includes!(*args)
end

invert_where()

允許您反轉整個 where 子句,而不是手動套用條件。

class User
  scope :active, -> { where(accepted: true, locked: false) }
end

User.where(accepted: true)
# WHERE `accepted` = 1

User.where(accepted: true).invert_where
# WHERE `accepted` != 1

User.active
# WHERE `accepted` = 1 AND `locked` = 0

User.active.invert_where
# WHERE NOT (`accepted` = 1 AND `locked` = 0)

請小心,因為這會反轉 invert_where 呼叫之前的所有條件。

class User
  scope :active, -> { where(accepted: true, locked: false) }
  scope :inactive, -> { active.invert_where } # Do not attempt it
end

# It also inverts `where(role: 'admin')` unexpectedly.
User.where(role: 'admin').inactive
# WHERE NOT (`role` = 'admin' AND `accepted` = 1 AND `locked` = 0)
# File activerecord/lib/active_record/relation/query_methods.rb, line 1000
def invert_where
  spawn.invert_where!
end

joins(*args)

args 執行 JOIN。給定的符號應與關聯名稱相符。

User.joins(:posts)
# SELECT "users".*
# FROM "users"
# INNER JOIN "posts" ON "posts"."user_id" = "users"."id"

多重 joins

User.joins(:posts, :account)
# SELECT "users".*
# FROM "users"
# INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
# INNER JOIN "accounts" ON "accounts"."id" = "users"."account_id"

巢狀 joins

User.joins(posts: [:comments])
# SELECT "users".*
# FROM "users"
# INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
# INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"

您可以使用字串自訂您的 joins

User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
# SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
# File activerecord/lib/active_record/relation/query_methods.rb, line 767
def joins(*args)
  check_if_method_has_arguments!(__callee__, args)
  spawn.joins!(*args)
end

left_joins(*args)

別名為:left_outer_joins

left_outer_joins(*args)

args 執行 LEFT OUTER JOIN

User.left_outer_joins(:posts)
# SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
別名:left_joins
# File activerecord/lib/active_record/relation/query_methods.rb, line 782
def left_outer_joins(*args)
  check_if_method_has_arguments!(__callee__, args)
  spawn.left_outer_joins!(*args)
end

limit(value)

指定要擷取的記錄數量上限。

User.limit(10) # generated SQL has 'LIMIT 10'

User.limit(10).limit(20) # generated SQL has 'LIMIT 20'
# File activerecord/lib/active_record/relation/query_methods.rb, line 1110
def limit(value)
  spawn.limit!(value)
end

lock(locks = true)

指定鎖定設定(預設為 true)。有關鎖定的詳細資訊,請參閱 ActiveRecord::Locking

# File activerecord/lib/active_record/relation/query_methods.rb, line 1137
def lock(locks = true)
  spawn.lock!(locks)
end

none()

傳回一個可鏈結的關係,其中不包含任何記錄。

傳回的關係實作 Null Object 模式。它是一個具有定義的 null 行為的物件,且總是傳回一個空的記錄陣列,而不會查詢資料庫。

任何後續鏈結到傳回關係的條件都會持續產生一個空的關係,且不會對資料庫發出任何查詢。

用於方法或範圍可能會傳回零個記錄,但結果需要可鏈結的情況。

例如

@posts = current_user.visible_posts.where(name: params[:name])
# the visible_posts method is expected to return a chainable Relation

def visible_posts
  case role
  when 'Country Manager'
    Post.where(country: country)
  when 'Reviewer'
    Post.published
  when 'Bad User'
    Post.none # It can't be chained if [] is returned.
  end
end
# File activerecord/lib/active_record/relation/query_methods.rb, line 1180
def none
  spawn.none!
end

offset(value)

指定在傳回列之前要略過的列數。

User.offset(10) # generated SQL has "OFFSET 10"

應與 order 搭配使用。

User.offset(10).order("name ASC")
# File activerecord/lib/active_record/relation/query_methods.rb, line 1126
def offset(value)
  spawn.offset!(value)
end

optimizer_hints(*args)

指定要在 SELECT 陳述式中使用的最佳化提示。

範例(適用於 MySQL)

Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
# SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`

範例(適用於 PostgreSQL 搭配 pg_hint_plan)

Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
# SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
# File activerecord/lib/active_record/relation/query_methods.rb, line 1384
def optimizer_hints(*args)
  check_if_method_has_arguments!(__callee__, args)
  spawn.optimizer_hints!(*args)
end

or(other)

傳回一個新的關聯,它是這個關聯和傳入引數的邏輯聯集。

這兩個關聯必須在結構上相容:它們必須涵蓋相同的模型,且它們之間的差異只在於 where(如果沒有定義 group)或 having(如果存在 group)。

Post.where("id = 1").or(Post.where("author_id = 3"))
# SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
# File activerecord/lib/active_record/relation/query_methods.rb, line 1066
def or(other)
  if other.is_a?(Relation)
    if @none
      other.spawn
    else
      spawn.or!(other)
    end
  else
    raise ArgumentError, "You have passed #{other.class.name} object to #or. Pass an ActiveRecord::Relation object instead."
  end
end

order(*args)

對查詢套用 ORDER BY 子句。

order 接受多種格式的引數。

符號

符號代表您要依其對結果排序的欄位名稱。

User.order(:name)
# SELECT "users".* FROM "users" ORDER BY "users"."name" ASC

預設情況下,順序是遞增的。如果您要遞減順序,您可以將欄位名稱符號對應到 :desc

User.order(email: :desc)
# SELECT "users".* FROM "users" ORDER BY "users"."email" DESC

可以這樣傳遞多個欄位,它們將按指定的順序套用。

User.order(:name, email: :desc)
# SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC

字串

字串會直接傳遞到資料庫,讓您可以指定簡單的 SQL 表達式。

這可能會造成 SQL 注入,因此只允許由純欄位名稱和簡單的 function(column_name) 表達式(加上選用的 ASC/DESC 修飾詞)組成的字串。

User.order('name')
# SELECT "users".* FROM "users" ORDER BY name

User.order('name DESC')
# SELECT "users".* FROM "users" ORDER BY name DESC

User.order('name DESC, email')
# SELECT "users".* FROM "users" ORDER BY name DESC, email

Arel

如果您需要傳入已驗證為資料庫安全的複雜表達式,您可以使用 Arel

User.order(Arel.sql('end_date - start_date'))
# SELECT "users".* FROM "users" ORDER BY end_date - start_date

自訂查詢語法(例如 PostgreSQL 的 JSON 欄位)以這種方式獲得支援。

User.order(Arel.sql("payload->>'kind'"))
# SELECT "users".* FROM "users" ORDER BY payload->>'kind'
# File activerecord/lib/active_record/relation/query_methods.rb, line 595
def order(*args)
  check_if_method_has_arguments!(__callee__, args) do
    sanitize_order_arguments(args)
  end
  spawn.order!(*args)
end

preload(*args)

指定要使用個別查詢急切載入的關聯 args。會針對每個關聯執行個別查詢。

users = User.preload(:address).limit(5)
users.each do |user|
  user.address.city
end

# SELECT "users".* FROM "users" LIMIT 5
# SELECT "addresses".* FROM "addresses" WHERE "addresses"."id" IN (1,2,3,4,5)

它會使用個別查詢載入所有地址,而不是使用 5 個個別查詢載入 5 個地址。

可以使用雜湊和陣列載入多個和巢狀關聯,類似於 includes

User.preload(:address, friends: [:address, :followers])
# SELECT "users".* FROM "users"
# SELECT "addresses".* FROM "addresses" WHERE "addresses"."id" IN (1,2,3,4,5)
# SELECT "friends".* FROM "friends" WHERE "friends"."user_id" IN (1,2,3,4,5)
# SELECT ...
# File activerecord/lib/active_record/relation/query_methods.rb, line 299
def preload(*args)
  check_if_method_has_arguments!(__callee__, args)
  spawn.preload!(*args)
end

readonly(value = true)

將關聯標記為唯讀。嘗試更新記錄將導致錯誤。

users = User.readonly
users.first.save
=> ActiveRecord::ReadOnlyRecord: User is marked as readonly

若要將唯讀關聯設為可寫,請傳遞 false

users.readonly(false)
users.first.save
=> true
# File activerecord/lib/active_record/relation/query_methods.rb, line 1208
def readonly(value = true)
  spawn.readonly!(value)
end

references(*table_names)

用於指示 SQL 字串會參考指定的 table_names,因此應在任何查詢中以 +JOIN+ 方式聯結,而不是個別載入。此方法僅與 includes 搭配使用。有關更多詳細資訊,請參閱 includes

User.includes(:posts).where("posts.name = 'foo'")
# Doesn't JOIN the posts table, resulting in an error.

User.includes(:posts).where("posts.name = 'foo'").references(:posts)
# Query now knows the string references posts, so adds a JOIN
# File activerecord/lib/active_record/relation/query_methods.rb, line 332
def references(*table_names)
  check_if_method_has_arguments!(__callee__, table_names)
  spawn.references!(*table_names)
end

regroup(*args)

允許您變更先前設定的群組陳述式。

Post.group(:title, :body)
# SELECT `posts`.`*` FROM `posts` GROUP BY `posts`.`title`, `posts`.`body`

Post.group(:title, :body).regroup(:title)
# SELECT `posts`.`*` FROM `posts` GROUP BY `posts`.`title`

這是 unscope(:group).group(fields) 的簡寫。請注意,我們取消套用整個群組陳述式。

# File activerecord/lib/active_record/relation/query_methods.rb, line 532
def regroup(*args)
  check_if_method_has_arguments!(__callee__, args)
  spawn.regroup!(*args)
end

reorder(*args)

以指定的順序取代關聯上定義的任何現有順序。

User.order('email DESC').reorder('id ASC') # generated SQL has 'ORDER BY id ASC'

在同一個關聯上後續呼叫 order 將會附加。例如

User.order('email DESC').reorder('id ASC').order('name ASC')

會產生一個包含 ORDER BY id ASC, name ASC 的查詢。

# File activerecord/lib/active_record/relation/query_methods.rb, line 651
def reorder(*args)
  check_if_method_has_arguments!(__callee__, args) do
    sanitize_order_arguments(args)
  end
  spawn.reorder!(*args)
end

reselect(*args)

允許您變更先前設定的選取陳述式。

Post.select(:title, :body)
# SELECT `posts`.`title`, `posts`.`body` FROM `posts`

Post.select(:title, :body).reselect(:created_at)
# SELECT `posts`.`created_at` FROM `posts`

這是 unscope(:select).select(fields) 的簡寫。請注意,我們取消套用整個選取陳述式。

# File activerecord/lib/active_record/relation/query_methods.rb, line 480
def reselect(*args)
  check_if_method_has_arguments!(__callee__, args)
  args = process_select_args(args)
  spawn.reselect!(*args)
end

reverse_order()

反轉關係中現有的順序子句。

User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
# File activerecord/lib/active_record/relation/query_methods.rb, line 1397
def reverse_order
  spawn.reverse_order!
end

rewhere(conditions)

允許您變更先前設定的特定屬性的 where 條件,而不是附加到該條件。

Post.where(trashed: true).where(trashed: false)
# WHERE `trashed` = 1 AND `trashed` = 0

Post.where(trashed: true).rewhere(trashed: false)
# WHERE `trashed` = 0

Post.where(active: true).where(trashed: true).rewhere(trashed: false)
# WHERE `active` = 1 AND `trashed` = 0

這是 unscope(where: conditions.keys).where(conditions) 的簡寫。請注意,與 reorder 不同,我們只取消套用指定的名稱條件,而不是整個 where 陳述式。

# File activerecord/lib/active_record/relation/query_methods.rb, line 960
def rewhere(conditions)
  return unscope(:where) if conditions.nil?

  scope = spawn
  where_clause = scope.build_where_clause(conditions)

  scope.unscope!(where: where_clause.extract_attributes)
  scope.where_clause += where_clause
  scope
end

select(*fields)

以兩種獨特的方式運作。

首先:取得區塊,因此可以使用它就像 Array#select 一樣。

Model.all.select { |m| m.field == value }

這會為範圍建立一個資料庫物件陣列,將它們轉換成陣列並使用 Array#select 進行反覆運算。

其次:修改查詢的 SELECT 陳述式,以便只擷取特定欄位

Model.select(:field)
# => [#<Model id: nil, field: "value">]

儘管在上述範例中,這個方法看起來會傳回陣列,但它實際上傳回關係物件,並且可以附加其他查詢方法,例如 ActiveRecord::QueryMethods 中的其他方法。

這個方法的引數也可以是欄位的陣列。

Model.select(:field, :other_field, :and_one_more)
# => [#<Model id: nil, field: "value", other_field: "value", and_one_more: "value">]

引數也可以是欄位和別名的雜湊。

Model.select(models: { field: :alias, other_field: :other_alias })
# => [#<Model id: nil, alias: "value", other_alias: "value">]

Model.select(models: [:field, :other_field])
# => [#<Model id: nil, field: "value", other_field: "value">]

您也可以使用一個或多個字串,這些字串將不變地用作 SELECT 欄位。

Model.select('field AS field_one', 'other_field AS field_two')
# => [#<Model id: nil, field_one: "value", field_two: "value">]

如果指定了別名,則可以從產生的物件中存取它

Model.select('field AS field_one').first.field_one
# => "value"

存取 select 擷取的欄位以外的物件屬性(id 除外)將會擲回 ActiveModel::MissingAttributeError

Model.select(:field).first.other_field
# => ActiveModel::MissingAttributeError: missing attribute 'other_field' for Model
# File activerecord/lib/active_record/relation/query_methods.rb, line 390
def select(*fields)
  if block_given?
    if fields.any?
      raise ArgumentError, "`select' with block doesn't take arguments."
    end

    return super()
  end

  check_if_method_has_arguments!(__callee__, fields, "Call `select' with at least one field.")

  fields = process_select_args(fields)
  spawn._select!(*fields)
end

strict_loading(value = true)

將傳回的關聯設定為 strict_loading 模式。如果記錄嘗試延遲載入關聯,這將會引發錯誤。

user = User.strict_loading.first
user.comments.to_a
=> ActiveRecord::StrictLoadingViolationError
# File activerecord/lib/active_record/relation/query_methods.rb, line 1223
def strict_loading(value = true)
  spawn.strict_loading!(value)
end

structurally_compatible?(other)

檢查給定的關聯是否與此關聯在結構上相容,以確定是否可以在不引發錯誤的情況下使用 andor 方法。結構上相容的定義為:它們必須作用於同一個模型,而且它們之間的差異僅在於 where(如果沒有定義 group)或 having(如果存在 group)。

Post.where("id = 1").structurally_compatible?(Post.where("author_id = 3"))
# => true

Post.joins(:comments).structurally_compatible?(Post.where("id = 1"))
# => false
# File activerecord/lib/active_record/relation/query_methods.rb, line 1020
def structurally_compatible?(other)
  structurally_incompatible_values_for(other).empty?
end

uniq!(name)

重複多個值。

# File activerecord/lib/active_record/relation/query_methods.rb, line 1440
def uniq!(name)
  if values = @values[name]
    values.uniq! if values.is_a?(Array) && !values.empty?
  end
  self
end

unscope(*args)

移除已定義在關聯鏈上的不需要關聯。當傳遞關聯鏈並希望修改關聯而不重建整個鏈時,這會很有用。

User.order('email DESC').unscope(:order) == User.all

方法參數是符號,對應於應取消作用域的方法名稱。有效的參數在 VALID_UNSCOPING_VALUES 中給出。該方法也可以使用多個參數呼叫。例如

User.order('email DESC').select('id').where(name: "John")
    .unscope(:order, :select, :where) == User.all

此外,還可以傳遞一個雜湊作為參數,以取消作用域特定的 :where 值。這通過傳遞具有單個鍵值對的雜湊來完成。鍵應該是 :where,值應該是取消作用域的 where 值。例如

User.where(name: "John", active: true).unscope(where: :name)
    == User.where(active: true)

此方法類似於 except,但與 except 不同的是,它會持續存在於合併中

User.order('email').merge(User.except(:order))
    == User.order('email')

User.order('email').merge(User.unscope(:order))
    == User.all

這表示它可以用於關聯定義中

has_many :comments, -> { unscope(where: :trashed) }
# File activerecord/lib/active_record/relation/query_methods.rb, line 705
def unscope(*args)
  check_if_method_has_arguments!(__callee__, args)
  spawn.unscope!(*args)
end

where(*args)

傳回一個新關聯,這是根據參數中的條件篩選目前關聯的結果。

where 接受多種格式的條件。在以下範例中,會提供產生的 SQL 作為說明;根據資料庫配接器不同,實際產生的查詢可能有所不同。

字串

單一字串(沒有其他參數)會傳遞給查詢建構函式作為 SQL 片段,並用於查詢的 where 子句中。

Client.where("orders_count = '2'")
# SELECT * from clients where orders_count = '2';

請注意,如果您沒有正確地從使用者輸入建立自己的字串,可能會讓您的應用程式暴露在注入攻擊中。建議您改用下列其中一種方法。

陣列

如果傳遞陣列,則陣列的第一個元素會視為範本,而其餘元素會插入範本中以產生條件。Active Record 會負責建立查詢以避免注入攻擊,並會視需要從 Ruby 類型轉換為資料庫類型。元素會按照出現順序插入字串中。

User.where(["name = ? and email = ?", "Joe", "[email protected]"])
# SELECT * FROM users WHERE name = 'Joe' AND email = '[email protected]';

或者,您可以在範本中使用命名佔位符,並將雜湊傳遞為陣列的第二個元素。範本中的名稱會替換為雜湊中對應的值。

User.where(["name = :name and email = :email", { name: "Joe", email: "[email protected]" }])
# SELECT * FROM users WHERE name = 'Joe' AND email = '[email protected]';

這可以在複雜查詢中產生更易讀的程式碼。

最後,您可以在範本中使用 sprintf 格式的 % 逸出字元。這與前述方法略有不同;您有責任確保範本中的值已正確加上引號。這些值會傳遞給連接器加上引號,但呼叫者有責任確保它們在產生的 SQL 中加上引號。加上引號後,會使用 Ruby 核心方法 Kernel::sprintf 的相同逸出字元插入這些值。

User.where(["name = '%s' and email = '%s'", "Joe", "[email protected]"])
# SELECT * FROM users WHERE name = 'Joe' AND email = '[email protected]';

如果 where 呼叫時帶有多個參數,則會視為這些參數傳遞為單一陣列的元素。

User.where("name = :name and email = :email", { name: "Joe", email: "[email protected]" })
# SELECT * FROM users WHERE name = 'Joe' AND email = '[email protected]';

當使用字串來指定條件時,你可以使用資料庫中提供的任何運算子。雖然這提供了最大的彈性,但你也有可能無意間引入對底層資料庫的依賴性。如果你的程式碼是打算供一般使用,請使用多個資料庫後端進行測試。

雜湊

where 也會接受雜湊條件,其中金鑰是欄位,而值是要搜尋的值。

欄位可以是符號或字串。值可以是單一值、陣列或範圍。

User.where(name: "Joe", email: "[email protected]")
# SELECT * FROM users WHERE name = 'Joe' AND email = '[email protected]'

User.where(name: ["Alice", "Bob"])
# SELECT * FROM users WHERE name IN ('Alice', 'Bob')

User.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight)
# SELECT * FROM users WHERE (created_at BETWEEN '2012-06-09 07:00:00.000000' AND '2012-06-10 07:00:00.000000')

對於 belongs_to 關係,如果使用 ActiveRecord 物件作為值,則可以使用關聯金鑰來指定模型。

author = Author.find(1)

# The following queries will be equivalent:
Post.where(author: author)
Post.where(author_id: author)

這也適用於多型 belongs_to 關係

treasure = Treasure.create(name: 'gold coins')
treasure.price_estimates << PriceEstimate.create(price: 125)

# The following queries will be equivalent:
PriceEstimate.where(estimate_of: treasure)
PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: treasure)

雜湊 條件也可以用類似元組的語法指定。 雜湊 金鑰可以是具有元組陣列作為值的欄位陣列。

Article.where([:author_id, :id] => [[15, 1], [15, 2]])
# SELECT * FROM articles WHERE author_id = 15 AND id = 1 OR author_id = 15 AND id = 2

聯結

如果關係是聯結的結果,你可以建立一個條件,使用聯結中的任何資料表。對於字串和陣列條件,請在條件中使用資料表名稱。

User.joins(:posts).where("posts.created_at < ?", Time.now)

對於雜湊條件,你可以在金鑰中使用資料表名稱,或使用子雜湊。

User.joins(:posts).where("posts.published" => true)
User.joins(:posts).where(posts: { published: true })

無引數

如果沒有傳遞引數,where 會傳回 WhereChain 的新執行個體,它可以與 WhereChain#notWhereChain#missingWhereChain#associated 鏈結。

WhereChain#not 鏈結

User.where.not(name: "Jon")
# SELECT * FROM users WHERE name != 'Jon'

WhereChain#associated 鏈結

Post.where.associated(:author)
# SELECT "posts".* FROM "posts"
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# WHERE "authors"."id" IS NOT NULL

WhereChain#missing 鏈結

Post.where.missing(:author)
# SELECT "posts".* FROM "posts"
# LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# WHERE "authors"."id" IS NULL

空白條件

如果條件是任何空白物件,則 where 是 no-op,並傳回目前的關係。

# File activerecord/lib/active_record/relation/query_methods.rb, line 932
def where(*args)
  if args.empty?
    WhereChain.new(spawn)
  elsif args.length == 1 && args.first.blank?
    self
  else
    spawn.where!(*args)
  end
end

with(*args)

新增一個共用資料表表達式 (CTE),然後你可以在另一個 SELECT 陳述式中參照它。

注意:CTE 僅支援 MySQL 8.0 以上版本。您無法在 MySQL 5.7 中使用 CTE。

Post.with(posts_with_tags: Post.where("tags_count > ?", 0))
# => ActiveRecord::Relation
# WITH posts_with_tags AS (
#   SELECT * FROM posts WHERE (tags_count > 0)
# )
# SELECT * FROM posts

定義共用表格運算式後,您可以使用自訂 FROM 值或 JOIN 來參照它。

Post.with(posts_with_tags: Post.where("tags_count > ?", 0)).from("posts_with_tags AS posts")
# => ActiveRecord::Relation
# WITH posts_with_tags AS (
#  SELECT * FROM posts WHERE (tags_count > 0)
# )
# SELECT * FROM posts_with_tags AS posts

Post.with(posts_with_tags: Post.where("tags_count > ?", 0)).joins("JOIN posts_with_tags ON posts_with_tags.id = posts.id")
# => ActiveRecord::Relation
# WITH posts_with_tags AS (
#   SELECT * FROM posts WHERE (tags_count > 0)
# )
# SELECT * FROM posts JOIN posts_with_tags ON posts_with_tags.id = posts.id

建議傳遞查詢作為 ActiveRecord::Relation。如果這不可行,而且您已驗證資料庫安全,您可以使用 Arel 將其傳遞為 SQL 文字。

Post.with(popular_posts: Arel.sql("... complex sql to calculate posts popularity ..."))

應特別小心避免 SQL 注入漏洞。此方法不應與包含未整理輸入的不安全值一起使用。

若要新增多個 CTE,只需傳遞多個鍵值對

Post.with(
  posts_with_comments: Post.where("comments_count > ?", 0),
  posts_with_tags: Post.where("tags_count > ?", 0)
)

或串連多個 .with 呼叫

Post
  .with(posts_with_comments: Post.where("comments_count > ?", 0))
  .with(posts_with_tags: Post.where("tags_count > ?", 0))
# File activerecord/lib/active_record/relation/query_methods.rb, line 459
def with(*args)
  check_if_method_has_arguments!(__callee__, args)
  spawn.with!(*args)
end

without(*records)

別名:excluding

執行個體保護方法

async!()

# File activerecord/lib/active_record/relation/query_methods.rb, line 1539
def async!
  @async = true
  self
end