Is there a way to combine where and where.not into one condition in Rails? - sql

I have an Event model, that has user_id inside it. I want to select all objects of this model, with specified user_id but not including specific events. So I can do it with a query like that:
Event.where(user_id: user.id).where.not(id: id)
But can I combine these 2 where functions into one?
I know that if I need to find, for example, events with specified ids and user_ids, I can do it this way:
Event.where(user_id: user_id).where(id: id)
and I can compact it using one where call instead of two:
Event.where(user_id: user_id, id: id)
but can I do the same thing if I am using where and where.not?

You can gather
Event.where(user_id: 1) + Event.where.not(id: 2)
or deny a parameter
Event.where(user_id: 1).where.not(id: 2)

You can write as per below to add where and where.not :
Event.where(
"user_id = ? AND id != ?",
user.id,
id
)
so if user_id = 1 and id = 2
than this will return records with user_id 1 and without id 2 :)

try this,you can create two scopes and calling then in chain
scope :with_user, ->(user) {user_id: user.id}
scope :excluded_event, ->(event_ids) { where.not(id: event_ids) }
Event.with_user(user).excluded_event(event_ids)

Related

How to select each model which has the maximum value of an attribute for any given value of another attribute?

I have a Work model with a video_id, a user_id and some other simple fields. I need to display the last 12 works on the page, but only take 1 per user. Currently I'm trying to do it like this:
def self.latest_works_one_per_user(video_id=nil)
scope = self.includes(:user, :video)
scope = video_id ? scope.where(video_id: video_id) : scope.where.not(video_id: nil)
scope = scope.order(created_at: :desc)
user_ids = works = []
scope.each do |work|
next if user_ids.include? work.user_id
user_ids << work.user_id
works << work
break if works.size == 12
end
works
end
But I'm damn sure there is a more elegant and faster way of doing it especially when the number of works gets bigger.
Here's a solution that should work for any SQL database with minimal adjustment. Whether one thinks it's elegant or not depends on how much you enjoy SQL.
def self.latest_works_one_per_user(video_id=nil)
scope = includes(:user, :video)
scope = video_id ? scope.where(video_id: video_id) : scope.where.not(video_id: nil)
scope.
joins("join (select user_id, max(created_at) created_at
from works group by created at) most_recent
on works.user_id = most_recent.user_id and
works.created_at = most_recent.created_at").
order(created_at: :desc).limit(12)
end
It only works if the combination of user_id and created_at is unique, however. If that combination isn't unique you'll get more than 12 rows.
It can be done more simply in MySQL. The MySQL solution doesn't work in Postgres, and I don't know a better solution in Postgres, although I'm sure there is one.

Rails: Query n IDs before and after some ID

I would like to query n users before and after some user.
Edit: for clarity
def local_query(id, range)
local_min = id - range*0.5
local_max = id + range*0.5
local_users = User.where(id: [local_min..local_max])
return local_users
end
Guessing that you want to query user by limiting ID(specific range of ID) i think following should work
user_id_min_range = 5
user_id_max_range = 10
User.where(id: (user_id_min_range..user_id_max_range))
Above code will return users whose ID are between 6-10
Or you can also do like following
User.where("id > ?", 5).order(id: :asc).limit(5)
Above query will select users with id greater than 5 and order them in Ascending ID and return top 5 users.
If I understand your question correctly you could use this scopes:
scope :next_users, ->(start_id, user_limit) {
order(id: :asc).where("id > ?", start_id).limit(user_limit)
}
scope :before_users, ->(start_id, user_limit) {
order(id: :desc).where("id < ?", start_id).limit(user_limit)
}
They will select n of the next or before users for a predefined user id.

Rails .where(.... and ....) Searching for two values of the same attribute

