Rails 4 scoped model with association also scoped - ruby-on-rails

I have 2 models, Student(has_many schedules) and Schedule and what I want is to find All students that have a active schedule today.
What I have now:
Student model:
scope :training_today, -> { joins(:schedules) & Schedule.active & Schedule.from_today }
Both schedule scopes work fine. See here:
$ Schedule.active.from_today
Schedule Load (1.4ms) SELECT "schedules".* FROM "schedules" WHERE "schedules"."active" = 't' AND "schedules"."day" = 4 ORDER BY day ASC
=> [#<Schedule id: 47, student_id: 2, hour: "11", active: true, created_at: "2014-05-18 23:26:34", updated_at: "2014-06-05 19:04:02", day: 4>,
#<Schedule id: 5, student_id: 1, hour: "08:00", active: true, created_at: "2014-05-16 02:54:21", updated_at: "2014-06-05 20:50:07", day: 4>]
What I want:
From my Student model I want to find only those from the query above(ids 1 and 2). Using the scope Student.training_today I get nothing(empty array). How can I get the correct students ?

I don't know what represents the day value in your Database, but this code implies that is it the week-day of this date:
scope :training_today, -> {
includes(:schedules).where(schedules: { active: true, day: Date.current.wday })
}

Related

Hash with 3 different keys each pointing to an array of instances. How do I sort the arrays by id?

I have a hash like so:
def my_requests
result = {
accepted: [],
rejected: [],
pending: [],
}
self.requests.each do |request|
serialized_request = RequestSerializer.new(request)
if request.accept == nil
result[:pending].push(serialized_request)
elsif request.accept
result[:accepted].push(serialized_request)
else
result[:rejected].push(serialized_request)
end
end
result
end
I will have a logged in user. I am trying to organize the logged in user's availabilities by id.
How do I sort each array by id. I know if this was just an array I can do:
array.sort_by{|request| request.id}
But how do I iterate through each key's array? I've tried multiple different ways and the only one that works is if I end up mapping over the hash and then another loop to sort the requests. But that doesn't return a hash. Is there a way for me to keep the structure and sort it?
The availabilities serializer is below:
class RequestSerializer < ActiveModel::Serializer
attributes :id, :start_time, :payment, :number_of_hours, :availability_id, :date, :name, :accept, :postcode, :phone_number
end
Below is one of the key, value pair outputs.
:rejected=>[#<RequestSerializer:0x00007fa416e168a8 #object=#<Request id: 64, payment: 200, number_of_hours: 20, accept: false, start_time: "2000-01-01 16:20:00", venue_id: 1, availability_id: 4, created_at: "2020-08-30 12:15:04", updated_at: "2020-08-30 12:15:52">, #instance_options={}, #root=nil, #scope=nil>, #<RequestSerializer:0x00007fa416e167b8 #object=#<Request id: 4, payment: 160, number_of_hours: 4, accept: false, start_time: "2000-01-01 16:15:00", venue_id: 2, availability_id: 5, created_at: "2020-06-17 21:19:07", updated_at: "2020-06-17 21:21:32">, #instance_options={}, #root=nil, #scope=nil>, #<RequestSerializer:0x00007fa416e166c8 #object=#<Request id: 71, payment: 100, number_of_hours: 1, accept: false, start_time: "2000-01-01 09:45:00", venue_id: 1, availability_id: 6, created_at: "2020-10-01 08:45:43", updated_at: "2020-10-01 08:46:04">, #instance_options={}, #root=nil, #scope=nil>, #<RequestSerializer:0x00007fa416e16560 #object=#<Request id: 66, payment: 30, number_of_hours: 3, accept: false, start_time: "2000-01-01 16:30:00", venue_id: 1, availability_id: 26, created_at: "2020-08-30 12:31:02", updated_at: "2020-08-30 12:32:10">, #instance_options={}, #root=nil, #scope=nil>, #<RequestSerializer:0x00007fa416e163f8 #object=#<Request id: 68, payment: 20, number_of_hours: 3, accept: false, start_time: "2000-01-01 12:00:00", venue_id: 1, availability_id: 28, created_at: "2020-09-01 08:17:26", updated_at: "2020-09-01 13:09:54">, #instance_options={}, #root=nil, #scope=nil>]
Thanks!
result.transform_values { |array| array.sort_by(&:request_id) }
If the arrays are not not arrays of requests, but RequestSerializer, just call .object on them to get the request to sort by.
result.transform_values do |array|
array.sort_by { |serializer| serializer.object.request_id }
end
Another option would be to define request_id on RequestSerializer
You have to sort each hash value separately:
result.each_value { |array| array.sort_by!(&:id) }
Hash#each_value traverses the values and sort_by! sorts the array in-place.
If you need to create a new sorted copy:
result.each_with_object({}) do |(key, value), list|
list[key] = value.sort_by(&:id)
end
As Stefan posted in the comments. I can use a database query to order the requests prior to splitting them into their arrays.
The answer that worked (without doing multiple loops) was:
def my_requests
result = {
accepted: [],
rejected: [],
pending: [],
}
requests.order(:id).each do |request|
serialized_request = RequestSerializer.new(request)
if request.accept == nil
result[:pending].push(serialized_request)
elsif request.accept
result[:accepted].push(serialized_request)
else
result[:rejected].push(serialized_request)
end
end
result
end
Simply by removing self. and using the .order query and the id attribute, everything comes out ordered!
Thanks Stefan! (and everyone else)
(Others worked with multiple methods or loops but as I was using Rails, the query is the quickest and easiest).

How to use rails ActiveRecord to perform a left join of a Model with a subquery

I am using rails 5. When I perform the following straight SQL query in the rails console, I get the following expected result:
2.3.5 :053 > w = ActiveRecord::Base.connection.exec_query("SELECT * FROM students s LEFT JOIN (SELECT student_id, MAX(urgent) AS has_urgent,MAX(created_at) AS last_contact FROM reports GROUP BY student_id) r ON r.student_id = s.id")
(0.5ms) SELECT * FROM students s LEFT JOIN (SELECT student_id, MAX(urgent) AS has_urgent,MAX(created_at) AS last_contact FROM reports GROUP BY student_id) r ON r.student_id = s.id
=> #<ActiveRecord::Result:0x00000000051e1518 #columns=["id", "name", "cwid", "email", "phone", "company", "role", "advisor", "advisor_email", "mentor", "created_at", "updated_at", "student_id", "has_urgent", "last_contact"], #rows=[[1, "Johnny Smith", "71419940", "jsmith#gmail.com", "8435550001", "foxtrot", "mentor", "John I. Moore, Jr.", "john.moore#citadel.edu", "", "2017-11-13 14:58:50.128114", "2017-11-13 14:58:50.128168", nil, nil, nil], [2, "Shelly", "12345678", "shelly#gmail.com", "8435550002", "bravo", "mentee", "Michael P. Verdicchio", "mv#citadel.edu", "Johnny Smith", "2017-11-13 14:58:50.160195", "2017-11-13 14:58:50.160243", "2", "t", "2017-11-13 14:58:50.210105"], [3, "Max", "87654321", "max#gmail.com", "8435550003", "palmetto", "unassigned", "Mei-Qin Chen", "mei.chen#citadel.edu", "", "2017-11-13 14:58:50.179220", "2017-11-13 14:58:50.179258", nil, nil, nil], [4, "George", "87654325", "george#gmail.com", "8435550004", "palmetto", "mentee", "Deepti Joshi", "djoshi#citadel.edu", "Johnny Smith", "2017-11-13 14:58:50.189733", "2017-11-13 14:58:50.189762", "4", "f", "2017-11-03 14:58:50.260914"]], #hash_rows=nil, #column_types={}>
However, this returns an ActiveRecord::Result type, but I really want is to use the Rails ActiveRecord to do the same query but return an ActiveRecord::Relation instead, I thought like so:
2.3.5 :054 > w = Student.joins("LEFT JOIN (SELECT student_id, MAX(urgent) AS has_urgent,MAX(created_at) AS last_contact FROM reports GROUP BY student_id) r ON r.student_id = students.id")
Student Load (0.5ms) SELECT "students".* FROM "students" LEFT JOIN (SELECT student_id, MAX(urgent) AS has_urgent,MAX(created_at) AS last_contact FROM reports GROUP BY student_id) r ON r.student_id = students.id LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 1, name: "Johnny Smith", cwid: "71419940", email: "jsmith#gmail.com", phone: "8435550001", company: "foxtrot", role: "mentor", advisor: "John I. Moore, Jr.", advisor_email: "john.moore#citadel.edu", mentor: "", created_at: "2017-11-13 14:58:50", updated_at: "2017-11-13 14:58:50">, #<Student id: 2, name: "Shelly", cwid: "12345678", email: "shelly#gmail.com", phone: "8435550002", company: "bravo", role: "mentee", advisor: "Michael P. Verdicchio", advisor_email: "mv#citadel.edu", mentor: "Johnny Smith", created_at: "2017-11-13 14:58:50", updated_at: "2017-11-13 14:58:50">, #<Student id: 3, name: "Max", cwid: "87654321", email: "max#gmail.com", phone: "8435550003", company: "palmetto", role: "unassigned", advisor: "Mei-Qin Chen", advisor_email: "mei.chen#citadel.edu", mentor: "", created_at: "2017-11-13 14:58:50", updated_at: "2017-11-13 14:58:50">, #<Student id: 4, name: "George", cwid: "87654325", email: "george#gmail.com", phone: "8435550004", company: "palmetto", role: "mentee", advisor: "Deepti Joshi", advisor_email: "djoshi#citadel.edu", mentor: "Johnny Smith", created_at: "2017-11-13 14:58:50", updated_at: "2017-11-13 14:58:50">]>
For this second approach, the Right table columns do not appear in the result, even though the generated SQL looks very similar. I am new to Rails and ActiveRecord, so if someone can help me understand why these two results are different and what I should do to make the second ActiveRecord query work like the straight SQL query I would appreciate it.
If you are using rails 5 then use left_joins or left_outer_joins.
Try this and let me know if it work for you.
Student.left_outer_joins(:reports).select("student.*, MAX(urgent) AS has_urgent,MAX(created_at) AS last_contact").group("student.id").
If you want to go with your first approach then these methods may helpful for you.
result.columns #Get the column names of the result.
result.rows #Get the record values of the result.
result.to_hash #Get an array of hashes representing the result (column => value)
#ActiveRecord::Result also includes Enumerable.

Search in jsonb and array combination in postgres with rails4 stored_accessor

I have a model called Event, where I have stored_accessor "list" (stored like data: {"list"=>[{"key"=>"key1", "value"=>"value1"}]}).
I need to make a search query o
#<Event id: "1", title: "HHHH", description: nil, data: {"list"=>[{"key"=>"key1", "value"=>"value1"}, {"key"=>"key2", "value"=>"value2"}]}, created_at: "2017-04-14 21:06:22", updated_at: "2017-04-20 10:36:08">
#<Event id: "2", title: "HHHH", description: nil, data: {"list"=>[{"key"=>"key1", "value"=>"value1"}]}, created_at: "2017-04-14 21:06:22", updated_at: "2017-04-20 10:36:08">
#<Event id: "3", title: "HHHH", description: nil, data: {"list"=>[{"key"=>"key11", "value"=>"value11"}, {"key"=>"key12", "value"=>"value12"}]}, created_at: "2017-04-14 21:07:22", updated_at: "2017-04-20 10:37:08">
#<Event id: "4", title: "HHHH", description: nil, data: {"list"=>[{"key"=>"key111", "value"=>"value111"}, {"key"=>"key112", "value"=>"value112"}]}, created_at: "2017-04-14 21:08:22", updated_at: "2017-04-20 10:38:08">
I have a serach params like
1) {'key'=> 'key1', 'value'=> 'value1'}
2) ["key"=>"key1", "value"=>"value1"}, {"key"=>"key2", "value"=>"value2"}]
In first case, it should return Event id 1 and 2.
In second case, it should return Event id 1. (event if return 1 and 2 both could be acceptable).
I am not sure with json and array combination.
Please help.
You may do it with PostgreSQL jsonb's operator #>. Also you need to write the full path for search params: {'list' => [{'key'=> 'key1', 'value'=> 'value1'}]}. Try this code:
to_contain1 = {'list' => [{'key'=> 'key1', 'value'=> 'value1'}]}
to_contain2 = {'list' => [{'key'=> 'key2', 'value'=> 'value2'}]}
Event.
where("data #> ?", to_contain1.to_json})
# returns events 1 & 2
Event.
where("data #> ?", to_contain1.to_json).
where("data #> ?", to_contain2.to_json)
# returns event 1

