Order records in table by other table column - ruby-on-rails

I have 3 tables: articles, people and person_mentions.
Article model has_many :people, through: person_mentions and so on.
In person_mentions table I have 3 columns article_id, person_id and mention_order. mention_order is an integer.
I need to find all people, who mentioned in article and order them by mention_order, but when I run
#article.people.order(mention_order: :asc)
I get an error
column people.mention_order does not exist
So, I need to make a query from people table with mention_order from person_mentions in it.
Thanks for any help!

Could you try:
#article.people.order("person_mentions.mention_order asc")

Related

Find records with at least one association but exclude records where any associations match condition

In the following setup a customer has many tags through taggings.
class Customer
has_many :taggings
has_many :tags, through: :taggings
end
class Tagging
belongs_to :tag
belongs_to :customer
end
The query I'm trying to perform in Rails with postgres is to Find all customers that have at least one tag but don't have either of the tags A or B.
Performance would need to be taken into consideration as there are tens of thousands of customers.
Please try the following query.
Customer.distinct.joins(:taggings).where.not(id: Customer.joins(:taggings).where(taggings: {tag_id: [tag_id_a,tag_id_b]}).distinct )
Explanation.
Joins will fire inner join query and will make sure you get only those customers which have at least one tag associated with them.
where.not will take care of your additional condition.
Hope this helps.
Let tag_ids is array of A and B ids:
tag_ids = [a.id, b.id]
Then you need to find the Customers, which have either A or B tag:
except_relation = Customer.
joins(:tags).
where(tags: { id: tag_ids }).
distinct
And exclude them from the ones, which have at least one tag:
Customer.
joins(:tags).
where.not(id: except_relation).
distinct
INNER JOIN, produced by .joins, removes Customer without Tag and is a source of dups, so distinct is needed.
UPD: When you need performance, you probably have to change your DB schema to avoid extra joins and indexes.
You can search examples of jsonb tags implementation.
Get ids of tag A and B
ids_of_tag_a_and_b = [Tag.find_by_title('A').id, Tag.find_by_title('B').id]
Find all customers that have at least one tag but don't have either of the tags A or B.
#Customer.joins(:tags).where.not("tags.id in (?)", ids_of_tag_a_and_b)
Customer.joins(:tags).where.not("tags.id = ? OR tags.id = ?", tag_id_1, tag_id_2)

Rails how to sort by occurrences in HABTM table?

I have two models:
Questions:
has_and_belongs_to_many :topics
Topics:
has_and_belongs_to_many :questions
And the associated joining table: questions_topics
How can I write a query to get all topics and their occurrences in the joining table, and sorted by the count (showing by order of the most active topics first)?
So basically I want to be able to do this in a single query:
list = Topics.all
Order list by list.questions.count
Update:
Is there a better rails way to write the following query? (which does appear to give the required result:
Topic.includes(:questions).group('questions_topics.topic_id').references(:questions).order("count(questions_topics.topic_id) DESC")

Rails 3: add sum column to the result hash

I have this models:
Event
has_many :incidents
and
Incident
belongs_to :event
I want to getting all events with the incident numbers for each one in one query. In Rails I know exist "group by" and "sum" methods but I don't know how mix both.
thanks.
You need to make a left join, group by object id and count elements:
Events.joins('LEFT JOIN incidents ON incidents.event_id = events.id').group_by('events.id').select('events.*, COUNT(incidents.id) AS incidents_count')
You could also look into counter_cache option.

ActiveRecord find categories which contain at least one item

Support I have two models for items and categories, in a many-to-many relation
class Item < ActiveRecord::Base
has_and_belongs_to_many :categories
class Category < ActiveRecord::Base
has_and_belongs_to_many :items
Now I want to filter out categories which contain at least one items, what will be the best way to do this?
I would like to echo #Delba's answer and expand on it because it's correct - what #huan son is suggesting with the count column is completely unnecessary, if you have your indexes set up correctly.
I would add that you probably want to use .uniq, as it's a many-to-many you only want DISTINCT categories to come back:
Category.joins(:items).uniq
Using the joins query will let you more easily work conditions into your count of items too, giving much more flexibility. For example you might not want to count items where enabled = false:
Category.joins(:items).where(:items => { :enabled => true }).uniq
This would generate the following SQL, using inner joins which are EXTREMELY fast:
SELECT `categories`.* FROM `categories` INNER JOIN `categories_items` ON `categories_items`.`category_id` = `categories`.`id` INNER JOIN `items` ON `items`.`id` = `categories_items`.`item_id` WHERE `items`.`enabled` = 1
Good luck,
Stu
Category.joins(:items)
More details here: http://guides.rubyonrails.org/active_record_querying.html#joining-tables
please notice, what the other guys answererd is NOT performant!
the most performant solution:
better to work with a counter_cache and save the items_count in the model!
scope :with_items, where("items_count > 0")
has_and_belongs_to_many :categories, :after_add=>:update_count, :after_remove=>:update_count
def update_count(category)
category.items_count = category.items.count
category.save
end
for normal "belongs_to" relation you just write
belongs_to :parent, :counter_cache=>true
and in the parent_model you have an field items_count (items is the pluralized has_many class name)
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
in a has_and_belongs_to_many relation you have to write it as your own as above
scope :has_item, where("#{table_name}.id IN (SELECT categories_items.category_id FROM categories_items")
This will return all categories which have an entry in the join table because, ostensibly, a category shouldn't have an entry there if it does not have an item. You could add a AND categories_items.item_id IS NOT NULL to the subselect condition just to be sure.
In case you're not aware, table_name is a method which returns the table name of ActiveRecord class calling it. In this case it would be "categories".

Rails relational issue - Find user's next outing

I have an outings table, which basically holds some information about an 'outing'. These outings then have crews, which have users (through crew_users)
Just as a note, outings have many crews, so there's an outing_id in the crews table
Now, I need to find the user's next outing, as in the first outing where outing.start_time > Time.now
I've got this in my user.rb model:
has_many :crew_users
has_many :crews, :through => :crew_users
has_many :outings, :through => :crews
And when I try to do this:
>> Users.find(1).outings
I get this error:
ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'crews.user_id'
in 'where clause': SELECT `outings`.* FROM `outings` INNER JOIN `crews` ON
`outings`.id = `crews`.outing_id WHERE ((`crews`.user_id = 1))
Anyone any ideas? Like I said, my goal is to get the user's next outing from the current time so there may very well be a much better to go about this!
You can try and reverse this is if doing a double :through is out of the question by filtering the Outing records instead. The SQL for this would look something like:
SELECT outings.id FROM outings
LEFT JOIN crews ON crews.id=outings.crew_id
LEFT JOIN crews_users ON crews_users.crew_id=crews.id
WHERE crews_users.user_id=? AND start_time>=NOW()
ORDER BY created_at
LIMIT 1
The quick and dirty approach is to run this and extract the outing_id value you need, then call Outing.find with that.

Resources