I've tried
#users = User.where(name: #request.requester or #request.regional_sales_mgr)
and
#users = User.where(name: #request.requester).where(name: #request.regional_sales_mgr).all
This doesn't seem to work. What I want is to find the user whose name matches #request.requester, and the user whose name matches #request.regional_sales_mgr, and save them both into the variable #users.
In the general case "OR" queries can be written as:
User.where("users.name = ? OR users.name = ?", request.requester, request.regional_sales_mgr)
Note: Rails 5 will support OR using:
User.where(name: request.requester).or(User.where(name: request.regional_sales_mgr))
For this specific case as state in other answers an IN query is simpler:
User.where(name: [request.requester, request.regional_sales_mgr])
You want to use the SQL IN clause. Activerecord provides a shortcut to this:
#users = User.where(name: [#request.requester, #request.regional_sales_mgr]).all
Giving an array of values to name: will generate the following SQL statement:
SELECT * FROM users WHERE name IN (value1, value2, and so on...);
This should find all the users whose names are #request.requester or #request.regional_sales_mgr
Diego's answer should solve Sabrams' question without the all in rails 4.x (query for user with a name of x or y)
#users = User.where(name: [#request.requester, #request.regional_sales_mgr])
For those who find this post like I did actually looking for a rails all, you can chain where's. For instance to only get users that have both of two skills through a join table. (This will returns users with both x and y skill, will not return user with only x)
#users = User.joins(:skillable_joins).where(skillable_joins: { skill_id: 1 }).where(skillable_joins: { skill_id: 2 })

Where condition along with group_by oprion in rails

I am new to rails so please anybody tell me how to use group_by option in controller page and and along with group_by i want to count the name through the Where Condition
In City Model add a scope.
scope :by_name, lambda { |name| where(name: name) }
When you call count on the scope
City.by_name('London').count
The following MySql will be executed...
SELECT count(*) FROMcitiesWHEREcities.name= 'London'
City.group_by(&:name)
The above statement will give you array of hash, in which key will be city_name and values will be array of city records.
Then if you need only count of each of array of city records for all the cities then you can do it by creating a new variable and storing the count of records along with their name using :
city_count = {}
City.group_by(&:name).each do |city_name, city_records|
city_count[city_name] = city_records.count
end
The above code will return you the array of hash which has key as city_name and the number of records as value.

complex join in scope in Rails ActiveRecord

I have the following scope, which I know is not optimal:
scope :event_stream_for, lambda{ |user|
where("target_id in (?) and target_type = ?", user.events.collect(&:id), "Event")
}
This creates 3 queries. How can I optimize it?
Alternatively, how do I put the whole sql statement in lambda of the scope, like
SELECT * FROM activities WHERE target_type =='Event' AND target_id IN (SELECT DISTINCT id FROM events WHERE (host_id == user.id OR invitee_id == user.id))
Thank you
Assuming user has many events and each event belongs to a user.
scope :event_stream_for, lambda{ |user
joins(:events). # or joins("LEFT JOIN events ON events.user_id = users.id").
where(["target_type=?", "Event"])
}
this will run one query. Haven't tested my code with your table, but it should work similarly.
----------------------- based on your edited question -------------------
SELECT * FROM activities WHERE target_type =='Event' AND target_id IN (SELECT DISTINCT id FROM events WHERE (host_id == user.id OR invitee_id == user.id))
It's all about ActiveRecord Relation, http://railscasts.com/episodes/239-activerecord-relation-walkthrough?view=asciicast
Activity.
select("*,distinct events.id AS events_id").
joins("events ON (events.host_id = #{user.id} OR events.invitee_id = #{user.id}").
where(:target_type => 'Event')
Try this on console, and if it works you can just simply change it to scope.
Since i m not sure what you are trying to do, you may need some adjustment.
How about this?
scope :event_stream_for, lambda{ |user|
where("target_id in (SELECT DISTINCT id FROM events WHERE (host_id == ? OR invitee_id == ?) and target_type = ?", user.id, user.id, "Event")
}
It's just rearranging what you already had, but it should get you down to one query, since it doesn't use the associations in code.