Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Mongoid "criteria" work?

I'm trying to do something straight forward such as:

User.all(:criteria => {:project_id => 2})

This returns an instance of Mongoid::Criteria

What can I do with this criteria? What if I just want the array of documents returned for further evaluation?

like image 431
randombits Avatar asked Sep 28 '11 19:09

randombits


3 Answers

To get an array from a Mongoid::Criteria: use the method .to_a

like image 156
K Farkye Avatar answered Nov 10 '22 04:11

K Farkye


In Mongoid, the criteria represents the query, not the elements. You can think of a criteria as a filter, a scope, a query object.

Once you have a criteria (scope), you can get the elements, executing an actual query to the database, with a method that is supposed to iterate over the elements or return one element, for example: .first, .last, .to_a, .each, .map, etc.

This is more efficient and allows you to compose a complex "query" from other simple ones.

For example, you can create some named scopes in your class:

class User
  include Mongoid::Document
  field :name,  type: String
  field :age,   type: Integer
  field :admin, type: Boolean

  scope :admins, where(admin: true) # filter users that are admins
  scope :with_name, (name)-> { where(name: name) } # filter users with that name
end

Then you can create some criteria objects:

admins      = User.admins
johns       = User.with_name('John')
admin_johns = User.admins.with_name('John') # composition of criterias, is like doing ANDs
young       = User.where(:age.lt => 25) # the Mongoid method .where also returns a criteria

Up to this point, you didn't fire any query to the mongo database, you were just composing queries.

At any time, you can keep chaining criterias, to refine the query even further:

young_admins = admins.merge(young)
old_admins = admins.where(age.gt => 60)

And finally, get the Array with the elements:

# Execute the query and an array from the criteria
User.all.to_a
User.admins.to_a
admins.to_a
young_admins.to_a

# Execute the query but only return one element
User.first
admins.first
johns.last

# Execute the query and iterate over the returned elements
User.each{|user| ... }
User.admins.each{|admin_user| ... }
johns.map{|john_user| ... }

So, define some named scopes in the Class, then use them to create a criteria, and do the real query when you need it (lazy loading). Criterias handle all of this for you even if you didn't know you needed it.

like image 43
tothemario Avatar answered Nov 10 '22 03:11

tothemario


Criteria is something like Relation object in ActiveRecord

You can use it this way

users = User.where(:project_id => 2)
users.each do |user|
  puts user.name
end
# or
users.all

This will return [] if there is no any user

users.all.each do |user|
  puts user.name
end
like image 40
fl00r Avatar answered Nov 10 '22 04:11

fl00r