I need to create a named scope in Mongoid that compares two Time fields within the same document. Such as
scope :foo, :where => {:updated_at.gt => :checked_at}
This obviously won't work as it treats :checked_at as a symbol, not the actual field. Any suggestions on how this can be done?
Update 1
Here is my model where I have this scope declared, with a lot of extra code stripped out.
class User
include Mongoid::Document
include Mongoid::Paranoia
include Mongoid::Timestamps
field :checked_at, :type => Time
scope :unresolved, :where => { :updated_at.gt => self.checked_at }
end
This gives me the following error:
'<class:User>': undefined method 'checked_at' for User:Class (NoMethodError)
As far as I know, mongodb doesn't support queries against dynamic values.
But you could use a javascript function:
scope :unresolved, :where => 'this.updated_at >= this.checked_at'
To speed this up you could add an attribute like "is_unresolved" which will be set to true on update when this condition is matched ( and index that ).
scope :foo, :where => {:updated_at.gt => self.checked_at}
For example, this will work:
scope :foo, where(:start_date.lte=>Date.today.midnight)
Not sure if you'll like this method, it's not the best, but it should work.
class User
include Mongoid::Document
include Mongoid::Paranoia
include Mongoid::Timestamps
field :checked_at, :type => Time
scope :unresolved, lambda{ |user| where(:updated_at.gt => user.checked_at) }
end
You call it with User.unresolved(my_user_object)
It seems now after rereading your post that this probably won't do what you want. If this is true, then you will probably need to use MapReduce or possibly Baju's method (have not tested it)
Related
How do you add an "IS NOT NULL" condition for associations in Thinking Sphinx search? For example if we have an article model which has the following index..
ThinkingSphinx::Index.define :article, :with => :active_record do
indexes subject, :sortable => true
indexes content
has pictures(:id), as: :picture_ids
end
..and we want to search for all articles which contain a certain keyword and have a picture. Articles and pictures are related by a simple has_many relationship
class Article < ActiveRecord::Base
has_many :pictures, -> { where 'pictures.type' => 'ArticlePicture' }
The following line used to work, as it is described here, but it no longer seems to work :-(
Article.search(keyword, without: {picture_ids: 0})
What is the correct way to do it? I am using Sphinx 2.2.10 and thinking-sphinx 3.2.0
You can add an additional attribute with a SQL snippet:
has "COUNT(DISTINCT pictures.id)", :as => :picture_count, :type => :integer
And then - once you've run rake ts:rebuild - I'd expect the following to work:
Article.search(keyword, :without => {:picture_count => 0})
It's important to note that you'll still need a reference to the pictures association in your index definition to ensure there's a SQL join. This is done by your existing attribute (picture_ids), or otherwise you can force the join using the following line within the index definition:
join pictures
I have a multi tenant Rails app.
I use this line of code in each of the models:
default_scope { where(tenant_id: Tenant.current_id) }
I have a model called worequest and it contains an integer column called request_closed. I would like each tenant to define which statuscode is closed for them.
After the default_scope statement, I'm trying to set a scope for requests that are not closed.
I've tried the following and they don't work:
scope :closed, where(:statuscode_id => Tenant.current.request_closed)
scope :closed, where(:statuscode_id => current_tenant.request_closed)
scope :closed, where(:statuscode_id => Tenant.request_closed)
scope :closed, where(:statuscode_id => Tenant.current_id.request_closed)
Is it possible to do what I want?
Thanks for the help!
Seeing as request_closed is an attribute on Tenant, you'll need to retrieve your current tenant, using your current_id value, and then call it on that.
Tenant.find(Tenant.current_id).request_closed
I'm using Mongoid::Versioning which works great except that I would like to prevent several fields from being versioned.
There's not a lot of info written about it in the docs so I'm not sure how to do this.
http://mongoid.org/docs/extras.html
class Person
include Mongoid::Document
include Mongoid::Versioning
# keep at most 5 versions of a record
max_versions 5
end
They show how to skip a version altogether but not how to restrict certain fields from being versioned.
Any ideas?
UPDATE
I found something like this digging through the code, but I'm not sure how to use it.
https://github.com/mongoid/mongoid/blob/master/lib/mongoid/versioning.rb#L90
All Field has option :versioned true by default If you don't want this versionned you can pass false. By exemple I want name versioned but no login
class User
include Mongoid::Document
include Mongoid::Versioning
field :name, :type => String
field :login, :type => String, :versioned => false
end
You can pass the :versioned option in embed association too.
You can override this option by iterating over the .fields on your Document.
So in your code you can add avoid versionned on some field by creating a little methode :
class User
include Mongoid::Document
include Mongoid::Versioning
include Mongoid::Voteable
field :name, :type => String
field :login, :type => String
def self.avoid_versioned(*unversioned_fields)
unversioned_fields.each do |f|
fe = self.fields[f.to_s]
fe.options[:versioned] = false if fe
re = self.relations[f.to_s]
re[:versioned] = false if re
end
end
avoid_versioned( :login, :votes )
end
You can probably find a way to do this, but I would suggest checking out this gem instead.
https://github.com/aq1018/mongoid-history
track_history :on => [:title, :body], # I want to track title and body fields only. Default is :all
:modifier_field => :modifier, # Adds "referened_in :modifier" to track who made the change. Default is :modifier
:version_field => :version, # Adds "field :version, :type => Integer" to track current version. Default is :version
:track_create => false, # Do you want to track document creation? Default is false
:track_update => true, # Do you want to track document updates? Default is true
:track_destroy => false, # Do you want to track document destruction? Default is false
You may skip versioning at any point in time by wrapping the persistence call in a versionless block.
class Person
include Mongoid::Document
include Mongoid::Versioning
end
person.versionless do |doc|
doc.update_attributes(name: "Theodore")
end
Rails 3.1, ActiveAdmin 0.3.4.
My question is somewhat similar to this one but different enough in terms of data modeling that I think it warrants its own response. Models:
class CheckoutRequest < ActiveRecord::Base
has_one :request_common_data, :as => :requestable, :dependent => :destroy
end
class RequestCommonData < ActiveRecord::Base
belongs_to :requestable, :polymorphic => true
end
The RequestCommonData model has a completed field (boolean) that I'd like to be able to filter in ActiveAdmin's CheckoutRequest index page. I've tried a few different approaches to no avail, including the following:
filter :completed, :collection => proc { CheckoutRequest.all.map { |cr| cr.request_common_data.completed }.uniq }
which results in no filter being displayed. Adding :as => :select to the line, as follows:
filter :completed, :as => :select, :collection => proc { CheckoutRequest.all.map { |cr| cr.request_common_data.completed }.uniq }
results in the following MetaSearch error message:
undefined method `completed_eq' for #<MetaSearch::Searches::CheckoutRequest:0x007fa4d8faa558>
That same proc returns [true, false] in the console.
Any suggestions would be quite welcome. Thanks!
From the meta_search gem page you can see that for boolean values the 'Wheres' are:
is_true - Is true. Useful for a checkbox like “only show admin users”.
is_false - The complement of is_true.
so what you need is to change the generate input name from 'completed_eq' to be 'completed_is_true' or 'completed_is_false'.
The only way I have found this possible to do is with Javascript, since by looking at the Active Admin code, the 'Wheres' are hardcoded for each data type.
I would usually have a line like this in my activeadmin.js file (using jQuery)
$('#q_completed_eq').attr('name', 'q[completed_is_true]');
or
$('#q_completed_eq').attr('name', 'q[completed_is_false]');
Terrible and ugly hack but have found no other solution myself.
Be careful to enable this only in the pages you want.
--- NEW FOR VERSION 0.4.2 and newer ---
Now Active Admin uses separate modules for each :as => ... option in the filters.
So for example you can place the code below inside an initializer file
module ActiveAdmin
module Inputs
class FilterCustomBooleanInput < ::Formtastic::Inputs::SelectInput
include FilterBase
def input_name
"#{#method}_is_true"
end
def input_options
super.merge(:include_blank => I18n.t('active_admin.any'))
end
def method
super.to_s.sub(/_id$/,'').to_sym
end
def extra_input_html_options
{}
end
end
end
end
and the use
:as => :custom_boolean
where you specify your filter.
Problem fixed... Turned out there was an active record method that got over written, now everything works as expected
I am trying to set up scopes so I can make a call that looks like
Competitor.of_type(type).at_event(event)
that will return all Competitors of type that attended event
My Models looks something like
class Competitor < Competitor
belongs_to :type
has_and_belongs_to_many :events
scope :at_event, ->(event) {where(:event_ids.in => event.competitor_ids)}
scope :of_type, ->(type) where(:type_id => type.id)
end
The following works (return mongoid criteria)
Competitor.of_type(type)
Competitor.at_event(event)
But when I chain them, it prints out something that looks like this:
#<Competitor:0x00000109e2b210>
#<Competitor:0x00000109e2ab08>
-------=-=------------------------------------
=> #<Mongoid::Criteria
selector: {},
options: {},
class: Competitor,
embedded: false>
There is a Competitor entry for each of Competitor.of_type(type) (the first chained criteria) and if I run .count on the query, I get the total number of Competitors in the database.
At the top of the mongoid documentation for scopes, it says All scopes are chainable and can be applied to associations as well, the later being discussed in the relations section.
Unfortunately I did not see a relations sub section, not could I find a single reference to scope in the main relations section.
I was able to get the following to return the results I wanted:
where(:id.in => event.competitor_ids).where(:type_id => type.id)
but if any part of the query is split into a separate method or scope it fails and provides the result I showed above.
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)
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