Order scope results by closest match - ruby-on-rails

I have three models: Doctor, Appointment, Patient. A Doctor has many patients through appointments and a Patient has many doctors, also through appointments.
I have a search query where you can filter doctors by patients, so if you input the names of the patients it will show you any doctor that has assigned any of those patients.
Here is the scope I use to search within the Doctor model:
scope :by_patient, lambda { |*names|
names_array = names.map { |name| "%#{name}%" }
joins(appointments: :patient).where('lower(patients.name) LIKE ANY (array[?])', names_array)
}
E.g.
Dr. Smith -> Patients: Karen, Joe, Kate, Mary
Dr. Johnson -> Patients: Kate, Mary
Dr. Spears -> Patients: John
If you look for Patients Kate and Mary the query will return both
Dr. Smith and Dr. Johnson, however, Dr. Smith should be shown first
because he has both patients while Dr. Johnson only has one.
How could I order the search results by those doctors who have the most patients?

Related

Hive join over partition

I have these 2 tables :
table products
(
product_id bigint,
product_name string
)
partitioned by (product_category as string)
table place_of_sale
(
product_id bigint,
city string
)
partitioned by (country as string)
how can I left join the 2 tables based on 'product_id' but over the partition ‘country’ of the table place_of_sale ?
This is an example with the desired result :
table products
product_id product_name product_category
1000 banana fruit
1001 coconut fruit
1002 ananas fruit
2002 cow animal
2003 beef animal
table place_of_sale
product_id city country
1000 Texas USA
1002 Miami USA
2003 Sydney Australia
desired result for a left join between table products and table place_of_sale over the partition country :
product_id product_name product_category city country
1000 banana fruit Texas USA
1001 coconut fruit null null
1002 ananas fruit Miam USA
2002 cow animal null null
2003 beef animal Sydney Australia
Here the example is given with only 2 different countries but imagine plenty of different countries.
It's like a left join performed for each country and then an union between the results of all countries.
If the sold product is that present in the table place_of_sale, then use LEFT JOIN:
select s.country, p.product_id, p.product_name, p.category,
case when s.product_id is NULL then "not sold" else "sold" as sold
from products p
left join place_of_sale s on p.product_id = s.product_id
order by country, product_id --order if necessary

How to query array columns in Rails 4?

I can't find any good articles about how to query array columns in Rails. I came across the need to query an Array column in Rails.
I found from an article teaching how to do basic query here.
Let's follow the example in the article where Book covers many subjects and subjects is stored as an array column:
add_column :books, :subjects, :text, array: true, default: []
Query books that contains a certain subject - e.g. History
Book.where("'history' = ANY (subjects)")
Query books that contains all listed subjects - e.g. Finance AND Business AND Accounting
Book.where("subjects #> ?", "{Finance,Business,Accounting}")
I wonder how I can do the following?
Query books that contains any of the listed subjects - e.g. Fiction OR Biography
Query books that doesn't contain a certain subject - e.g. NOT Physics
Query books that doesn't contain ANY of the subjects - e.g. NOT (Physics OR Chemistry OR Biology)
And is there any Rails way of doing the above queries?
For,
Query books that contains any of the listed subjects - e.g. Fiction OR Biography
Book.where("subjects && ?", "{Fiction,Biography}")
Query books that doesn't contain a certain subject - e.g. NOT Physics
Book.where("subjects <> ?", "{Physics}")
Query books that don't contain ANY of the subjects - e.g. NOT (Physics OR Chemistry OR Biology)
Book.where.not("subjects && ?", "{Physics,Chemistry,Biology}")
You can see the array functions of Postgres for reference.
https://www.postgresql.org/docs/8.2/functions-array.html
Usually, associations are a preferable way of approaching the problem:
Book has_many :subjects # or has_one/has_and_belongs_to_many
Subject belongs_to :book # or has_and_belongs_to_many
And then just create a table subjects, save all your subjects there and you're set up.
Your queries:
Query books that contains any of the listed subjects - e.g. Fiction OR
Biography
Book.find_by_sql "SELECT * FROM books WHERE 'Fiction' = ANY (subjects) OR 'Biography' = ANY (subjects)"
Query books that doesn't contain a certain subject - e.g. NOT Physics
Book.where.not("subjects #> ?", "{Physics}")
Query books that doesn't contain ANY of the subjects - e.g. NOT
(Physics OR Chemistry OR Biology)
Book.find_by_sql "SELECT * FROM books WHERE books NOT IN (SELECT * FROM books WHERE 'Physics' = ANY (subjects) OR 'Chemistry' = ANY (subjects) OR 'Biology' = ANY (subjects)"

