Get the most voted with Mongoid and rails (something like count and group by) - ruby-on-rails-3

I have three documents
class User
include Mongoid::Document
has_many :votos
...
...
end
class Picture
include Mongoid::Document
has_many :votos
belongs_to :user
...
...
end
class Voto
include Mongoid::Document
belongs_to :picture
belongs_to :user
field :value => :type => Integer
...
...
end
In the Voto document, the field value is a number between 1 and 5
So i need to get the most voted pictures of all to show...
How can i achieve this???
Thanks

You can do it by querying also but it will take hell of a time and performance will decrease. The other solution is create a field total_votos in model Picture and whenever a vote is given to a picture add the field value into total_votes
class Picture
include Mongoid::Document
has_many :votos
belongs_to :user
field :total_votes,:type => Integer
...
...
end
class Voto
include Mongoid::Document
belongs_to :picture
belongs_to :user
field :value => :type => Integer
after_create do
picture = self.picture
picture.total_votes += self.value
picture.save
end
...
...
end
and you can find the maximum value just by running the query
Picture.where(:max_votes => Picture.all.max(:total_votes))

Related

Rails - eager load has_many :through with condition on association

Let's say I have an Item model and Category model with has_many :through association:
class Item < ActiveRecord::Base
has_many :category_items
has_many :categories, through: category_items
end
class Category < ActiveRecord::Base
has_many :category_items
has_many :items, through: category_items
end
class CategoryItems < ActiveRecord::Base
belongs_to :category
belongs_to :items
end
now, I want to have a scope on items that will get all items that are in specific status (assume it has status attribute) for specific category. for example: get all items with status "in stock" and which belongs to category with id = 3, something like:
scope :in_stock_for_category, ->(category) { where(status: SOME_ENUMERATED_VALUE) ....
i'm missing the last part of the query to limit the result set to the specific category.
Thanks!
Since you don't have a category_id column in your items table, you need to join either category_items or cateogeries in your scope before you can specify a particular category's ID condition.
class Item < ActiveRecord::Base
scope :in_stock_for_category, -> do |category|
joins(:category_items).
where(category_items: {category_id: category.id}).
where(items: {status: SOME_ENUMERATED_VALUE}).
group("items.id") # grouping might be unnecessary since you're adding the where condition for the category's id
end
end
That will work. Or if you want to join categories, do the following:
class Item < ActiveRecord::Base
scope :in_stock_for_category, -> do |category|
joins(:categories).
where(categories: {id: category.id}).
where(items: {status: SOME_ENUMERATED_VALUE}).
group("items.id") # grouping might be unnecessary since you're adding the where condition for the category's id
end
end
If you already have a category however, it might be useful to create a has_many relationship for a items that have a certain status. Something like the following:
class Category < ActiveRecord::Base
has_many :in_stock_items, -> do
where(items: {status: SOME_ENUMERATED_VALUE})
end, through: :category_items, source: :item
end
Also, if you have a status scope in Item (something like scope :in_stock, -> { where(status: SOME_ENUMERATED_VALUE) }), you can most likely change the above has_many relationship to the following:
class Category < ActiveRecord::Base
has_many :in_stock_items, -> do
merge(Item.in_stock) # http://apidock.com/rails/ActiveRecord/SpawnMethods/merge
end, through: :category_items, source: :item
end
That should tidy things up.

Rails 3: has_many through query

I have these three Active Record models:
class Event < ActiveRecord::Base
has_many :event_categories, inverse_of: :event
has_many :categories, through: :event_categories
end
class EventCategory < ActiveRecord::Base
belongs_to :event
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :event_categories
has_many :events, through: :event_categories
end
I think the relations are good.
If I want to know what Events have a Category, for example id=5.. I do:
Category.find(5).events
But, if I want to know all Events for more than one category, for example:
Category.where(:id => [3,5]).events
It isn't working. Any ideas?
Please note, when you do has_many :events in a model, Active Record defines a method of name events for that class.
When you do Category.find(5).events, you get events associated with one object (i.e. Category.find(5)) , however Category.where(:id => [3,5]) returns an array of Category objects, so you can't use events function on an array, Only way to get events for all search results is iterate over them and access them individually, something like following:
all_events = Category.where(:id => [3,5]).inject([]) {|res,cat| res << cat.events}
Above code will do one query per iteration, to avoid this, we can include events, in the first query itself, like following, which will provide result in only one query:
all_events = Category.includes(:events).where(:id => [3,5]).inject([]) {|res,cat| res << cat.events}

Rails active record query through multiple tables

I am trying to query for questions based on subject or category. I have a Category model which has many Subjects, and a Subjects model which has many Questions. How do I select 50 questions where the subject_id = x or category_id = y? I'm not sure if I need to change my model associations then query or use a query with the current associations. Here are the models (stripped of some excess code):
category.rb
class Category < ActiveRecord::Base
has_many :subjects, class_name: "Subject",
foreign_key: "category_id"
has_many :questions, through: :subjects
end
subject.rb
class Subject < ActiveRecord::Base
belongs_to :category
has_many :questions, class_name: "Question",
foreign_key: "subject_id"
end
question.rb
class Question < ActiveRecord::Base
belongs_to :subject
end
The most success I've had is with "Question.joins(:subject).group(category_id:1)", which only returns the last question with an associated category. Any suggestions? Thanks!
So you could make a scope
class Question < ActiveRecord::Base
scope :q_or_c ->(q, c){ where('category_id = ? OR question_id = ?', q, c) }
...
end
and then call it with
Question.q_or_c(question_id, category_id)

Rails: Has many through associations -- find with AND condition, not OR condition

I have the following query method in my ActiveRecord model:
def self.tagged_with( string )
array = string.split(',').map{ |s| s.lstrip }
select('distinct photos.*').joins(:tags).where('tags.name' => array )
end
So, this finds all records that have tags taken from a comma separated list and converted into an array.
Currently this matches records with ANY matching tags -- how can I make it work where it matches ALL tags.
IE: if currently if I input: "blue, red" then I get all records tagged with blue OR red.
I want to match all records tagged with blue AND red.
Suggestions?
-- EDIT --
My models are like so:
class Photo < ActiveRecord::Base
...
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
...
def self.tagged_with( string )
array = string.split(',').map{ |s| s.lstrip }
select('distinct photos.*').joins(:tags).where('tags.name' => array )
end
...
end
class Tag < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :photos, :through => :taggings
end
class Tagging < ActiveRecord::Base
belongs_to :photo
belongs_to :tag
end
A tag has two attributes: ID and Name (string).
This should work:
def self.tagged_with( string )
array = string.split(',').map{ |s| s.lstrip }
select('distinct photos.*').
joins(:tags).
where('tags.name' => array).
group("photos.id").
having("count(*) = #{array.size}")
end
Above will match photos that have tags red and blue at least. So that means if a photo has red, blue and green tags, that photo would match too.
You could change your select statement to the following:
select('distinct photos.*').joins(:tags).where('tags.name = ?', array.join(' OR '))
Which will properly create the OR string in the where clause.
ian.
LOL the solution for this is not a simple task--I thought through it from a SQL standpoint and it was UGLY. I figured somebody else has to have tried this so I did some searching and found this post that should help you:
HABTM finds with "AND" joins, NOT "OR"

Using after_create

I have a model, Category. And I want to create an new default sub_category when ever the category is created. But I'm not sure how to do it. Here is what I have.
class Category < ActiveRecord::Base
attr_accessible :title, :position
has_many :sub_categories
after_create :make_default_sub
def make_default_sub
#Sub_Categories.new( :title=>' ');
end
end
Why not to use ancestry gem? In the future if you will have more subcategories, it will be easier to manage them.
For example in your case:
class Category < ActiveRecord::Base
attr_accessible :title, :position
has_ancestry
after_create :create_default_subcategory
def make_default_sub
children = self.children.new
children.title = ''
children.position = 1 # or autogenerated
children.save!
end
end
But can you explain, why do you need such a strange default behaviour?
Thanks