AR friendly where clause with table and field specified - ruby-on-rails

How do I convert this to more of more AR friendly where clause?
relation.where("users.id = (?)", user_id)
end
I tried
relation.where(id: ?, user_id)
but I need to specify the table since the AR has multiple joins.
As I want to be able to provide just a single user_id or an array of user_ids.

It seems a little hard to say with so little information. But, have you tried:
relation.where(users: {id: user_id})
user_id can be an integer, an array of ids, an enumerable of users, etc.

Related

how to use sunspot/solr to order by inclusion in an associated field

I am using rails 4 with sunspot which uses solr and I am trying to order a model by the field value of an associated model.
I have a number and the number has_many favorites associated to it (multiple users can favorite the number).
Each favorite stores a user_id.
I want to order the numbers where the user_id of an associated favorite matches the current_user.id.
something like order('number.favorites.user_id', current_user.id)
I have tried to index an array of favorites.user_ids in the number searchable boolean(:favorite_user_ids) { favorites.pluck(:user_id) }
but now I'm trying to think of how to order by inclusion in that indexed array.
For example: "order where the current_user.id matches an id in the number.favorite_user_ids array.
leftJoin strategy:
I also found leftOuterJoin directly in solr https://solr.apache.org/guide/7_2/stream-decorator-reference.html#leftouterjoin
I think if I do a left outer join where I use on current_user.id = favorite.user_id then after I can order by user_id

Combine multiple queries into one active record relation?

I am trying to write a search query for my app where based on the query string it will search for groups or users matching the string.
Here is what I have written:
def search_api
#groups = Group.where("name ILIKE '#{params[:query]}'")
#users = User.where("first_name ILIKE '#{params[:query]}' OR last_name ILIKE '#{params[:query]}")
end
Is there a way to combine these two queries into one activerecord relation array? Besides the brute force iterating over both arrays and putting them into one?
I am not sure about the solution but I have a security suggestion.
Your queries are not SQL Injection safe. You could pass array instead of injecting params in SQL string.
The following queries are SQL injection safe:
#groups = Group.where("name ILIKE ?", "#{params[:query]}")
#users = User.where("first_name ILIKE ? OR last_name ILIKE ?", "#{params[:query]}")
So here is a solution. Not sure that it is better than leaving everything as it is, but formally it's the answer to a question (besides that the result is not an ActiveRecord relation):
Group.connection.execute("(SELECT id, 'groups' as table FROM groups WHERE...) UNION (SELECT id, 'users' as table FROM users WHERE...)")
This returns an object of type PG::Result which you can treat as array of hashes. And, as it has already been said, it is good to pass arguments as an array instead of inserting them directly into SQL. Unfortunately, if You want to get a result as ActiveRecord, you may use UNION only for different queries to one table. In that case it looks like:
Group.find_by_sql("(SELECT * FROM groups WHERE...) UNION (SELECT * FROM groups WHERE...)")

ActiveRelation uniquness by column

SO i have active relation like:
[[id:1, post_id: 10, user_id:2][id:2, post_id:11, user_id:2]]
When i say relation.uniq, it doesn't return one value, because id's of objects are different(which is obvious behavior).
How do i get only unique object, but not by full fields, but only by some specific field, for example (:user_id)
I've tried to use distinct, but that returns me only array of fields, instead of active relation or objects
As result, i'd want to get an active relation where, field user_id would be unique
Though i'm not sure if this is good idea, maybe i should use some other methodics to get what i want, because i'm starting to understand, that this is not the best idea
How did you use distinct? When I run
> Model.select('DISTINCT column')
it seems to return an object of class ActiveRecord::Relation.
Use pluck.
Model.distinct.pluck(:user_id)
Examples:
Person.pluck(:id)
# SELECT people.id FROM people
# => [1, 2, 3]
Client.distinct.pluck(:role)
# SELECT DISTINCT role FROM clients
# => ['admin', 'member', 'guest']

Rails basic search and PostgreSQL

I'm trying to create basic search in my web app. Here's code of search function.
def self.search(title, category_id, city_id)
if title || category_id || city_id
joins(:category).where('title LIKE (?) AND category.category_id IN (?) AND city.city_id IN (?)', "%#{title}%", "%#{category_id}%", "%#{city_id}%")
else
scoped
end
end
I have these associations in my model:
has_one :category
has_one :city
And I get this error
ActionView::Template::Error (PG::Error: ERROR: missing FROM-clause entry for ta
ble "category"
LINE 1: ..._id" = "events"."id" WHERE (title LIKE ('%%') AND category.c...
I'm using PostgreSQL. What I can do to remove this error?
The form of joins that you're using wants the association name, the SQL wants the table name. The table should be called categories.
A few other things:
I don't see you joining :city anywhere so your next error will be "Missing FROM-clause entry for table "city". The solution will be to .joins(:city) and use cities in the where. But keep reading anyway.
You don't need the parentheses around the value for LIKE, just title LIKE ? is fine.
You're using IN expressions for the category and city but you're giving them LIKE patterns and that won't work: the IDs will be numbers and you can use LIKE with numbers. If you're using IN then you'll usually want to supply a list of possible values, if you only want to match one value then just use = and a single value for the placeholder.
The categories table probably doesn't have a category_id column, similarly for the cities table and city_id column. Those two columns should be in your model's table.
Searching for a title when you don't have a title doesn't make much sense. Similarly for country and city.
That looks like a lot of problems but they can be fixed without too much effort:
def self.search(title, category_id, city_id)
rel = scoped
rel = rel.where('title like ?', "%#{title}%") if(title)
rel = rel.where('category_id = ?', category_id) if(category_id)
rel = rel.where('city_id = ?', city_id) if(city_id)
rel
end
and you don't even need joins or explicit table names at all.

Rails ActiveRecord Join

I'm using rails and am trying to figure out how to use ActiveRecord within the method to combine the following into one query:
def children_active(segment)
parent_id = Category.select('id').where('segment' => segment)
Category.where('parent_id'=>parent_id, 'active' => true)
end
Basically, I'm trying to get sub categories of a category that is designated by a unique column called segment. Right now, I'm getting the id of the category in the first query, and then using that value for the parent_id in the second query. I've been trying to figure out how to use AR to do a join so that it can be accomplished in just one query.
You can use self join with a alias table name:
Category.joins("LEFT OUTER JOIN categories AS segment_categories on segment_categories.id = categories.parent_id").where("segment_categories.segment = ?", segment).where("categories.active = ?", true)
This may looks not so cool, but it can implement the query in one line, and there will be much less performance loss than your solution when data collection is big, because "INCLUDE IN" is much more slower than "JOIN".

Resources