ActiveRecord query with conditionals on nested associations - ruby-on-rails

I've got a Person model, who has_many roles, and roles, in turn, belong_to an application. I'd like to query all the roles a person has for a given application. So far I've got:
p = Person.includes(:roles => [:application]).where(:loginid => 'their_loginid', :roles => {:application_id => 1})
Which works, but it's querying based on Person.roles.application_id; instead, I'd like to query based on Person.roles.application.api_key (another property of an application).
I tried:
p = Person.includes(:roles => [:application]).where(:loginid => 'their_loginid', :roles => {:application => {:api_key => 'the_api_key'}})
but I receive the error that:
no such column: application.api_key
leading me to think my usage of ActiveRecord isn't joining the tables together correctly.
Any ideas?

Try this:
p = Person.includes(:roles => [:application]).where(:loginid => 'their_loginid', :role_id => Application.find_by_api_key('api_key').role_ids)

try this
p = Person.joins.includes(:roles => [:application]).where(:loginid => 'their_loginid', :roles => {:application => {:api_key => 'the_api_key'}})
In my case it woks.

Related

Slow performance from rake task - How to streamline these Rails queries

I am experiencing slow performance on a rake task. Upon review, it looks like the same item is being queried 3 times. Is there a way to simplify this so only one query is executed? Would this have a significant performance improvement? Would love to hear your ideas.
Here's a breakdown...
So I check if it exists:
if Ecommerce.exists?(:legacy_id => legacy_id, :company => company)
Then the same item is queried again to pluck a value needed:
historical_interest = Ecommerce.where(:legacy_id => legacy_id, :company => company).pluck(:interest)
Then if the plucked value is different, the record is updated:
if interest != historical_interest
Ecommerce.where(:legacy_id => legacy_id, :company => company).update_all(:interest => interest, :end_date => end_date, :revenue => revenue)
end
else
#other stuff
Full code:
if Ecommerce.exists?(:legacy_id => legacy_id, :company => company)
# It exists.
historical_interest = Ecommerce.where(:legacy_id => legacy_id, :company => company).pluck(:interest)
# Another statement to check that the Interest != UnitSold
if interest != historical_interest
Ecommerce.where(:legacy_id => legacy_id, :company => company).update_all(:interest => interest, :end_date => end_date, :revenue => revenue)
end
else
#other stuff
this should help a little:
ecommerce_array = Ecommerce.where(legacy_id: legacy_id, company: company)
if ecommerce_array.any?
historical_interest = ecommerce_array.pluck(:interest)
return unless interest == historical_interest
ecommerce_array.update_all(....)
else
#....
EDIT:
change this historical_interest = ecommerce_array.pluck(:interest) into this historical_interest = ecommerce_array.collect(&:interest)

Mongoid not in query

I have some trouble with mongoid:
test "Test candidate" do
User.create(:id => 1, :sex => User::Male, :country => 1, :city => 1)
User.create(:id => 2, :sex => User::Female, :country => 1, :city => 1)
User.create(:id => 3, :sex => User::Female, :country => 1, :city => 1)
user = User.not_in(:id => [2]).second
assert_not_equal(user.id, 2)
end
Test failed. I've tried to use where(:id => {'$nid' => [2]}), but it have same effect.
What is wrong? How to use "not in" condition with mongoid?
PS, "second" is ok, with "first" test passed, because id=1
Try this query:
user = User.not_in(:_id => [2]).second
In MongoDB primary key has name _id. Mongoid tries to be friendly and partially hides this fact from the developer by aliasing it to id in the object model. But when you do queries, it cannot tell if you want primary key _id or some completely ordinary field id.
user = User.where(:id.nin => [2,3,4,5])
This is as per mongoid official doc : http://mongoid.org/en/origin/docs/selection.html

Writing query with new 'where'

I am trying to do a simple query with Rails 3 where clause.
I have the following:
Participant.find(:first, :conditions => ["participants.role = ? AND participants.board_id = ?", "Manager", board.id])
Which works very well. I am trying to rewrite it the Rails 3 way as follows:
Participant.where(:board => board, :role => "Manager")
However this does not work. Is there a way to stipulate first with the where to get the same return as above?
Participant.where(:board_id => board.id, :role => "Manager").first

use a named_scope to select according to a count value

For example, say I have something like BlogCategory which has a HABTM with BlogPost, and I want to select only the BlogCategories that have actually been used in a BlogPost
named_scope :published, {
:include => :blog_posts,
:select => 'blog_categories.*, count(blog_posts.id) as post_count',
:group => 'blog_categories.id having post_count > 0',
:conditions => 'blog_posts.published = 1',
}
Problem I'm having is that the :select part of this seems to be getting completely ignored by rails, so the count field doesn't get put into the query, and I end up with the error "Unknown column 'post_count' in 'having clause'"
I don't know why it doesn't recognize post_count but it should work with:
:group => 'blog_categories.id having count(blog_posts.id) > 0',

rails find: using conditions while including same table twice via different named associations

I have posts which are sent by users to other users. There are two models - :post and :user, and :post has the following named associations:
belongs_to :from_user, :class_name => "User", :foreign_key => "from_user_id"
belongs_to :to_user, :class_name => "User", :foreign_key => "to_user_id"
Both :user and :post have "is_public" column, indicating that either a single post and/or the entire user profile can be public or private.
My goal is to fetch a list of posts which are public AND whose recipients have public profiles. At the same time, I would like to "include" both sender and recipient info to minimize the # of db calls. The challenge is that I am effectively "including" the same table twice via the named associations, but in my "conditions" I need to make sure that I only filter by the recipient's "is_public" column.
I can't do the following because "conditions" does not accept an association name as a parameter:
Post.find(:all, :include => [ :to_user, :from_user ],
:conditions => { :is_public => true, :to_user => { :is_public => true }})
So, one way I can accomplish this is by doing an additional "join" on the "users" table:
Post.find(:all, :include => [ :to_user, :from_user ],
:joins => "inner join users toalias on posts.to_user_id = toalias.id",
:conditions => { :is_public => true, 'toalias.is_public' => true })
Is there a better, perhaps cleaner, way to do this?
Thanks in advance
I was facing the same problem and found a solution after watching sql query generated from rails query, sql query automatically generates an alias
try this,
Post.find(:all, :include => [ :to_user, :from_user ],
:conditions => { :is_public => true, 'to_users_posts.is_public' => true })
It worked for me :)
I have not been able to find a better solution than the one originally stated in my question. This one doesn't depend on how Rails names/aliases tables when compiling a query and therefore appears to be cleaner than the alternatives (short of using 3rd party gems or plugins):
Post.find(:all, :include => [ :to_user, :from_user ],
:joins => "inner join users toalias on posts.to_user_id = toalias.id",
:conditions => { :is_public => true, 'toalias.is_public' => true })
If you are on Rails 3, take a look at squeel gem, esp if you are doing these kind of complex joins often. Or if you dont want to add a extra gem, take look at the Arel table in Rails 3.
I very pleasant for that question, because I've tried to find proper solution about 2h, so after using few tips above, I've found the proper, in my case, solution. My case:
I need filter instances by created_by_id/updated_by_id fields, that fields are in every table, so...what I did
In concern 'Filterable' I wrote the method and when I needed filter by that fields I used that ->
key = "#{key.pluralize}_#{name.pluralize.downcase}.email" if %w(created_by updated_by).include?(key)
# in case with invoices key = 'updated_bies_invoices.email'
results = results.eager_load(:created_by, :updated_by).where("#{key} = ?", value)

Resources