rails query by count on has_many relationship - ruby-on-rails

I have a model, for example, Post, and a Post has_many comments. I would like to query posts in order of posts with the most comments to the least. How can I do this with an active record query?
Right now I am trying:
Post.includes(:comments).order('COUNT(comments.post_id) DESC')
but I am getting the error:
ActiveRecord::StatementInvalid (PG::UndefinedTable: ERROR: missing FROM-clause entry for table "comments")

Post.left_outer_joins(:comments)
.group(:id) # required by Postgres
.order(Arel.sql('COUNT(comments.*) DESC')) # https://github.com/rails/rails/issues/32995
If you want to use the count in the result you can select it as well:
Post.left_outer_joins(:comments)
.select('posts.*, COUNT(comments.*) AS comment_count')
.group(:id) # required by Postgres
.order('comment_count DESC') # Use COUNT(comments.*) in Oracle
Another way to solve this is by using counter_cache.

Related

Update_all query with joins in rails console

I have an trackers table and applications tables
application.rb
has_many :trackers
tracker.rb
belongs_to :application
What I trying to do is update the check_in_date in the trackers table to be query to begin_date in the applications tables only for those records which have check_in_date is equal to "2019-05-30".
I am trying to run the command below but I am getting an error.
Tracker.joins(:application).where("check_in_date = ?", "2019-05-30").update_all("tracker.check_in_date = application.begin_date")
error
ActiveRecord::StatementInvalid (PG::UndefinedTable: ERROR: missing FROM-clause entry for table "application")
Any idea where i am going wrong.
Maybe try this:
Note I have no idea if this will work so don't do it in production unless you can confirm
Tracker.where(check_in_date: "2019-05-30")
.update_all("check_in_date = (#{
Application.select(:begin_date)
.where('applications.id = trackers.application_id').to_sql})"
)
The theory is that this should result in the following SQL
UPDATE trackers
SET check_in_date = (
SELECT
begin_date
FROM
applications
WHERE
applications.id = trackers.application_id
)
WHERE
trackers.check_in_date = "2019-05-30"
There seems some typo
if check_in_date is date type then convert is "2019-05-30" in date
my_check_in_date = "2019-05-30".to_date
While using any attribute in the query model name should always be plural
Tracker.joins(:application)
.where("trackers.check_in_date= ?", my_check_in_date)
.update_all("trackers.check_in_date = applications.begin_date")
.references(:application)

Using joins to query by attribute on associated recored

I currently have this horribly written query:
membership_ids = User.where(skip_membership_renewal: true).includes(:memberships).map(&:membership_ids).flatten
Memberships.where(id: membership_ids)
I have been trying to use joins so that I can just make one query.
Membership.includes(:user).where("user.skip_membership_renewal", true)
However, this doesn't work since I keep getting the error: ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR.
My relationship is:
User has_many :memberships
Membership belongs_to :user
What am I doing incorrectly?
You just have a pluralization error. In Rails, you define models as singular (User) and the database table is pluralized (users).
Membership.includes(:user).where("users.skip_membership_renewal" => true)
That said, you don't need to resort to using SQL literals for such a simple case. There are a bunch of other ways of assembling this query, like the scope option David Aldridge suggested, or either of these:
non_renewing_users = User.where(skip_membership_renewal: true)
Membership.joins(:user).merge(non_renewing_users)
Membership.where(user: non_renewing_users)
What's more is that these both only execute a single SQL query for most adapters because they use subqueries:
SELECT "memberships".*
FROM "memberships"
WHERE "memberships"."user_id" IN (
SELECT "users"."id" FROM "users"
WHERE "users"."skip_membership_renewal" = true
)
You can probably aim to use:
Membership.where(:user => User.skip_membership_renewal)
Add a scope onto User ...
def self.skip_membership_renewal
where(skip_membership_renewal: true)
end
You should find that it runs as a single query.

Active Record doesn't update collection when there is joins and includes in query

Hello I've a problem with my query.
There are my models below:
class Owner
has_many :busiensses
has_many :adverts
end
class Business
belongs_to :owner
end
class Advert
belongs_to :owner
end
When I make this query everything is okay and it returns right collection full of needed objects:
Owner.joins(:adverts).includes(:businesses)
.where(businesses: {owner_id: nil})
But when I add to query update it raises error
Owner.joins(:adverts).includes(:businesses)
.where(businesses: {owner_id: nil})
.update_all(status: 'sth')
Error:
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "businesses"
Where is the problem? I bet this error from SQL and it raised when you forget add sth to FROM statement and that sth needed in further conditions, but where in AR i forgot to add it?
Owner.joins(:adverts)
.includes(:businesses)
.where(businesses: {owner_id: 1})
.update_all(name: "blaaaaa")
This statement translates into this query:
UPDATE "owners"
SET "name" = 'blaaaaa'
FROM "businesses" /* missed this */
WHERE "owners"."id" IN
(SELECT "owners"."id" FROM "owners"
INNER JOIN "adverts"
ON "adverts"."owner_id" = "owners"."id"
WHERE "businesses"."owner_id" = 1)
You miss the "FROM 'bussinesses'" which causes the error:
missing FROM-clause entry for table "businesses"
My solution is to use joins instead of using includes. It works fine in my machine.
Owner.joins(:adverts)
.joins(:businesses)
.where(businesses: {owner_id: 1})
.update_all(name: "blaaaaa")
=> 1

Rails & Active Record query to find parent events without a child

An Event has_many Votes and a Vote belongs_to a User. How can I get a user's event's that they personally have not voted on? This was my best shot:
Event.includes(:votes).where.not("votes.user_id = ?", current_user.id)
But I am getting the below error:
PG::UndefinedTable: ERROR: missing FROM-clause entry for table
"votes"
You need to either use a hash within the final parenthesis or use references as per Active Record Query Interface. Here's the resulting code:
Event.includes(:votes).where.not("votes.user_id = ?", current_user.id).references(:votes)

Why does it recognize as Unknown Column?

I'm trying to sort ordered by updated_at of User record, which is associated from Code table.
#codes = Code.joins(:user).where('body like ?', "%"+params[:search]+"%").order('user.updated_at DESC').page(params[:page]).per(10)
However, it won't let me sort:(
This is the error message I get.
Error Message
Mysql2::Error: Unknown column 'user.created_at' in 'order clause
Your database table should be users not user (plural not singular). Update your order method as follows:
order('users.updated_at DESC')

Resources