Print what is not in a join table - ruby-on-rails

I have a join table created from 2 other tables.
Say one model is called cat, another is called request, and the join table is called catrequest (this table would have cat_id and request_id)
How would I print all of the cats not intersected in the join table i.e. all of the cats NOT requested using rails. I saw some DB based answers, but I am looking for a rails solution using ruby code.
I get how to print a cat that belongs to a request i.e.:
<% #requests.each do |request| %>
<% request.cats.each do |cat| %>
<%= cat.name %>
<% end %>
but I don't understand how to do the reverse of this.

To get a list of cats that have never been requested you'd go with:
Cat.includes(:cat_requests).where(cat_requests: { id: nil })
# or, if `cat_requests` table does not have primary key (id):
Cat.includes(:cat_requests).where(cat_requests: { cat_id: nil })
The above assumes you have the corresponding association:
class Cat
has_many :cat_requests
end

It sounds like what you need is an outer join, and then to thin out the cats rows that don't have corresponding data for the requests? If that's the case, you might consider using Arel. It supports an outer join and can probably be used to get what you're looking for. Here is a link to a guide that has a lot of helpful information on Arel:
http://jpospisil.com/2014/06/16/the-definitive-guide-to-arel-the-sql-manager-for-ruby.html
Search the page for "The More the Merrier" section which is where joins are discussed.

Related

Rails, where filter on an already joined table

I have a table :periods that has a column :hours
I also have a table :simulations that belongs to :period (so it has period_id as a column)
I join the 2 table
#simulations=Simulation.join(:period) #Controller
Now in my view I have to loop through my simulations but filter based on the "hours" column, which exists on the period table.
So I tried things like
In my view I have to loop through simulations:
<% #simulations.where( :period => {:hour => count})each do |sim| %>
or
<% #simulations.periods.where(:hour => count )each do |sim| %>
Again, I want to filter my simulations based on data from the period table. And neither of these approaches work. I have also tried using includes and eager_loads neither of which works.
Am I attempting something that is not possible under rails?
Also I am using postgres
Try using this query, in your controller
#simulations = Simulation.where(periods: {hours: count}) # All simulations that belongs to periods with hours = count
Notice, the 's' in 'periods' and adjust the 's' in 'hours' as per your column name.
In views, directly use simulations variable:
<%= #simulations.each do |sim| %>
You are making same query twice of joining periods and simulations. Also try to avoid any query on Model in views or in controller. Instead, you should make a method in Model and use it to get the results from above query.

Rails 4 grouping duplicates

I wasn't sure how to ask this question. I am new to Ruby on Rails and am still figuring out how to piece everything together.
I have books, orders, products and users. Within a specific view, I would like to display one product_id per user — most users will have the same product_id multiple times.
Here's my book view.
<% #orders.each do |order| %>
<% order.product.books.each do |book| %>
<%= link_to book.title %>
<% end %>
<% end %>
In my book controller I have
def index
#orders = Order.select(:product_id).distinct
#books = Book.page params[:page]
end
All of this works well (view shows one product_id per order product_id, even if there are duplicates) until I call order.created_at which gives a missing attribute error. I know this error is because I am only requesting :product_id on order, but when I add :created_at (#orders = Order.select(:product_id, :created_at).distinct) to the select query I get duplicate :product_ids.
What am I missing?
Well, you have to think about what that would look like. When you are selecting rows by a distinct product_id you are discarding all other order rows that have the same product_id. But when you add created_at back into the mix your resulting record set will include the same product_id because the distinct is operating against the tuple of (product_id, created_at) instead of just the product_id.
Your original #orders isn't actually all of your orders, it's just a subset because it chose a unique one per product_id.
I find it helpful to append to_sql to the end of a query to see what the actual SQL coming out of ActiveRecord is doing:
> Order.select(:product_id).distinct.to_sql
=> SELECT DISTINCT "orders"."product_id" FROM "orders"
> Order.select(:product_id, :created_at).distinct.to_sql
=> SELECT DISTINCT "orders"."product_id", "orders"."created_at" FROM "orders"
Try running those in the database and you'll see what data ActiveRecord is using to construct ActiveRecord instances and realize that ActiveRecord is just taking whatever rows come back in the data and manufacturing objects based on that.

How to access data from a scope with a join

I have two tables, People and Vehicles. I created a scope that shows which people do not have Vehicles.
scope :person_has_no_vehicle, -> { joins("LEFT OUTER JOIN vehicles ON vehicles.person_id = people.id").where('Vehicles.person_id IS NULL').limit(100)}
In my View I have
<% #person.person_has_no_vehicle.each do |test| %>
How would I access data from my vehicles table?
I.E.
<% if test.vehicles.person_id == NIL %> <-- That does not work, but you get the general idea.
First, I suggest you some refactor:
# FROM
scope :person_has_no_vehicle, -> { joins("LEFT OUTER JOIN vehicles ON vehicles.person_id = people.id").where('Vehicles.person_id IS NULL').limit(100)}
# TO
scope :without_vehicle, -> { includes(:vehicles).where(vehicles: { id: nil }).limit(100) }
Then in your view you can use:
<% People.without_vehicle.each do |people| %>
# These People instances do not have any vehicle record associated
# so `people.vehicles` in this loop should return an empty ActiveRecord::Relation
I don't understand why you "would access data from the vehicles table", the scope you want is the exact opposite: People without any vehicle associated.
Anyway, if you really want to test if there is not Vehicle corresponding to the People record, try this:
<% People.without_vehicle.each do |people| %>
<%= "THIS GUY OWNS AT LEAST 1 VEHICLE!!" if people.vehicles.present? %>

IF/CASE statement OR MySQL OR array for get category name

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.

Rails JOIN TABLES

I have a following SQL QUERY:
SELECT articles.name, articles.price, users.zipcode FROM articles INNER JOIN users ON users.id = articles.user_id WHERE vectors ## to_tsquery('crime') ORDER BY articles.price ASC
And I Would like to write it inside of a find method from an ActiveRecord Class named Articles (Articles belongs_to user). Basically i wanna search for Articles and access the zipcode propertie from the User (user has_many Articles)
I wrote the following version, but i'm not sure that its working because in the response i dont receive any information about the user zipcode.
a = Article.find(:all,:conditions=>"vectors ## to_tsquery('crime')",:joins= >:user,:order=>:price,:include=>:user)
But i have no idea how to access the zipcode information. how can I access this information ? this is the right approach ?
Regards,
Victor
If you've coupled Articles and Users like you say above, this should be pretty easy:
#articles = Article.find(:all, :conditions => "…", :include => :user)
Then, in your view, you can do:
<ul>
<% for each article in #articles do %>
<li><%= article.user.zipcode %></li>
</ul>
<% end %>
This works because Rails creates a property for the parent object (User) in the model (Article) - you can read more about that in the API docs. This even works without the "include" key above, but leaving it out would mean a database query in every step of the loop.

Resources