范围界定
Overview
在本指南中,您可以学习;了解如何在 Mongoid 模型中实现作用域。作用域提供了一种重复使用常用过滤条件的便捷方法。要学习;了解有关创建过滤条件的更多信息,请参阅指定文档查询指南。
如果您对大多数查询应用相同的条件,则可以在应用应用程序中实现作用域以减少重复代码。
命名范围
命名范围是在类加载时定义的条件,由提供的名称引用。 与过滤条件类似,它们是延迟加载且可链接的。
此示例定义了一个 Band
模型,其中包括以下命名范围:
japanese
:匹配country
字段值为"Japan"
的文档rock
:匹配其中genre
字段的值包含"rock"
的文档
class Band include Mongoid::Document field :country, type: String field :genres, type: Array scope :japanese, ->{ where(country: "Japan") } scope :rock, ->{ where(:genres.in => [ "rock" ]) } end
然后,您可以使用命名范围进行查询。 以下查询使用命名作用域来匹配 country
字段值为 "Japan"
且 genre
字段值包括 "rock"
的文档:
Band.japanese.rock
高级范围界定
您可以在命名范围中定义 Proc
对象和区块,以便它们接受参数并扩展功能。
此示例定义了一个包含 based_in
作用域的 Band
模型,该模型匹配 country
字段值为作为参数传递的指定值的文档:
class Band include Mongoid::Document field :name, type: String field :country, type: String scope :based_in, ->(country){ where(country: country) } end
然后,您可以使用 based_in
范围进行查询,如以下代码所示:
Band.based_in("Spain")
Mongoid 允许您定义一个作用域来隐藏现有的类方法,如以下示例所示:
class Band include Mongoid::Document def self.on_tour true end scope :on_tour, ->{ where(on_tour: true) } end
通过将 scope_overwrite_exception
配置选项设置为 true
,您可以指示 Mongoid 在作用域覆盖现有类方法时引发错误。
默认作用域
如果您对大多数查询应用相同的条件,则默认作用域非常有用。 通过定义默认范围,您可以将这些条件指定为使用该模型的任何查询的默认。 默认作用域返回 Criteria
对象。
要创建默认作用域,您必须在模型类上定义 default_scope
方法。
以下代码在 Band
模型上定义 default_scope
方法,以仅检索active
字段值为 true
的文档:
class Band include Mongoid::Document field :name, type: String field :active, type: Boolean default_scope -> { where(active: true) } end
然后,对 Band
模型的任何查询都会预筛选 active
值为 true
的文档。
字段初始化
默认范围会将新模型的字段初始化为默认范围中给定的值,默认是这些值是文字,例如布尔值或整数。
注意
字段和作用域冲突
如果在字段定义和默认域中提供默认值,则默认域中的值优先,如以下示例所示:
class Band include Mongoid::Document field :name, type: String field :on_tour, type: Boolean, default: true default_scope ->{ where(on_tour: false) } end # Creates a new Band instance in which "on_tour" is "false" Band.new
我们不建议使用点表示法来引用默认作用域中的默认字段。 这可能会指示 Mongoid 在新模型中初始化意外字段。
示例,如果定义引用 tour.year
字段的默认作用域,则新模型将使用字段tour.year
进行初始化,而不是使用带有包含 year
字段的嵌套对象的 tour
字段。
查询 时,Mongoid 会正确解释点表示法,并匹配嵌套字段具有指定值的文档。
关联
如果在属于关联的模型上定义默认作用域,则必须重新加载关联才能重新应用作用域。 如果您更改关联中的文档值,从而在应用范围时影响其可见性,则必须执行此操作。
此示例使用以下模型:
class Label include Mongoid::Document field :name, type: String embeds_many :bands end class Band include Mongoid::Document field :name, type: String field :active, default: true embedded_in :label default_scope ->{ where(active: true) } end
假设您创建一个 Label
模型,其中包含与 Band
的关联,其中 active
的值为 true
。 当您将 active
字段更新为 false
时,尽管默认作用域,Mongoid 仍会加载该字段。 要查看应用范围的关联中的文档,您必须调用 reload
操作符。
以下代码演示了此序列:
label = Label.new(name: "Hello World Records") band = Band.new(name: "Ghost Mountain") label.bands.push(band) label.bands # Displays the Band because "active" is "true" band.update_attribute(:active, false) # Updates "active" to "false" # Displays the "Ghost Mountain" band label.bands # => {"_id":"...","name":"Ghost Mountain",...} # Won't display "Ghost Mountain" band after reloading label.reload.bands # => nil
or 和 nor 查询行为
Mongoid 将默认范围内的条件视为任何其他查询条件。 在使用 or
和 nor
方法时,这可能会导致令人惊讶的行为。
以下示例演示了 Mongoid 如何解释对具有默认范围的模型的查询:
class Band include Mongoid::Document field :name field :touring field :member_count default_scope ->{ where(touring: true) } end # Combines the condition to the default scope with "and" Band.where(name: 'Infected Mushroom') # Interpreted query: # {"touring"=>true, "name"=>"Infected Mushroom"} # Combines the first condition to the default scope with "and" Band.where(name: 'Infected Mushroom').or(member_count: 3) # Interpreted query: # {"$or"=>[{"touring"=>true, "name"=>"Infected Mushroom"}, {"member_count"=>3}]} # Combines the condition to the default scope with "or" Band.or(member_count: 3) # Interpreted query: # {"$or"=>[{"touring"=>true}, {"member_count"=>3}]}
查询时禁用作用域
您可以使用 unscoped
操作符指示 Mongoid 不要应用默认范围,如以下示例所示:
# Inline example Band.unscoped.where(name: "Depeche Mode") # Block example Band.unscoped do Band.where(name: "Depeche Mode") end
在运行时覆盖默认作用域
您可以使用 with_scope
方法在运行时更改区块中的默认作用域。
以下模型定义了命名范围 mexican
:
class Band include Mongoid::Document field :country, type: String field :genres, type: Array scope :mexican, ->{ where(country: "Mexico") } end
您可以使用 with_scope
方法在运行时将 mexican
命名作用域设立为默认作用域,如以下代码所示:
Band.with_scope(Band.mexican) do Band.all end
类方法
Mongoid 将返回 Criteria
对象的类方法视为作用域。 您可以使用这些类方法查询,如以下示例所示:
class Band include Mongoid::Document field :name, type: String field :touring, type: Boolean, default: true def self.touring where(touring: true) end end Band.touring