Check if a value created_at not equal to current date exists in an array in Ruby

I have a collection of user.paid_subscriptions in which each subscription has attributes created_at(datetime) and active(boolean).
How can I check if a PaidSubscription exists such that created_at is not equal to a certain date and active is true?
PaidSubscription looks like this:
[
#<PaidSubscription id: 11457,
user_id: 12,
period: 3,
price: 4000,
expires_at: "2016-03-08 09:44:56",
expires_at: "2016-03-08 09:44:56",
created_at: "2015-12-08 09:44:56",
updated_at: "2016-03-08 23:00:09",
active: false,
giver_id: 20573,
partial: false,
remaining_days: 0>,
#<PaidSubscription id: 13948,
user_id: 12,
period: 1,
price: 1500,
expires_at: "2016-04-11 12:07:40",
created_at: "2016-03-11 13:07:40",
updated_at: "2016-04-11 22:00:11",
active: false,
giver_id: nil,
partial: false,
remaining_days: 0>,
#<PaidSubscription id: 11458....
]
Try this,
If it is a query then it should be like this
user.paid_subscriptions.where("created_at < :date or created_at > :date and active = :active",{date: DateTime.civil(yyyy,mm,dd), active: true})
Or
if it is an array you can use it like below.
user.paid_subscriptions.any? {|ps| ps.active && ( ps.created_at.to_date < Date.civil(yyyy, mm, dd) || ps.created_at.to_date > Date.civil(yyyy, mm, dd))}

