1

There's a view where I want to display the contents of

for example:

recipe.user.name 
recipe.name
recipe.picture
recipe.created_at

sorted by

recipe.likes.count

Controller:

@recipes_by_likes = Recipe.likes_count.paginate(page: params[:page])

Model:

Recipe has_many :likes

and

Like belongs_to :recipe

Scope:

scope :likes_count, -> { 
  select('recipes.*, COUNT(likes.id) AS likes_count').
  joins(:likes).
  order('likes_count DESC')
}

but that gives me

SELECT  recipes.*, COUNT(likes.id) AS likes_count 
FROM "recipes" 
INNER JOIN "likes" ON "likes"."recipe_id" = "recipes"."id"  
ORDER BY "recipes"."created_at" DESC, likes_count DESC 
LIMIT 30 OFFSET 0

which returns only 1 (wrong) result.

Oss
  • 4,232
  • 2
  • 20
  • 35
Stef Hej
  • 1,367
  • 2
  • 14
  • 23
  • Possible duplicate of [Rails 3 ActiveRecord: Order by count on association](http://stackoverflow.com/questions/8696005/rails-3-activerecord-order-by-count-on-association) – David Aldridge Nov 28 '15 at 13:36

1 Answers1

2

1. Query

You should not count likes by their id since it is always unique and will always return 1, instead by the recipe_id which is the same in the likes associated with a certain recipe.

scope :likes_count, -> { 
  select('recipes.*, COUNT(likes.recipe_id) AS likes_count')
  .joins(:likes)
  .group('recipes.id')
  .order('likes_count DESC')
}

2. Counter Cache Method

Also you can use a counter_cache

class Like < ActiveRecord::Base
  belongs_to :recipe, counter_cache: true
end

And add a column to Recipe model

add_column :recipes, :likes_count, :integer, default: 0
Oss
  • 4,232
  • 2
  • 20
  • 35
  • getting: SELECT recipes.*, COUNT(likes.recipe_id) AS likes_count FROM "recipes" INNER JOIN "likes" ON "likes"."recipe_id" = "recipes"."id" ORDER BY "recipes"."created_at" DESC, likes_count DESC LIMIT 30 OFFSET 0 but only 1 result – Stef Hej Nov 28 '15 at 00:52
  • Why is there a `LIMIT` and `OFFSET`? Anyways try to add `group("likes.recipe_id")` to your above query and tell me what you get. – Oss Nov 28 '15 at 00:54
  • SELECT recipes.*, COUNT(likes.recipe_id) AS likes_count FROM "recipes" INNER JOIN "likes" ON "likes"."recipe_id" = "recipes"."id" GROUP BY likes.recipe_id ORDER BY "recipes"."created_at" DESC, likes_count DESC LIMIT 30 OFFSET 0 .. 7 results (except of recipes with no likes) in wrong order – Stef Hej Nov 28 '15 at 01:03
  • Updated the answer. Also added the `counter_cache` method – Oss Nov 28 '15 at 12:03
  • 1
    added counter_cache as you described - works like a charm! – Stef Hej Nov 28 '15 at 13:33