How do you set up chainable scopes based on relations in Mongoid
Although @MZaragoza's answer was complete, it seems that this syntax is no longer allowed:
scope :rock_n_rolla, where(occupation: "Rockstar")
Use procs instead:
summary:
Scopes in Mongoid must be procs that wrap criteria objects.
resolution:
Change the scope to be a proc wrapped critera.
Example:
class Band
include Mongoid::Document
scope :inactive, ->{ where(active: false) }
end
Mongoid v 7.0.3
scopes
Similar to Active Record, Mongoid allows you to define scopes on your models as a convenience for filtering result sets. Scopes are defined at the class level, either using the scope macro or by defining class methods that return a criteria object. All scopes are chainable and can be applied to associations as well, the later being discussed in the relations section.
Named scopes are defined at the class level using a scope macro and can be chained to create result sets in a nice DSL.
class Person
include Mongoid::Document
field :occupation, type: String
field :age, type: Integer
scope :rock_n_rolla, where(occupation: "Rockstar")
scope :washed_up, where(:age.gt => 30)
scope :over, ->(limit) { where(:age.gt => limit) }
end
# Find all the rockstars.
Person.rock_n_rolla
# Find all rockstars that should probably quit.
Person.washed_up.rock_n_rolla
# Find a criteria with Keith Richards in it.
Person.rock_n_rolla.over(60)
Note that definitions are evaluated at class load time. For evaluation at runtime you will want to make sure to define using a proc or lambda. In the following example the first date is set as the date of class load, where the second scope sets the date at the time the scope is called.
scope :current, where(:start_date.lte => Date.today)
scope :current, -> { where(:start_date.lte => Date.today) }
class methods
For those who prefer a Data Mapper style syntax, class methods that return criteria can be treated as chainable scopes as well.
class Person
include Mongoid::Document
field :occupation, type: String
field :age, type: Integer
class << self
def rock_n_rolla
where(occupation: "Rockstar")
end
def washed_up
where(:age.gt => 30)
end
def over(limit)
where(:age.gt => limit)
end
end
end
# Find all the rockstars.
Person.rock_n_rolla
# Find all rockstars that should probably quit.
Person.washed_up.rock_n_rolla
# Find a criteria with Keith Richards in it.
Person.rock_n_rolla.over(60)
Named scopes and class methods that return a criteria can be chained together - that's the beauty of Mongoid's powerful criteria API.
class Person
include Mongoid::Document
field :occupation, type: String
field :age, type: Integer
scope :washed_up, where(:age.gt => 30)
scope :over, ->(limit) { where(:age.gt => limit) }
def self.rock_n_rolla
where(occupation: "Rockstar")
end
end
# Same queries apply here as well.
Person.rock_n_rolla
Person.washed_up.rock_n_rolla
Person.rock_n_rolla.over(60)