Rails How do I find case insensitive values that are not already associated to the user? - ruby-on-rails

Newbie Rails developer here so please bare with me.
I have a table called Ingredients where it contains a title field and an association to a User. A user can have many ingredients.
I want to query the database to get the ingredients that are not already available to a User.
I tried doing something like this with Rails:
#ingredients = current_user.ingredients
#community_ingredients = Ingredient.all.excluding(#ingredients).pluck(:title, :id)
But the problem is that this still returns values that are the same & only the case is different.
How can I achieve this outcome?

Try following queries.
#community_ingredients = Ingredient.includes(:user).where("users.user_id = ?", current_user.id).where(users: { id: nil } ).pluck(:title, :id)
OR
Ingredient.includes(:user).where("users.user_id = ?", current_user.id).where(ingredients: {user_id: nil } ).pluck(:title, :id)
OR
Ingredient.includes(:user).where("users.user_id = ?", current_user.id).where(users: { ingredient_id: nil } ).pluck(:title, :id)
Choose right query based on your association and feel free to suggest me so I can remove the extra one.
Most probably the first or second query will work, I strongly feel the third might not be the case.
Let's say this one is not working for you and you want to have solution based on your architecture.
#ingredients = current_user.ingredients.pluck(:title)
#community_ingredients = Ingredient.where.not("lower(title) IN (?)", #ingredients.map(&:downcase)).pluck(:title, :id)
So basically we need to convert both column value and the matching list in same case.
So we have converted to downcase.
here is how it looks in my local system, just make sure it's working that way.

Related

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.

rails getting attributes of associated model for many objects in one query

My title might be confusing, I wasn't sure what to write.
In rails I understand how to fetch Many Objects for One parent object
#first_user = User.first
#first_user_posts = #first_user.posts
But how can I fetch Many Objects for Many parent objects and select its attributes in one query?. I am trying to do something like that:
#many_posts = Post.all
#posts_by_user_gender = #many_posts.joins(:user).map(&:gender)
hoping it would give me an array that could look something like this:
#posts_by_user_gender => ["male", nil, "female", nil]
#I know I can do this map technique if I fetch it directly from the User model
# User.all.map(&:gender),
# but I want to start with those that posted in a specific category
# Post.where(:category_id => 1)
and then to count the males I could use the Ruby Array method .count
#males_count_who_posted = #posts_by_user_gender.count("male")
=> 1
I could always do 3 separate queries
#males_count_who_posted = #many_posts.select(:user_id).joins(:user)
.where("gender = ?", "male").count
#females_count_who_posted = ...
but I find that extremely inefficient, especially if I do the same for something like "industry" where you could have more than 3 options.
you can join model via SQL syntax
#posts_by_user_gender = #many_posts.joins("LEFT JOIN Users where users.id=posts.user_id").joins("LEFT JOIN Genders where genders.id=user.gender_id")

How to make find_or_initialize case insensitive on a Postgres DB?

In a Rails 3.2 app I have a Post model that belongs to a Category. In the new post form is a text field for assigning a category or creating a new category.
It works via the following method on the Post model
def category_name=(name)
if name.present?
post_cat = category.find_or_initialize_by_name(name)
if post_cat.new_record?
post_cat.save(:validate => false)
self.category = post_cat
else
self.category = post_cat
end
end
end
This is more or less working, but is case sensitive. For example, the database now contains records for "Featured", "featured" and "FEATURED" categories.
How can I make the above find_or_initialize function behave as though it were case insensitive. I am using a Postgres database, and I have a suspicion that this is not quite as easy as "making find_or_initialize case insensitive".
I'm grateful for any advice as to best practice, what to consider, and useful references.
I refer to your comment above: Maybe you could add a second field, containing the value of the string which is displayed to the user, and find_or_initialize by the name column:
post_cat = category.find_or_initialize_by_name(name.downcase)
post_cat.display_name = name if post_cat.new_record?
So the second line ensures, that an existing record doesn't get overriden by another user, who tries to add categories like bRandnAmE. The only pitfall I see is, that the first user, who creates the category, has to spell it correct.

How to set default_scope order on attribute from another unassociated model

I have a Search resource that returns posts based on a filter as described in Railscast111, and have the following code:
def filter_posts
posts = Post.order('created_at DESC')
posts = posts.where("name ilike ?", "%#{keywords}%")
posts = posts.where(... #numerous other filters
posts
end
The filter itself seems to work fine. However, the content is not always returned in order of 'created_at DESC'. How can I sort the final output so that it's always in order of 'created_at DESC'? Currently, there is no association between the Post and Search models. Do I need to build one? If so, how?
Have you tried chaining the two conditions together?
posts = Post.where("name like?", "%#{keywords}%").order('created_at DESC')
Depending on how many filters you end up calling, you'll need to keep updating your original result, with the updated scope (based on your filter), as each time you use where it creates a new scope, instead of adjusting the original one. So you seem to be on the right path, as your original code does this, e.g
posts = Post.where("filter1")
posts = posts.where("filter2")
Have you tried sorting after all of the filters have been applied, so something like
posts = posts.order('created_at DESC')
or
posts = posts.sort_by &:created_at
Also, I'm not really sure what you mean by a Search resource, when (at least in this case) it appears you could keep the search logic within the Post model itself. Can you clarify, or maybe post the model?

rails where() sql query on array

I'll explain this as best as possible. I have a query on user posts:
#selected_posts = Posts.where(:category => "Baseball")
I would like to write the following statement. Here it is in pseudo terms:
User.where(user has a post in #selected_posts)
Keep in mind that I have a many to many relationship setup so post.user is usable.
Any ideas?
/EDIT
#posts_matches = User.includes(#selected_posts).map{ |user|
[user.company_name, user.posts.count, user.username]
}.sort
Basically, I need the above to work so that it uses the users that HAVE posts in selected_posts and not EVERY user we have in our database.
Try this:
user.posts.where("posts.category = ?", "Baseball")
Edit 1:
user.posts.where("posts.id IN (?)", #selected_posts)
Edit 2:
User.select("users.company_name, count(posts.id) userpost_count, user.username").
joins(:posts).
where("posts.id IN (?)", #selected_posts).
order("users.company_name, userpost_count, user.username")
Just use the following:
User.find(#selected_posts.map(&:user_id).uniq)
This takes the user ids from all the selected posts, turns them into an array, and removes any duplicates. Passing an array to user will just find all the users with matching ids. Problem solved.
To combine this with what you showed in your question, you could write:
#posts_matches = User.find(#selected_posts.map(&:user_id).uniq).map{ |user|
[user.company_name, user.posts.size, user.username]
}
Use size to count a relation instead of count because Rails caches the size method and automatically won't look it up more than once. This is better for performance.
Not sure what you were trying to accomplish with Array#sort at the end of your query, but you could always do something like:
#users_with_posts_in_selected = User.find(#selected_posts.map(&:user_id).uniq).order('username DESC')
I don't understand your question but you can pass an array to the where method like this:
where(:id => #selected_posts.map(&:id))
and it will create a SQL query like WHERE id IN (1,2,3,4)
By virtue of your associations your selected posts already have the users:
#selected_posts = Posts.where("posts.category =?", "Baseball")
#users = #selected_posts.collect(&:user);
You'll probably want to remove duplicate users from #users.

Resources