testing queries using rspec and factory_girl : Is there a better way?

I have the following classes and relationships
City has_many Cinemas
Cinemas has_many Movies
Movies has_many Ratings
Movies Has_many Genres through GenreMovie
and I want to test queries like
* Show me the all movies in NewYork
* Show me the all movies in NewYork order by the rating
* Show me the all movies in NewYork order by length_of_movie, in genre "Action"
* show me all movies in Cinema "X" order by rating, that are in Genre "SciFi"
Currently the way I am doing as below, using factory girl, and chaining a bunch of models together to have data to check against,
city = create(:city)
cinema = create(:cinema, city: city)
5.times do
movie = create(:movie, cinema: cinema, tags: ["sci fi", "action"]
3.times do
create(:rating, score: 2.3, movie: movie)
end
end
and repeating that 3-4 to generate enough data to query against but it seems so clunky.
Is there a better way ?
I normally test this using a very "minimalistic" approach:
e.g. for your first case I would create two movies, one in NY, and one outside. Your method should only return the one in NY
For the second, create three movies, both in NY, with different rating. Create them in a not logic way, so that, no matter what, they will be sorted. Check whether your method returns them in the right order
Similar for the other cases.
I would not just create 5x3 movies. Makes no sense, and only costs time...
There are several factory_girl constructs you could use to clean these up. create_list will create an array of objects, cleaning up your x.times do blocks. A with_ratings trait on your movie factory could allow you to opt in to having ratings automatically created when you create a movie (via FactoryGirl callbacks). You could even have that use a transient attribute in order to control the number and rating. So your result could look something like this:
cinema = create(:cinema)
movies = create_list(
:movie,
5,
:with_ratings,
cinema: cinema,
tags: [...],
ratings_count: 3,
ratings_value: 2.3
)
If you need access to the city, you can get it via cinema.city.
See:
transient attributes
traits

Rails, two dimensional table, pivot, nested hash loops

I am building grade-book report - a two dimensional table that shows lesson names going horizontally and a list of students going vertically.
Student Name | LessonID x | LessonID x | LessonID x
Joe 95% 95%
Mary 80% 80%
Sam 80% 80%
My data is in a table that has these fields:
student_id, lesson_id, grade_in_pct, grade_in_pts, grade_high, grade_low, grade_median
The total number of students and lessons is not fixed.
I considered using ruport/acts_as_reportable or mysql pivot procedure, however it looks like the pivot only gives me one dimension. So that's not going to work, because in my view I want to add mouse-over features and conditional formatting to show more info on each grade.
So I think my only option is to generate a nested hash and then loop through it in the view. What are your thoughts? Could someone suggest a way to build a nested hash? Would it be too processor intensive to loop through 250 rows (~50 students, 5 lessons each)?
I am stuck. Please help. Thanks!
This is how I would do it:
MODELS:
Student Model:
has_many: Grades
has_and_belongs_to_many: Lessons
Lesson Model:
has_many: Grades
has_and_belongs_to_many: Students
Grade Model:
belongs_to: Student, Lesson
CONTROLLER:
#data = Student.all
#lessons = Lesson.all
VIEW:
header_row
#data.each do |student|
#lessons.each do |lesson|
student.grades.find_by_lesson(lesson).some_data

Grouping records on a show page

In my Rails app, I have a Runs model, a Timesheets model, and an Athletes model. A Run belongs_to an Athlete, and an Athlete has_many Runs. A Run also belongs_to a Timesheet, and a Timesheet has_many Runs.
Timesheet/show/:id lists the runs associated with a particular timesheet, along with the athlete associated with each respective run.
On this page, it's common to have something like this:
Joe Smith 5.98, 14.98, 21.22, 36.33, 55.67
Jane Doe 4.99, 13.99, 21.22, 36.21, 55.66
Joe Smith 6.00, 14.87, 21.22, 36.31, 55.58
I'm sure this is a beginners mistake, but I don't know how to group the runs by each athlete, such as this:
Joe Smith
5.98, 14.98, 21.22, 36.33, 55.67
6.00, 14.87, 21.22, 36.31, 55.58
Jane Doe
4.99, 13.99, 21.22, 36.21, 55.66
What am I missing?
The awesome group_by method on any enumerable!
so if you have #timesheets:
#timesheets.group_by(&runner_id).each do |runner_id, runs|
runs.each{|run| puts run}
end

Resources