Hi I am ROR developer and using rails 3.2.13 with Postgres database.
I have two models:
Question
attr_accessible :category, :is_active, :question_text, :question_type_id, :survey_id,
:user_id
has_many :abusive_questions
And
AbusiveQuestion
attr_accessible :question_id, :user_id, :ipaddress, :posted_by
belongs_to :question
From this I want to get the AbusiveQuestion which count is greater than a particular value (ex: 5).
I did the following from my rails command
AbusiveQuestion.count(:group=>"abusive_questions.question_id")
and got
=> {1=>1, 5=>3, 3=>1}
For this result, the key is the question_id and value is the count but, I want to get the question which value is greater then a particular dynamic value (for ex:2).
Please help me.
I think you'll want to join Question on AbusiveQuestion then use a select and having to get what you want.
Something like:
AbusiveQuestion.select('abusive_questions.*, count(question.id) as question_count').
joins(:questions).
group('abusive_questions.question_id').
having('count(abusive_questions.question_id) > 5')
You can use having method.
http://guides.rubyonrails.org/active_record_querying.html#having
x = YOUR_DYNAMIC_COUNT
AbusiveQuestion.group(:question_id).having("count_all > ?", x).count
Related
I have a Student model and that model has_many subscriptions. I need to return all students who have subscriptions. this object must contain only the student's name, email, phone and status, in addition to the subscription id and recurrence_code. I thought something more or less like this but I can't succeed in the query:
students = Student.all.includes(:subscriptions)
students.each do |student|
student.select(:id, :name, :email, :phone, :status, subscription: {
methods: [:id, :recurrency_code]}
end
This is a classic inner join scenario
class Student < ApplicationRecord
has_many :subscriptions
end
class Subscription < ApplicationRecord
belongs_to :student
end
I find it helpful to break these problems into steps:
"Only Students where a Subscription record is present" is a standard inner join:
Student.joins(:subscriptions).uniq
"object must contain only the student's name, email, phone and status"
Student.joins(:subscriptions).select(:name, :email, :phone, :status).uniq
"in addition to the subscription id and recurrence_code"
students = Student.joins(:subscriptions)
.select(
'students.name, students.email,'\
'students.phone, students.status, '\
'subscriptions.id as subscription_id,'\
'subscriptions.recurrence_code as subscription_recurrence_code'
)
A few notes:
1. Using select with joins
#vee's SO Answer here points out:
If the column in select is not one of the attributes of the model on which the select is called on then those columns are not displayed. All of these attributes are still contained in the objects within AR::Relation and are accessible as any other public instance attributes.
This means if you load an individual record (e.g. students.first), you will only see the Student attributes by default. But you can still access the Subscription attributes by the as name you set in the query. E.g.:
students.first.subscription_recurrence_code
2. Use .uniq to eliminate duplicates.
Because of the has_many relationship, the query Student.joins(:subscriptions) will return a record for each subscription, which means each student will appear in the result the same number of times as they have subscriptions. Calling .uniq (short for unique) will remove duplicates from the result.
I'm agree with the Chiperific response, but I disagree to use the uniq method because it doesn't call the 'DISTINCT' in the SQL query.
Rails: uniq vs. distinct
For me it's better to use distinct. So the query could be as this:
Student.joins(:subscriptions).distinct.select(
:name, :email, :phone, :status,
'subscriptions.id AS subscription_id',
'subscriptions.recurrence_code'
)
I have following models in my rails app:
class Student < ApplicationRecord
has_many :tickets, dependent: :destroy
has_and_belongs_to_many :articles, dependent: :destroy
class Article < ApplicationRecord
has_and_belongs_to_many :students, dependent: :destroy
class Ticket < ApplicationRecord
belongs_to :student, touch: true
I need to extract all Students who has less than articles and I need to extract all Students who's last ticket title is 'Something'.
Everything I tried so far takes a lot of time. I tried mapping and looping through all Students. But I guess what I need is a joined request. I am looking for the most efficient way to do it, as database I am working with is quite large.
go with #MCI's answer for your first question. But a filter/select/find_all or whatever (although I havn't heared about filter method in ruby) through students record takes n queries where n is the number of student records (called an n+1 query).
studs = Student.find_by_sql(%{select tmp.id from (
select student_id as id from tickets where name='Something' order by tickets.created_at desc
) tmp group by tmp.id})
You asked
"I need to extract all Students who has less than articles". I'll presume you meant "I need to extract all Students who have less than X articles". In that case, you want group and having https://guides.rubyonrails.org/active_record_querying.html#group.
For example, Article.group(:student_id).having('count(articles.id) > X').pluck(:student_id).
To address your second question, you can use eager loading https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations to speed up your code.
result = students.filter do |student|
students.tickets.last.name == 'Something'
end
Here association is HABTM so below query should work
x = 10
Student.joins(:articles).group("articles_students.student_id").having("count(articles.id) < ?",x)
I'm trying to create a rails scope based on the count of a model's HABTM assocation, but I'm struggling with the SQL.
I want Match.open to return matches with less than two users. I also have Match.upcoming, which returns matches with a 'future_date' in the future, which is working well.
My code:
class Match < ActiveRecord::Base
has_and_belongs_to_many :users
scope :open, joins('matches_users').
select('*').
group('matches.id').
having('count(matches_users.user_id) < 2')
scope :upcoming, lambda {
where("proposed_date between ? and ?", Date.today, Date.today.next_month.beginning_of_month)
}
I'm currently getting the error:
SQLite3::SQLException: no such column: matches_users.user_id: SELECT * FROM "matches" matches_users GROUP BY matches.id HAVING count(matches_users.user_id) < 2
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: matches_users.user_id: SELECT * FROM "matches" matches_users GROUP BY matches.id HAVING count(matches_users.user_id) < 2
I'm currently achieving this with a class method:
def self.open
self.select{|match| match.users.length < 2}
end
Which works, but I'd really like to move this into a scope for speed, and so that I can chain the scopes like Match.open.upcoming.
What am I doing wrong here? What's the correct way to do this? Any help would be appreciated.
Give this a shot - I've used something similar before and it seems to work for me:
class Match < ActiveRecord::Base
has_and_belongs_to_many :users
scope :open, joins(:matches_users)
.select('matches.*')
.group('matches.id')
.having('count(matches_users.id) < 2')
...
end
I am a rails newbie and am trying to perform a search on a table with rails, and i'm just using my sql knowledge to do this. But this just doesn't seems like rails or ruby even...
Is there any better way to do what i'm doing below? (basically, only pass date arguments to sql if they are filled)
def search(begin_date=nil, end_date=nil)
subject = " and created_at "
if !(begin_date.nil? || end_date.nil?)
where_part = subject + "BETWEEN :begin_date AND :end_date"
else if (begin_date.nil? && end_date.nil?)
where_part = ""
else if(begin_date.nil?)
where_part = subject + " <= :end_date"
else if (end_date.nil?)
where_part = subject + " >= :begin_date"
end
end
end
end
User.joins(places: {containers: {label: :user}}).where("users.id= :user_id "+where_part, user_id: self.id, begin_date:begin_date, end_date:end_date).group(...).select(...)
end
EDIT
user.rb
has_many :containers
has_many :user_places
has_many :places, through: :user_places
has_many :labels
place.rb
has_many :containers
has_many :user_places
has_many :users, through: :user_places
container.rb
belongs_to :label
belongs_to :place
belongs_to :user
label.rb
belongs_to :user
has_many :containers
Basically, i want to get a count of the number of containers within a given user's labels or with a direct relationship, per location, and want to be able to filter it by begin and end dates.
Either of this dates may be nil, and so i would need to address this in my "query".
My question is : How can i do this the rails way? I took a look at http://guides.rubyonrails.org/active_record_querying.html and perhaps i could use the except command here somewhere...but this relationship model just seems a bit complex to do this with ActiveRecord...how may I?, i really think i should use ActiveRecord, but how?
Thank you
You can apply multiple where calls to a query so you can build your base query:
query = User.joins(...)
.group(...)
.select(...)
.where('users.id = :user_id', :user_id => self.id)
and then add another where call depending on your date interval:
if(begin_date && end_date)
query = query.where(:created_at => begin_date .. end_date)
# or where('created_at between :begin_date and :end_date', :begin_date => begin_date, :end_date => end_date)
elsif(begin_date)
query = query.where('created_at >= :begin_date', :begin_date => begin_date)
elsif(end_date)
query = query.where('created_at <= :end_date', :end_date => end_date)
end
Each where call adds another piece to your overall WHERE clause using AND so something like:
q = M.where(a).where(b).where(c)
is the same as saying WHERE a AND b AND c.
I cant think of a great reason why you would actually want to generate SQL in your code. Active record seems like a much more efficient solution for your needs, unless there is a reason why you cant use that.
Link explaining how to join tables with active record
I have a table called FeedItems, and basically the user needs to see the first created feed item for each post id.
At the moment I'm using 'group', and SQLite is giving me the last created, for some reason. I've tried to sort the list of feed items before grouping, but it makes no difference.
user_id | post_id | action
----------------------------------------
1 1 'posted'
3 2 'loved' <--- this, being created afterwards
should not appear in the query.
I know this has to do with an INNER JOIN, and I have seen some similar examples of this being done, however the difference is that I'm not sure how to do this AND use the existing query I already have to find out if the users are friends with the current user.
Here's the code for the model:
class FeedItem < ActiveRecord::Base
belongs_to :post
belongs_to :user
default_scope :order => 'created_at desc'
scope :feed_for, lambda { |user| feed_items_for(user).group(:post_id) }
private
def self.feed_items_for(user)
friend_ids = %(SELECT friend_id FROM friendships WHERE (user_id = :user_id OR friend_id = :user_id))
ghosted_ids = %(SELECT pending_friend_id FROM friend_requests WHERE user_id = :user_id)
where("user_id IN (#{friend_ids}) OR user_id IN (#{ghosted_ids}) OR user_id = :user_id", {:user_id => user.id})
end
end
Any help would be greatly appreciated, thanks.
ActiveRecord provides you with dynamic attribute-based finders, which means that you have
Feeds.find_last_by_post_id(117)
If you would prefer to have the fist, you have:
Feeds.where(:post_id => 117).first
And you can always do, which I'm not sure is a "best practice":
Feeds.where(:post_id => 117).order('created_on DESC').first
You can read more about it at:
http://api.rubyonrails.org/classes/ActiveRecord/Base.html