Trying to return AREL not Array in Rails - ruby-on-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').

Related

Using has_scope to filter by nested attributes in a HABTM relationship

I have an API in which i have a HABTM relationship between Characters and Movies (a movie has many characters and a character has many movies). I'm trying to set up a filter with has_scope gem so i can do something like
/api/characters?by_movie=:movie_id so instead of getting all the Characters from index in my CharactersController, i only get the characters that take part in a specific movie.
The way the relationship is set up allows me to do something like Characters.find(1).movies -> returns the list of movies of that character. And if i send a POST to character and i want to add movies to it i can do it like "movie_ids":[1,2].
I have tried this approach with no success:
in my Character.rb scope :by_movie, -> id, {where(movie_ids: id)} and scope :by_movie, -> id, {where(movies: id)} and in my controller: has_scope :by_movie, using: :id
The documentation in has_scope is very little, i wasn't able to find my specific problem, i hope some of you can help me, thank you.
Well, i figured it ou (sort of)
app/models/movie.rb
has_and_belongs_to_many :genres
scope :by_genre, -> (genres) {
genre_array = genres.split(',') # this turns the genre string into an array
joins(:genres).where(genres: genre_array) # this joins the genres table to our movies model, these tables are related already by a HABTM relationship.
}
And in our app/controllers/movies_controller.rb
has_scope :by_genre
#movies = apply_scopes(Movie).all
The only problem i can't solve is that if i pass let's say
localhost:3000/studios/movies?by_genre=1,2,3 #This is what your GET should look like. 1, 2 and 3 are the id's of the Genres the movie has.
say a movie has all three genres, so when i send this, i get as a result that one movie three times, and not only one result. So instead of comparing our genres_array with genre_ids array, it's sort of comparing each element of genres_array with genre_ids, one at a time. I haven't been able to solve this problem yet.

Select descendants from has_many association in Rails 4

So I have a list of sources and have entries. Entries have a field source_id to reference the Source. Now I need to get the count of entries for a number of sources, via the user_id field which is a field in sources.
So basically I need to select all source IDs from a certain user_id and then through those IDs, get a number of entries. I tried the following:
entries_count = Source.where('user_id = ?', user_id).entries.count
But this obviously didn't work.
What would be the way to achieve this?
entries_count = Sources.where('user_id = ?', user_id) will return a collection, not an object. So you have to get each collection element's entries first, and count after:
entries_count = Sources.where('user_id = ?', user_id).map(&:entries).flatten.count
Unrelated thing: is you model really called Sources or it is a typo and it's Source in fact?
I would go from entries to sources using
Entry.joins(:source).where(sources: {user_id: user_id}).count

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.

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.

How do I select only the associated objects in a Rails "where" query?

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])

Resources