How to create active record query in rails? - sql

I'm new in rails and I have a sql query but I don't know how can I convert it into rails active records query.
UserExercise.connection.exec_query("SELECT * FROM (SELECT \"user_courses_progresses\".\"user_id\", \"user_courses_progresses\".\"progress\" FROM \"user_courses_progresses\" WHERE \"user_courses_progresses\".\"is_primary\" IS TRUE) x JOIN users ON \"x\".\"user_id\" = \"users\".\"id\" ORDER BY progress desc")

Combining Active Record and "raw" SQL that might look like this:
User
.select('*')
.from(
UserCoursesProgress
.select(:user_id, :progress)
.where(is_primary: true),
:x
)
.joins('JOIN users ON x.user_id = users.id')
.order(progress: :desc)

Related

How to use joins with `where` condition

I want to get users with their assets, which have type 'Google'.
I tried
User.joins(:assets).where(assets: { assetable_type: 'Google' })
or
User.joins(:assets).where("assets.assetable_type = 'Google'")
But that scopes work identically and return nothing.
SQL, which scopes generate:
SELECT "users".* FROM "users" INNER JOIN "assets" ON "assets"."assetable_id" = "users"."id" AND "assets"."assetable_type" = 'User' WHERE (assets.assetable_type = 'Google')
It doesn't seem to look right
What am I doing wrong?
You didn't gave any details about your models and associations
So, I can give you a temporary solution to use raw sql
sql = "SELECT users.* FROM users INNER JOIN assets ON assets.assetable_id = users.id WHERE assets.assetable_type = 'Google'"
result = ActiveRecord::Base.connection.execute(sql).to_a
Note: If you are using relation as per rails standards it should be
sql = "SELECT users.* FROM users INNER JOIN assets ON assets.assetable_id = users.id WHERE assets.assetable_type = 'Google'"
result = ActiveRecord::Base.connection.execute(sql).to_a
But I think you are using another name i.e., assetable_id instead of user_id.

Convert SQL queries to Activerecord query

How to convert this into active records queries?
#test = #keyword_list.find_by_sql("
SELECT keywords.keyword, minimum_ranking.min_position from keywords
JOIN (select keyword_id, min(position) as min_position from rankings group by keyword_id) minimum_ranking
on minimum_ranking.keyword_id = keywords.id
where created_at between ('2019-10-15 07:00:00') AND ('#{date}')
")
Not only that before these code, i already have 2 layer of association
#project = Project.find(params[:id])
#keywords = #project.keywords
#pagy, #keyword_list = pagy(#keywords.includes(:rankings).order(current_ranking: :asc), items: 50)
At the moment i been running loop and .where on the views, which making loading the page extremely slow. i am trying to tackle this issue by making a single sql queries in the controller, managed to wrote it in sql but can't figure out to do it in Active Record.
Have a look at https://scuttle.io to get started:
Keyword.select(
[
Keyword.arel_table[:keyword], MinimumRanking.arel_table[:min_position]
]
).where(
Arel::Nodes::Between.new(
:created_at, (
Arel::Nodes::Group.new(LITERAL<'2019-10-15 07:00:00'>)
).and(Arel::Nodes::Group.new(LITERAL<'#{date}'>))
)
).joins(
Keyword.arel_table.join(Ranking.arel_table).on(
MinimumRanking.arel_table[:keyword_id].eq(Keyword.arel_table[:id])
).join_sources
)

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)

rails 4 complex SQL scope

I have a model Users which has_many EventLogs.
I would like create a scope which will order Users by those with the most occurrences of EventLogs they have.
scope :highest_completed_events, .....
How can I count the number of EventLogs with a status of 2, and then order the users with the highest occurrence of that type of event.
User.joins(:event_logs).where("event_logs.status_id = 2")#... COUNT, then ORDER BY
Hope that makes sense.
Here's a query you can execute to get your users ordered by the number of events they have:
#users = User.
select("users.*, COUNT(event_logs.id) as event_logs_count").
joins('LEFT JOIN event_logs ON event_logs.user_id = users.id').
group('users.id').
order('event_logs_count DESC')
You should use a LEFT JOIN since you'll want to include users who don't have any events.
If you were to write it as a scope:
scope(:highest_completed_events, {
select: 'users.*, COUNT(event_logs.id) as event_logs_count',
joins: 'LEFT JOIN event_logs ON event_logs.user_id = users.id',
group: 'users.id',
order: 'event_logs_count DESC'
})
#users = User.highest_completed_events
In order to filter the events by a particular status, simply use a where().
#users = User.
select("users.*, COUNT(event_logs.id) as event_logs_count").
joins('LEFT JOIN event_logs ON event_logs.user_id = users.id').
where('event_logs.status = ?', STATUS_COMPLETE).
group('users.id').
order('event_logs_count DESC')
As an aside, sometimes you'll run into issues with ActiveRecord stripping out your custom select() statement when doing something like #users.count. What I normally do is nest this kind of thing in a custom from() statement.
_from = User.
select("users.*, COUNT(event_logs.id) as event_logs_count").
joins('LEFT JOIN event_logs ON event_logs.user_id = users.id').
group('users.id').
order('event_logs_count DESC').to_sql
#users = User.from("(#{_from}) as users")
#users.count # will work
Try:
User.all.sort_by{|u| u.event_logs.select{|l| l.status_id = 2}.count}.reverse
Or is it 'eventlogs'? Schouldn't your line be has_many :event_logs ?
BTW, my solution is not very efficient but DB-agnostic.

Nested sql queries in rails when :has_and_belongst_to_many

In my application I the next task that has not already been done by a user. I have Three models, A Book that has many Tasks and then I have a User that has has and belongs to many tasks. The table tasks_users table contains all completed tasks so I need to write a complex query to find the next task to perform.
I have came up with two solutions in pure SQL that works, but I cant translate them to rails, thats what I need help with
SELECT * FROM `tasks`
WHERE `tasks`.`book_id` = #book_id
AND `tasks`.`id` NOT IN (
SELECT `tasks_users`.`task_id`
FROM `tasks_users`
WHERE `tasks_users`.`user_id` = #user_id)
ORDER BY `task`.`date` ASC
LIMIT 1;
and equally without nested select
SELECT *
FROM tasks
LEFT JOIN tasks_users
ON tasks_users.tasks_id = task.id
AND tasks_users.user_id = #user_id
WHERE tasks_users.task_id IS NULL
AND tasks.book_id = #book_id
LIMIT 1;
This is what I Have done in rails with the MetaWhere plugin
book.tasks.joins(:users.outer).where(:users => {:id => nil})
but I cant figure out how to get the current user there too,
Thanks for any help!
I think this will duplicate the second form with the LEFT JOIN:
class Task < ActiveRecord::Base
scope :next_task, lambda { |book,user| book.tasks.\
joins("LEFT JOIN task_users ON task_users.task_id=tasks.id AND task_users.user_id=#{user.id}").\
where(:tasks=>{:task_users=>{:task_id=>nil}}).\
order("date DESC").limit(1) }
end
Note that instead of tasks_users this uses the table name task_user, which is more typical for a join model. Also, it needs to be called with:
Task.next_task(#book_id,#user_id)
book.tasks.where("tasks.id not in (select task_id from tasks_users where user_id=?)", #user_id).first
That would give you the first task that doesn't already have an entry in tasks_users for the current user.