Currently I have a table of 10,000 users who I want to search. I also have another table of 3 users that I have approved. I want to make it so that the search result only returns 9997 of the users. In other words, if I have approved a user, I do not want them to appear in the search result. Here is what I have so far for the code:
approved_users = Approval.where(user_id: logged_in_user.id)
approved_user_ids = approved_users.map { |user| user.approved_user_id}
pre_search_results = User.find_by_sql("SELECT *
FROM users LEFT JOIN approvals
ON users.id = approvals.approved_user_id
WHERE name LIKE ? OR operator_id LIKE ?"
)
The first statement approved_users returns this when I manually added 3 users:
id: 10, user_id: 39, approved_user_id: 37
id: 11, user_id: 39, approved_user_id: 35>,
id: 12, user_id: 39, approved_user_id: 41>
I then made approved_user_ids just give me the id portion of the above statement, so it returns this:
[37, 35, 41]
Now, I want pre_search_results to return me the 9997 other users and I have the above statement. It is currently returning nothing. Any ideas what can be done?
To get all users whose ids are not present in approved_user_ids, you can use: pre_search_results = User.where("id NOT IN (?)", approved_user_ids)
My thought is to split your two different conditions into two different scopes, which can then be chained together...
I'm also assuming a relationship in your User model.
user.rb
class User
has_many :approvals
scope :has_been_approved, -> { joins(:approvals) }
scope :has_not_been_approved, -> ( includes(:approval).where(approvals: { user_id: nil })
scope :search_by_name, ->(term) { where("name LIKE :t", t: term) }
...which lets you do something in your controller (or wherever) like...
User.has_not_been_approved.search_by_name("Rodolfo")
The scope for has_not_been_approved is a little awkward. Here's where I found the example: Rails find record with zero has many records associated
Related
I need to get some data from ActiveRecord, I have following two tables Department and users I have issue that I am getting one hash in which user is giving me user_ids and emails, now I want to create hash container users, departments and emails in specific format. I have tried a lot map/select but still could not figure out any simple way.
class User < ApplicationRecord
belongs_to :department
end
And Department
class Department < ApplicationRecord
has_many :users
end
I am getting following values from user
sample_params = [{ user_id: 1, email: 'example1#example.com' },
{ user_id: 5, email: 'example5#example.com' },
{ user_id: 13, email: 'example13#example.com'}]
Now I have retrieve departments from database and other data and join in one huge hash so I can add it to my class. I can get all my users by following command
users = User.where(id: sample_params.map{ |m| m[:user_id] })
I will get whole ActiveRecord objects with all users if I run following command I am getting all user_id and project_id
users.map { |u| {user_id: u.id, department_id: u.department_id } }
I will get this
[{:user_id=>1, :department_id=>3},
{:user_id=>5, :department_id=>3},
{:user_id=>13, :department_id=>2}]
but I want to create following hash, Is there any simple way to do it directly using query or in other few lines, I can try using Iteration but that would be very long and complicated. As I also need to merge emails in it and add one project instead of same projects multiple ids.
[
{department_id: 1, users: [{user_id: 1, email: 'example1#example.com'},
{user_id: 5, email: 'example5#example.com'}]},
{department_id: 2, users: [{ user_id: 13, email: 'example13#example.com']
I am using here sample data the real data is very very large include hundreds of users and many departments.
I don't think you can do it in one go! let us run throgh and try how can we solve it. First instead of using map in your params let us try another alternate for it. Remove following line
users = User.where(id: sample_params.map{ |m| m[:user_id] })
user following line
User.where(id: sample_params.pluck(:user_id)).pluck(:department_id, :id).grou
p_by(&:first)
This will bring you result in each users id with department grouping in one query
{3=>[[3, 1], [3, 5]], 2=>[[2, 13]]}
Now we are getting department_id and user_id so we will run map on them to get first array with department_id and user_id in a group with following command
data =
User
.where(id: sample_params.pluck(:user_id))
.pluck(:department_id, :id)
.group_by(&:first).map do |department_id, users|
{ department_id: department_id,
users: users.map { |_, id| id } }
end
This will give you hash with department and users
[{:department_id=>3, :users=>[1, 5]}, {:department_id=>2, :users=>[13]}]
Now you have hash of department and users. so let us work on this this is second phase where I will use select to get email from your params and merge it to your data.
result = []
data.each do |department|
department_users = []
department[:users].each do |user|
emails = sample_params.select { |user| user[:user_id] == 1 }[0][:email];
department_users << { id: user, emails: emails }
end; result << {department_id: department[:department_id], users: department_users}
end
Now if you do puts result[0] you will get following hash as you wanted.
{:department_id=>3, :users=>[{:id=>1, :emails=>"example1#example.com"}, {:id=>5, :emails=>"example1#example.com"}]}, {:department_id=>2, :users=>[{:id=>13, :emails=>"example1#example.com"}]}
This will solve issue, I understand there are two operation but there is no double sql queries in single it is working and you are also replacing your emails. I hope it solve you issue, any one can make it simpler would be appreciated.
I'm building an e-commerce app with rails and I have the following models:
Order
id:
user_id:
order_status:
subtotal:
OrderItem
id:
order_id:
product_id:
unit_price:
quantity:
total_price:
Product
id:
base_price:
discount:
price:(base_price less discount)
Each user can have one pending_order (order_status:1) and this order is then considered the "cart"
Then let's say a product get's its price updated, or a new discount amount.
I want to identify all the order_items that belong to a pending order and belong to the product being changed.
This is so that I can update the prices on these pending order items.
How can I write this query so that I can call it in the Product model before_save method?
I want something like (I know this is wrong but just to get an idea):
#items = OrderItem.where(order.order_status:1).where(product_id:self.id)
I think I need to use .joins() but I'm at a lose for how to get this working.
Can anyone help?
Something like this:
#items = OrderItem.joins(:order).where(product_id: 1, orders: { order_status: 1})
?
Knowing that by default Rails orders data by ID, how can I order by ids given to the where clause?
ids = Bookmark.where(user_id: 7).order(created_at: :desc).pluck(:company_id)
Result:
[146, 140, 128, 4, 2]
Now, when I try to get the companies in the same order from ids
Company.where(id: ids).pluck(:id)
Result:
[2, 4, 128, 140, 146]
Expected Result:
[146, 140, 128, 4, 2]
My pretended result will be the same in both cases (same order).
The companies should be returned in the same order that the Bookmarks on that company where created.
Company.includes(:bookmarks) .where(id: ids).order('bookmarks.created_at desc').pluck(:id)
So it looks like given a user ID, you want a list of companies sorted by the created_at date of the bookmarks that join Users and Companies.
Am I correct in assuming that Users and Companies share a many-to-many relationship through Bookmarks?
If that's the case, the following "join" could work for you!
Company.joins(:bookmarks).where('bookmarks.user_id = ?', 7).order('bookmarks.created_at')
Of course, in your code, this could be generalized to grab companies for any user!
user_id = 42
companies = Company.joins(:bookmarks).where('bookmarks.user_id = ?', user_id).order('bookmarks.created_at')
ActiveRecord "joins" reference
What exactly are you trying to order by... the company_id?
pluck returns an array, which is why this does NOT work:
ids = Bookmark.where(user_id: 7).pluck(:company_id).order(company_id: :desc)
## undefined method `order' for Array
Instead, you can call sort on the array.
ids = Bookmark.where(user_id: 7).pluck(:company_id).sort
That should do the trick
You are explicitly ordering by created_at in order(created_at: :desc)
You want to order by company_id ids = Bookmark.where(user_id: 7).order(:company_id).pluck(:company_id)
This is how I have solved the question:
Bookmark.includes(:company).where(user_id: current_user).order(created_at: :desc)
and when iterating over the elements I use:
record.company instead of record.
This way I have the companies from the same order that the Bookmarks where created.
Probably you can try this:
company_by_id = Company.find(ids).index_by(&:id) # Gives you a hash indexed by ID
ids.collect {|id| company_by_id[id].id }
Having some difficulty with an elegant way to solve this.
Users can rate movies 1 or 0. A user has many movies through ratings. I am trying to write an instance method to return a list of users who have at least one positive match in common (both rated movie 1).
e.g.
some_user.potential_matches => returns list of users who rated at least one of some_user.movie_ratings.where(rating: 1) as a 1
Hope that makes sense.
I think this one liner might do the job for you (it may need some tweaking to match your class names etc)
User.joins(:movies).where(movies: { id: Movie.joins(:users).where(users: { id: u }).joins(:ratings).where(ratings: { rating: 1 })})
When you're trying a query that is a bit more complicated I find it helpful to start at the end and work your way back
1) Find all movies with ratings == 1
Movie.joins(:ratings).where(ratings: { rating: 1 })
2) Replace all movies with only movies where user u has provided a rating. In your case since you're looking at an instance method for class User this will be self
Movie.joins(:users).where(users: { id: u }).[INSERT QUERY FROM STEP 1 HERE]
3) The last step is to find all users that have provided ratings for the movies returned by the query above
User.joins(:movies).where(movies: { id: [INSERT QUERY FROM STEP 2 HERE] })
[EDIT]
You can add .uniq at the end to return a unique set of users.
Assuming you have movie_id and user_id in your MovieRating model when you built the association, please try the method here:
def potential_matches
# find movie ids the user rated 1
movie_ids = self.movie_ratings.where(rating: 1).map(&:movie_id)
# find other users who rated 1 in those movies and get user_ids
other_user_ids = MovieRating.where(rating: 1).where(movie_id: movie_ids).where.not(user_id: self.id).map(&:user_id).uniq
# find those users with id
other_users = User.where(id: other_user_ids)
return other_users
end
In your user class you can add this instance variable:
def users_with_rated_movies_in_common
User.joins(:movies).where(movies: { id: movies }).where.not(id: self)
end
This basically grabs all users who have rated a movie, and the rated movies is a movie that the current user rated. Finally, we don't want to include the actual user.
You should be able to chain additional conditions onto the query.
I have prepare these two arrays:
list_of_students = Student.where('class = ?', param[:given_class])
list_of_teachers = Teacher.where(...)
Student belongs_to Teacher and Teacher has_many students.
And now, I'd need to remove from the list_of_students all items (students), where teacher_id is not included in list_of_teachers.
I found some tips and HOWTO's on comparing arrays, but none of that helped to figure out this case.
Thank you in advance
You can use the IN SQL statement.
list_of_students = Student.where('class = ? AND teacher_id IN (?)', param[:given_class], list_of_teachers.map(&:id))
If the list_of_teachers is an ActiveRecord::Relation (and not an array), you can also use #pluck(:id) or (from Rails 4) #ids
Student.where('class = ? AND teacher_id IN (?)', param[:given_class], list_of_teachers.ids)
There are several ways to write the IN statement. Given you already have a where, I joined it to the main where. But you could also write
Student.where('class = ?', param[:given_class]).where(teacher_id: list_of_teachers)
or
Student.where(class: param[:given_class], teacher_id: list_of_teachers)
Also note you don't need to assign the list of teachers to a variable.
Student.where(class: param[:given_class], teacher_id: Teacher.where(...).ids)
Last but not least, if your Teacher query is simple, you may want to use a single query and a JOIN. Assume you want to get all the Teachers with name Rose.
Student.where(class: param[:given_class], teacher_id: Teacher.where(name: 'Rose').ids)
You can rewrite the same query to
Student.where(class: param[:given_class]).joins(:teacher).where(teacher: { name: 'Rose' })
or (the final and shorter expression)
Student.joins(:teacher).where(class: param[:given_class], teacher: { name: 'Rose' })
You can try something like
a = [1,2,3]
b = [1,4,5]
pry(main)> a.delete_if {|a1| !b.include? a1}
=> [1]
it checks each value in a is in b or not. If not it deletes the value from a and gives you a array finally.
This is an example. You can use this accordingly