How do I select only the associated objects in a Rails "where" query? - ruby-on-rails

I have a model Category, which has_many Products, and a Product in turn has_many Categories. When a user searches for a Category, I'd like to return the products of the matching Categories without losing my Arel object. Here's what I have so far:
Category.where("upper(title) like ?", search_term.upcase).map {|category| category.products}.flatten
This does the trick of returning the products, but of course what I have is an array and not Arel. I can get as far as adding an :includes(:products) clause, so I do indeed get the products back but I still have them attached to their categories. How do I adjust my query so that all I get back is an Arel that only addresses products?

If it is products that you want then you should probably start with the Product object when you are searching. For example ,you could do it like this:
Product.joins(:categories).where("upper(categories.title) like ?", search_term.upcase)
The reason I use joins instead of includes is that joins will perform an INNER JOIN instead of LEFT OUTER JOIN which is what you need to only return the products that are actually associated with the found categories.
To make it a little more elegant you could wrap it all up in a scope in your Product model like this:
# In Product.rb
scope :in_categories_like, Proc.new{ |search_term|
joins(:categories).where("upper(categories.title) like ?", search_term.upcase)
}
# In use
#products = Product.in_categories_like(params[:search_term])

Related

Get children of a collection of parents with ActiveRecord

I have a Category model and Post model with a one-to-many relationship.
I'd like to have all posts for a certain set of categories.
I want the result to be an ActiveRecord object to be able to do further queries.
Right now I'm using .map like so
categories.map{|c| c.posts.order(position: :asc)}
Use embedded query to Posts, as follows:
Post.where(category_id: Category.all.pluck(:id)).order(position: :asc)
First find all the categories that you are interested and get the ids:
category_ids = Category.where('name like ?', '%foo%').pluck(:id)
Then just query for the Posts where the category_id is included in this list of ids:
posts = Post.where(category_id: category_ids)
This is a AR object, so you can keep adding order or where and so on:
posts.order(position: :asc)

Rails - How Do I do this Query? Get One of Each Record

I have a BlogPost model with a :category attribute. I am making a new BlogPost form, and I would like for the select menu to populate itself with each category entered in previous records by the user.
Therefore, I need one query to find all BlogPosts with a User ID, and then round up a list of each category they have entered. The same category will exist in multiple records, but of course I only want to return copy of it for the select menu.
Thank you :)
You can SELECT DISTINCT categories returned an INNER JOIN to the right user :
Category
.joins( :posts )
.where( posts: { user_id: current_user.id } )
.uniq
This should send a query like this :
SELECT DISTINCT categories.*
FROM categories
INNER JOIN posts ON posts.category_id = categories.id
WHERE posts.user_id = [whatever]
EDIT NOTE: be wary that uniq is both a method on a Relation and on an Array. Be sure to call it on the relation before it is cast to an array, or you will perform the uniq on an array of non-distinct results, which works too, but is absurd performance-wise.

Trying to return AREL not Array in Rails

I have two models: Products and Tags through a products_tags join in a HABTM relationship.
I am currently defining my controller index as:
#stats = Product.all(:include => :tags).uniq
which returns an array. How can I return an Active Rel object? I tried added scoped, however received a no method error.
I need to find and list a unique list of tags, and be able to view what product each specif tag belongs to.
Try #stats = Product.includes(:tags).uniq.
Note that uniq turns the Relation into an Array; if you want to do something like SELECT DISTINCT you'll want to use Product.includes(:tags).select('DISTINCT whatever').

Rails ActiveRecord Join

I'm using rails and am trying to figure out how to use ActiveRecord within the method to combine the following into one query:
def children_active(segment)
parent_id = Category.select('id').where('segment' => segment)
Category.where('parent_id'=>parent_id, 'active' => true)
end
Basically, I'm trying to get sub categories of a category that is designated by a unique column called segment. Right now, I'm getting the id of the category in the first query, and then using that value for the parent_id in the second query. I've been trying to figure out how to use AR to do a join so that it can be accomplished in just one query.
You can use self join with a alias table name:
Category.joins("LEFT OUTER JOIN categories AS segment_categories on segment_categories.id = categories.parent_id").where("segment_categories.segment = ?", segment).where("categories.active = ?", true)
This may looks not so cool, but it can implement the query in one line, and there will be much less performance loss than your solution when data collection is big, because "INCLUDE IN" is much more slower than "JOIN".

Find all objects with broken association

I have two models in my rails app with a has many and belongs to association.
Category has many items and Item belongs to category.
These models are associated in the normal way through a category_id column in the Item model.
I'm looking for a quick way of finding all elements in the database with broken associations.
i.e. find all categories that exist with no associated items and items that exist with no associated category.
For example, if I have an item with a category_id of 7, but the category with id 7 has been deleted then this would be considered broken.
For your example, to find items with category_id's for categories which don't exist any more:
Item.where('NOT EXISTS (SELECT * FROM categories where category.id = item.category_id)')
You might want to look at this as well:
A rake task to track down missing database indexes (not foreign keys though, but indexes): https://github.com/eladmeidar/rails_indexes
A very effective way is using find_by_sql to let the database do the heavy lifting:
uncategorized_items = Item.find_by_sql("select * from items where category_id IS NULL")
Another way is a named scope:
class Item < ActiveRecord::Base
scope :uncategorized, where(:category_id => nil) # rails 3
# or...
named_scope :uncategorized, :conditions => 'category_id IS NULL'
end
These are just a couple of ideas. I assume that once you've found these broken associations you plan to fix them, right? You might want to use validates_associated in both models if it's important to you that this not happen again.
You can use find_by_sql and a left outer join to find all the items in one table but not another. Here, I use a downloads table and an image_files table (I've only included the SQL):
SELECT d.*, d.image_file_id
from downloads as d
LEFT OUTER JOIN image_files as i
ON i.id = d.image_file_id
WHERE d.image_file_id IS NULL

Resources