I have a category_aliases table, which has aliase column (array type), and category_id column, which points to categories table.
I have products table, which has category column.
I have categories table.
I want to loop through all products (a lot), which has old categories, and map these to new categories.
So I need to check if any of category_aliases.aliase (aliase is an array of old categories) includes this product's category, and if yes, I want to map it to new category from categories table.
What I tried is:
Product.all.each do |p|
CategoryAlias.all.each do |ca|
if ca.aliase.include? p.category
p.update_column(:category, Category.find(ca.category_id).name)
else
p.update_column(:category, 'undefined')
end
end
end
But I am surely missing something, because even though I know, that there should almost always be a match, it updates p.category to 'undefined' all the time.
You do not need to iterate through all categories ('each') in each product loop, you just need to find a matching category detect.
Furthermore: if you need to iterate over many Products, is might be better to use find_each:
category_aliases = CategoryAlias.pluck(:name, :aliase)
Product.find_each do |product|
category = category_aliases.detect { |ca| ca.aliase.include? product.category }
if category
p.update_column(:category, cateory.name)
else
p.update_column(:category, 'undefined')
end
end
I see number of issues in the code:
You updated category field on each iteration. It leads to update to 'undefined' if CategoryAlias record does not contain category. All other iterations do not make sense then.
You need to break inner loop right after category is updated.
You need to update category to 'undefined' in outer loop, if no alias category found in the whole category_aliases table.
Related
In my app I'm obtaining a certain category, and I'm filtering the associated items based on their name.
The following code should be pretty clear:
categories = Category.where(:id => params[:category_id]).includes(:items).where("lower(items.name) like ?", "%#{params[:keywords].downcase}%")
However, if the name filter excludes all the items, the categories object returned by where is nil. Is this the expected behaviour? How can I get the category even either items exist or not?
The easiest way might be to just split the query:
#category = Category.find(params[:category_id])
#items = #category.items.where("lower(items.name) like ?", "%#{params[:keywords].downcase}%")
Based on your code it seems like category_id references only 1 category so I've changed it to singular.
You should look into doing an OUTER JOIN against the items table which will give you categories regardless of whether or not their items meet the name filter.
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').
On my site I got entries which have category. Site have only 5 categories, so I have dilemma:
Make relationship between category table and entries (category_id) table
OR
Make method which return category name via IF/CASE statement? Like:
case #entry.category.id
when 1
"Games"
when 2
"Movies"
when 3
"Fun"
[...]
end
(I remind that I must get 10 category name per page)
OR
Use array:
cat[1] = "Games"
cat[2] = "Movies"
cat[3] = "Fun"
[...]
<%= cat[#entry.category.id] %>
I think this relation definitely belongs into the database. (adding a category table)
it is the most sane and most scalable option.
It is also the cleanest, because you break the seperation of data, display and logic (MVC: model, view, controller) when hardcoding the categories in your application.
you can easily select the item AND its category with a single query:
SELECT item.*, category.name
FROM item
LEFT JOIN category ON category.id = item.category_id
WHERE some=condition
there are similar queries for INSERTs and UPDATEs (at least in MySQL), so you never need a second query.
If the only thing you care about category is "name", then you should just store the category_name in the entries table.
OR
Make a constant CATEGORY_NAME and wrapper method to get the name with id in the entries table (without using Category table/model at all). eg.,
class Entry
CATEGORY_NAME = [ "Games", "Movies", "Fun"]
def category_name
CATEGORY_NAME[cat_id] #cat_id being just 0,1,2 .. depends how you want to store
end
...
I am sure there are many ways to achieve this anyway.
Hope it helps.
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
Ok, I'm not sure that my title was clear enough, but I will try to explain
I have two tables: orders that has_many items and items that belongs_to orders.
I just started to learn RoR and stuck with a simple task. All I want is to
display orders and related items like this:
Order 1:
Item 1
Item 2
Order 2:
Item 1
Item 2
...
I know how to display orders or items separately, I know how to display items' orders (item.order.id), but how to display orders and items in the table like above? In template where I display orders I could go through each item every iteration and compare it foreign order_id with order.id, but that would be awkward. I'm supposing that I should get items into some kind of multidimensional hash where key would be order_id and then I could just refer to this hash by order id and get all items in it, but I'm not sure it's correct.
I hope what I have written here is understandable.
When you define a has_many relation, you automatically get the methods to query those objects. In this case, the method order.items.
So you can do:
Order.find_each do |order|
puts "Order #{order.id}:"
order.items.each do |item|
puts "Item #{item.id}"
end
end
(I used find_each method, which is available from Rails 2.3+. You could use a simple Order.all.each though.