Rails console setting limit to one on queries

When I run ActiveRecord queries, the Rails Console seems to be appending LIMIT 1 to my queries.
So I have a sheet which has_many slots. When I query Slot.find_by(sheet_id: 96), I get:
Slot Load (2.3ms) SELECT "slots".* FROM "slots" WHERE "slots"."sheet_id" = ? LIMIT 1 [["sheet_id", 96]]
=> #<Slot id: 153, label: "Foo", name: "Foo", email: "", phone: "", comments: "Fighters", sheet_id: 96, created_at: "2015-04-30 14:28:47", updated_at: "2015-04-30 14:28:47">
But when I query Sheet.find(96).slots:
Sheet Load (10.0ms) SELECT "sheets".* FROM "sheets" WHERE "sheets"."id" = ? LIMIT 1 [["id", 96]]
Slot Load (4.6ms) SELECT "slots".* FROM "slots" WHERE "slots"."sheet_id" = ? [["sheet_id", 96]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Slot id: 153, label: "Foo", name: "Foo", email: "", phone: "", comments: "Fighters", sheet_id: 96, created_at: "2015-04-30 14:28:47", updated_at: "2015-04-30 14:28:47">, #<Slot id: 154, label: "Bar", name: "James", email: "", phone: "", comments: "Foobar", sheet_id: 96, created_at: "2015-04-30 14:28:47", updated_at: "2015-04-30 14:28:47">, ... >
You have to do Slot.find_all_by_sheet_id(96)
EDIT The above code should have worked. Although I use Rails 4.1.8. Try following as well:
Slot.where(:sheet_id => 338)
The find_by method returns a single result, always.
If you want to get all of the slots for a particular sheet, there are a few options:
Sheet.find(96).slots or more likely #sheet.slots if you've already found the sheet
Slot.where(sheet_id: 96) would also work
To be clear, this has nothing to do with the Rails console and everything to do with the .find_by method.

Resources