Activerecord query with array in where clause - ruby-on-rails

I have a location table in my rails app which has four columns :-
| Id | Longitude | Latitude | user_id
Now I have an array containing the list of user_ids. How can I write an active record query to select the last row of each user id. For example, if I have three user_ids in my array [1,2,3] I want the query to return just the last row corresponding to each user_id (lets assume entries for each user_id is present in the table) from the table.
So, far I am able to get all the rows corresponding to all the user_ids using this following query:
#ids = [1,2,3]
#locations = Location.where(user_id: ids)
How can I modify this activerecord query so that it returns only the last row corresponding to each user_id

Assuming you have a User model that has many locations, you could start from the user model and use the association to get to your last location for each user.
User.where(:id => #ids).includes(:locations).collect { |u| u.locations.last }
User.where(:id => #ids) returns your collection of user objects.
includes(:locations) eager loads the associated locations, so we don't run into an n+1 problem.
collect { |u| u.locations.last } maps the last associated location into an array

You can also try this:
#ids = [1,2,3]
#locations = #ids.map {|id| [Location.where(user_id: id).last.location,User.find(id).name]}
#This would give you something like this: [["US",XYZ],["IND","ABC"]]

Related

Order an Active Record Relation with a custom method

I have to order an ActiveRecord Relation.
Profile.where(id: [1,2,3]).order_user_preferences
where records with country_code = 'US' (or a specific string) first.
I've created this self.order_user_preferences, but I can't access the relation object inside the method.
I think "sort_by" converts the ActiveRecord Relation to an array. Do you know how to keep it as an "Active record relation"?
class Profile
def self.order_user_preferences()
# Order by user country
countries = ['US']
# User-country first
return relation.sort_by {|p| countries.include?(p.country_code) ? 0 : 1} if countries.size > 0
end
end
Expected results
PROFILE ID | Country code
1 | US
2 | EU
3 | JP
You need order not sort_by. Sort by sort on the enumerable object and it is not aware of associations. Read more here: https://apidock.com/ruby/Enumerable/sort_by
So you will probably end up with something like Profile.joins(:countries).where(id: [1,2,3], countries: {country_code: "US"}).order("countries.country_code asc")
This will bring you any profiles where the id is 1 or 2 or 3 and they HAVE an associated country with country. Profiles that do not have an associated country will not be included.

Filtering objects by checking if multiple entries in many-to-many table exist

I have three tables: listings, amenities, and listing_amenities. I have a filter where users can filter listings by amenities, and in my controller I take in their filter as an array of amenity descriptions. I am trying to filter for listings which have ALL of those amenities. Currently, I can filter, but it only succeeds to check if listings have at least ONE of the provided amenities.
Current query:
scope :filter_by_amenities, ->(amenities) { # amenities is array of descriptions
includes(:listing_amenities)
.where(listing_amenities: {
:amenity_id => (
Amenity.where(:description => amenities)
)
})
}
How can I modify the query to only return listings which have ALL of the amenities, rather than at least one?
This can be done using class method and querying on the resultant records iteratively.
def self.filter_by_amenities(amenities)
amenity_ids = Amenity.where(:description => amenities).pluck(:id)
records = self.all
amenity_ids.each do |amenity_id|
record_ids = records.includes(:listing_amenities).where(listing_amenities: {:amenity_id => amenity_id)}).pluck(:id)
records = records.where(id: record_ids)
end
records
end

Filter table on attribute of first result from joined table

I have two tables users and task_lists, users has_many task_lists.
task_lists belongs to users and has an attribute tasks_counter.
users
|id|
task_lists
|id|user_id|tasks_counter|
I would like to find all the users whose first (MIN(id)) tasks_list has a tasks_counter < 5.
How would I achieve this in PostGreSQL? I'm using Rails, if somebody knows a solution using ActiveRecords.
This will set users_ids variable with an Array containing all User id's whose first TaskList has a tasks_counter < 5:
user_ids = TaskList.select("MIN(id) AS id, user_id, tasks_counter")
.group(:user_id) # Get first TaskList for each user
.select { |t| t.tasks_counter < 5 } # Keep users tha meet criteria
.pluck(:user_id) # Return users' id in array
If you would like to get an ActiveRecord_Relation object with User objects you can use the result from the previous query and filter User.
users = User.where(id: user_ids)
Or, everything in one line:
users = User.where(id: TaskList.select("MIN(id) AS id, user_id, tasks_counter")
.group(:user_id)
.select { |t| t.tasks_counter < 5 }
.pluck(:user_id))

Rails ActiveRecord: Pluck from multiple tables with same column name

I have a simple use case:
User has many Tweets
Tweet belongs to User
And i'm trying to pluck a column name that exists on both tables. For example:
#tweets = Tweet.includes(:user).all.pluck(:created_at)
Each table has a created_at column, but the result from above returns the created_at for the tweet. How can I also pluck the user's created_at?
My workaround is below utilizing joins and selects:
#tweets = Tweet.joins(:user).select("users.created_at AS created_date").all
So how can I do this by using pluck?
You can do the below
#tweets = Tweet.includes(:user).all.pluck(:created_at, "users.created_at")
And also .all is not necessary here as joins/includes fetch all records/associated records. So the final query would be like below
#tweets = Tweet.includes(:user).pluck(:created_at, "users.created_at")

How to update collection data for collection of ids?

I am getting collection of ids [1,2,3,4] in the params and I make a call to an API that will return the array for the corresponding ids. E.g. ["Apple","Orange","Mango"]. How can I update this in my database for the corresponding ids?
For example: the ids which are mentioned above are from my user table. ids = [1,2,3,4], which are primary keys in my user table.
From the API response I got an array of fruit_names for the correlated user_ids. E.g.: ids = [1,2,3,4] and fruit_names = ["a","b","c","d"], where the fruit_name column exists in my user table. How do I update fruit_name from the API response correlated ids in my user table?
You can use each_with_index in combination with update for this:
ids.each_with_index do |id, index|
User.update(id, :fruit_name, fruit_names[index])
end
The above code assumes:
ids = [1,2,3,4]
fruit_names = ["a","b","c","d"]
and that the indexes of those arrays match.
Note that this will execute a query for each item in your ids array. If your ids array is very big this is not going to perform well.
Hash[ids.zip fruit_names].each do |id, fruit|
User.update_all({:fruit_name => fruit}, {:id => id})
end
OR
User.where(:id => ids).each do |usr|
usr.update_attribute(:fruit_name, fruit_names[ids.index(usr.id)])
end

Resources