Rails ancestry: Get children records of ancestry model - ruby-on-rails

I've two models:
Post
- category_id
Category (ancestry model)
The category tree looks for example like this:
- Root Category
- Sub Category
Now lets say a post gets categorized to Sub Category. I'm in the Root Category and i would like to see all posts which are in Sub Category as well. Is it possible to get these as well with ancestry?
The category tree always has just one nested level so maybe ancestry is anyways too much..
Thanks in advance
Working example for just one nested level
#category = Category.find(params[:id])
category_ids = #category.children.map(&:id) << #category.id
category_ids = category_ids.join(",")
#posts = Post.recent.where("category_id IN (#{category_ids})").page(params[:page])

I suggest that the following would do the trick:
Post.where(category_id: #category.subtree_ids)
And would also probably be the most efficient way without doing something really horrible.

Something like
Post.where('subcategory_id in ('+#category.subcategories.select(&:id).join(',')+')')
You then request the posts, which have their category-id in the list generated by #category.sucategories, from which you select the id and merge them into a string like '1,5,77'.
Something very very bad to do is (just for fun):
#mylist = Array.new
Post.all.each do |item|
#mylist << item if item.subcategory.category = #selectedrootcategory
Update:
Just thought of some other solution:
#allposts = Array.new
#selectedrootcategory.includes(:subcategory => :posts).subcategories.each do |subcat|
#allposts << subcat.posts
All postst are retrieved in one query (thanks to includes), then you iterate over the subcategories and posts.

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)

Querying or iterating join tables in ActiveRecord/Rails

I have two tables:
Venues
has_many :venue_intel_maps
VenueIntelMap
belongs_to :venue
In the venue_intel_map table there's a column called :tag_label that I want to grab for each particular venue that has a venue_intel_map
In my controller below this returns an array of each venue where then each venue has a venue_id
def show
#venues = Venue.where(parent_venue_id: current_admin.parent_venue_id)
end
So in my views, I can do this for particular venue.
- #venues.each do |venue|
=venue.name
=venue.address
=venue.location
But because I want to get to venue_intel_maps table so I can call a column called :tag_label I wanted to avoided a nested each statement that might look something like this
- #venues.each do |venues|
- venues.venue_intel_maps.each do |intel_maps|
= intel_maps.tag_label
Is there anyway I can clean this up or any suggestions? I was trying to play around with .joins or .selects in active record but didn't know how to successfuly do that so I can somehow put the logic in my Venues model.
EDIT
Would it also be better somehow to define in the controller my venue doing this? In the views I'm going to be pulling things from my venue_intel_maps table so would this serve any advantage?
#venues = Venue.joins(:venue_intel_maps).where(parent_venue_id: current_admin.parent_venue_id)
depending on your Rails version, try:
#venues.venue_intel_maps.pluck(:tag_label)
#venues.venue_intel_maps.map(&:tag_label)
see: http://rubyinrails.com/2014/06/rails-pluck-vs-select-map-collect/
You could preload (Rails 3+) the venue_intel_maps in the controller
def show
#venues = Venue.where(
parent_venue_id: current_admin.parent_venue_id
).preload(:venue_intel_maps)
end
Then in the view, use the iteration you suggested
- #venues.each do |venue|
= venue.name
= venue.address
= venue.location
= venue.venue_intel_maps.each do |intel_maps|
= intel_maps.tag_label

Rails: finding records where attribute includes some value

I have a "Stores" model that contains various locations. Among the attributes for each store is the the "brands" that is carries.
Example: Store1, brands: "Nike, Adidas, Polo"; Store2, brands: "Jcrew, Polo"
I want to be able to select all stores where brand contains "Adidas" (may also contain other brands)
Something along the lines of:
#search = Stores.where(brands: params[:brand])
but need it to be
#search = Stores.where(brands.include? params[:brand])
which clearly doesn't work
What's the best way to deal with this?
If brands is a string and params[:brand] contains a single brand name, you can use MySQL's LIKE function:
#search = Stores.where(['BRANDS LIKE ?', "%#{params[:brand]}%"])
You can do this with the following statement.
#search = Stores.where("brands = ?", params[:brand])
A similar example is given in Listing 11.43 of the Hardtl rails tutorial
You should also note that rails models generally are meant to have singular names, i.e. Store instead of Stores.

Ruby on Rails: where returning nil

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.

Handle many-to-many relationship in rails

I have a many-to-many relationship in a db I use with rails.
Say I have a schema that looks like this :
Cinemas
- cinema_id
- name
Movies
- movie_id
- name
Showtimes
- showtime_id
- timestamp
- movie_id
- cinema_id
So basicaly : [Cinemas] 1 -- N [Showtimes] N -- 1 [Movies]
In one of my pages I have a cinema id and want to show the movies based on the cinema_id and group the showtimes per movie. I am not exactly sure on how to handle this properly. In .Net I would use a SelectMany linq operator and create a collection of movies but this does not seem to exists in ruby/rails.
One way to do this I think would be to do something like:
#showtimes = Showtime.where(:cinema_id, cinema_id)
#showtimes.each do |st|
#movies.add(st.Movie) unless #movies.include? st.Movie
end
That would work ... but it seems ... ugly or it is just me ?
Based on your schema, each showtime record will have exactly one movie and one cinema. You can easily get separate lists this way:
#showtimes = Showtime.where(:cinema_id, cinema_id).order(:showing_at)
#movies = #showtimes.map {|s| s.movie }
You might also want to look at the .group and .includes methods in ActiveRecord.
If you want to go straight to a list of movies showing at a certain cinema, you could also do this:
#movies = Movie.joins(:showtimes).where('showtimes.cinema_id = ?', cinema_id)
With that, you can loop over the movies and show each one's showtimes.
If you have your associations setup correctly in your model, you should be able to do something like this:
Controller:
#showtimes = Showtime.where(:cinema_id, cinema_id)
View:
<%= #showtimes.each do |showtime| %>
<p><%= showtime.movie.name %></p>
<% end %>

Resources