Rails: Query n IDs before and after some ID - sql

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.

Related

Rank Query Results - Rails

I've got the following query that searches my customer database and does so successfully:
SELECT "customers".* FROM "customers" WHERE ((("customers"."first_name" IN ('John', 'Doe') OR "customers"."last_name" IN ('John', 'Doe')) OR "customers"."email" IN ('John', 'Doe')) OR "customers"."main_phone" IN ('John', 'Doe'))
The equivalent Rails query is:
array = ["John","Doe","111-111-1111"]
Customer.where(first_name: array).or(customers.where(last_name: array)).or(customers.where(email: array)).or(customers.where(main_phone: array))
This works fine, however I am wanting to rank the results. For example, if record # 1 matches for both first and last name, I want that record to display at the top of my results. How could I do this?
The combination of where and or actually create a single SQL query with no "precedence" of the results
You will have to
1. break it down to separate queries
2. for each record found, count in how many queries it was found
3. order your results by this counter in descending order
Here is some code (it can be prettier):
class Customer < ApplicationRecord
def self.relevance_search(query)
matches = []
matches << Customer.where(first_name: query)
matches << Customer.where(last_name: query)
matches << Customer.where(email: query)
matches << Customer.where(main_phone: query)
counter = {}
matches.each do |match|
match.each do |match_result|
id = match_result.id
if counter.key? id
counter[id] += 1
else
counter[id] = 1
end
end
end
counter.map { |id, occurrences| { relevance: 100*(occurrences/4.0), customer: Customer.find(id) } }.sort_by { |r| r[:relevance] }.reverse!
end
end
Usage:
Customer.relevance_search ["John", "Doe", "111-111-1111"]
The return value is and array ordered by relevance in %
Enjoy :)

Rails 5 - How to use postgresql query?

In rails 5, I am using pg(postgresql) for a back-end database. Now I want to query through rails and get the data. How can I use IN and ORDER(:created_at, :desc) conditions in a query.
In controller,
PAGE_LIMIT = 5
posts = Post.where("user_id IN (?)", [1,2,3]).order(created_at: :desc)
posts = posts.paginate(params[:page], PAGE_LIMIT)
I am writing a custom method like,
def paginate(page, limit = 5)
page = page ? page.to_i : 1
limit = limit.to_i
offset = (page - 1) * limit
self.offset(offset).limit(limit)
end
I am new to postgresql. Please help me to solve this issue?
suppose you have User model and you want to get user which has id in [1,2,3]
User.where("id IN (?)", [1,2,3]).order(created_at: :desc)
for more dynamic
x = [1,2,3]
name = "testing123"
User.where("name = ? AND id IN (?)", name, x).order(created_at: :desc)
for more details you can see Active Record Query
to make it working with pagination for array changes to be done here
modified answer
PAGE_LIMIT = 5
posts_arr = Post.where("user_id IN (?)", [1,2,3]).order(created_at: :desc)
posts = Post.where(id: posts_arr.map(&:id)) #here to get active records of posts to make it working for custom pagination
posts = posts.paginate(params[:page], PAGE_LIMIT)

Active Record Scope: Only include if all joined models match where statement

I have the following scope for customers:
scope :filter, -> {
joins(:subscriptions)
.where("subscriptions.end_date <= ?", 1.year.ago.to_date)
}
This scope works fine if customers have one subscriptions less than one year but if they have 2 subscriptions and one is greater than a year and one is less than a year, it still is included in this scope.
How do I write the scope to NOT include the customer if one of the subscriptions doesn't match the where statement?
you could probably do a subquery where you count the number of subscriptions > 1.year.ago, and compare that count to 0
It would look something like this
scope :filter, -> {
joins(:subscriptions)
.where("subscriptions.end_date <= ?", 1.year.ago.to_date)
.where("(select count(subscriptions.id) from subscriptions where subscriptions.customer_id = id && subscriptions.end_date > ?) = 0", 1.year.ago.to_date)
}

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

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)

Highscore excerpt (10 better, 10 worse scores) in a single SQL query

Database schema:
User(id, name)
Highscore(id, points, user_id)
When a User has submitted his highscore, I want him to see the 10 better and 10 worse highscores (to display his placement). Is it possible to accomplish this in a single SQL / ActiveModel query?
This is as far as I've gotten:
Highscore.where("points < ?", highscore.points).order(points: :desc).limit(10).merge(Highscore.where("points >= ?", highscore.points).order(:points).limit(10))
but merge does something else than I thought it did. So this doesn't work.
Doing merge or multiple where calls will AND the conditions together. I haven't had any luck getting UNION to work in ActiveRecord, but you can get the same result with subqueries and Arel.
It would look something like this:
highscores = Highscore.arel_table
bottom_scores_query = Highscore.select(:id).where('points < ?', highscore.points).order(points: :desc).limit(10).to_sql
top_scores_query = Highscore.select(:id).where("points >= ?", highscore.points).order(:points).limit(10).to_sql
Highscore.where(highscores[:id].in(Arel.sql(bottom_scores_query)).or(highscores[:id].in(Arel.sql(top_scores_query)))
This solution uses Arel to do two subqueries to get the high scores where the id is in the top 10 high scores, or where the id is in the bottom 10 high scores.
Refactored into scopes, this would look something like:
class Highscore < ActiveRecord::Base
scope :top_scores, ->(highscore) { where("points >= ?", highscore.points).order(:points).limit(10) }
scope :bottom_scores, ->(highscore) { where('points < ?', highscore.points).order(points: :desc).limit(10) }
scope :summarized, ->(highscore) {
top_scores_query = top_scores(highscore).select(:id).to_sql
bottom_scores_query = bottom_scores(highscore).select(:id).to_sql
where(arel_table[:id].in(Arel.sql(bottom_scores_query)).or(arel_table[:id].in(Arel.sql(top_scores_query))))
}
end
And then you could get them by calling Highscore.summarized(highscore).