Rails where date is greater than given date query - sql

Trying to search where movies coming out have a release date greater than today's date
Movie.where('release > ?', Date.today)
ActiveRecord::StatementInvalid: Mysql::ParseError: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'release > '2011-09-25')' at line 1: SELECT `movies`.* FROM `movies` WHERE (release > '2011-09-25')

Rails 3+ :
Movie.where('release > ?', DateTime.now)
Pre Rails 3
Movie.where(['release > ?', DateTime.now])

In recent versions of rails, you can do this:
User.where(created_at: 3.days.ago..Time.now)
See some other examples here: https://stackoverflow.com/a/24150094

Update
Rails core team decided to revert this change for a while, in order to discuss it in more detail. See this comment and this PR for more info.
I am leaving my answer only for educational purposes.
new 'syntax' for comparison in Rails 6.1 (Reverted)
Movie.where('release >': DateTime.now)
Here is a link to PR where you can find more examples.

In Ruby 2.7, you can try this:
License.where(expiration: Time.zone.today..)
SELECT "licenses".* FROM "licenses" WHERE "licenses"."expiration" >= '2021-07-06 15:12:05'

Ruby beginless/endless ranges can also be used as an out-of-the-box solution:
Post.where(id: 1..)
=> Post Load (0.4ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" >= $1 [["id", 1]]
Post.where(id: ..9)
=> Post Load (0.3ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" <= $1 [["id", 9]]
Post.where(id: ...9)
=> Post Load (0.3ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" < $1 [["id", 9]]
Note:
Replace id with your date column release
Replace value with Date.today

Related

ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column "order_count" does not exist when .having('order_count > 5')

Context: I am using Active Admin and would like to define a scope to show me any Users with more than 5 orders in the next week. This is a Postgres DB in a Rails app.
User.where(vetting_status: ["Enabled"]).joins(:orders).where('orders.date >= ? AND orders.date <= ?', Date.today, Date.today + 6.days).select('users.first_name, COUNT(orders.id) AS order_count').group('users.first_name').having('order_count > 5').order('order_count desc')
However, this gives me the following error when I try to run it: ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column "order_count" does not exist
The issue is with the .having('order_count > 5') as I can run the query if I remove that part. However, that's the crucial part and I'm not sure what I'm getting wrong. I've tried .having('gardener.order_count > 5') and .having('orders.order_count > 5') but neither of those worked either. Thanks in advance for help. P.S. I have searched a fair few answers and couldn't find a solution.
The HAVING clause is evaluated before the SELECT - so the server doesn't yet know about that alias.
Instead of COUNT(orders.id) AS order_count you should do count(orders) as order_count You can look around here
I have tried so far below and its working, hopefully it will help you.
User.where(vetting_status: ["Enabled"]).joins(:orders).where('orders.date >= ? AND orders.date <= ?', Date.today, Date.today + 6.days).group('users.first_name').having('count(orders) > 5').order('count(orders) desc').select('users.first_name')
Alternatively you should do as with your above query: -
count(orders) as order_count
This also means the it will get total orders of user as order count.
like this: -
User.where(vetting_status: ["Enabled"]).joins(:orders).where('orders.date >= ? AND orders.date <= ?', Date.today, Date.today + 6.days).select('users.first_name, count(orders) as order_count').group('users.first_name').having('count(orders) > 5').order('order_count desc')
Hence both answers are working without any error. :D

Get Users with params and without others

I'm trying to get group's users with specific ids that are not admin.
For the moment I have:
group.users
.joins(:roles)
.where(id: user_ids)
.where.not(roles: { role_type: Role::Type::ADMIN })
.pluck(:id)
In my log I have:
SQL to load the group:
(0.3ms) SELECT "users"."id" FROM "users" INNER JOIN "groups_users"
ON "users"."id" = "groups_users"."user_id"
WHERE "groups_users"."group_id" = $1 [["group_id", 137375]]
SQL for the query above:
(0.6ms) SELECT "users"."id" FROM "users" INNER JOIN "roles"
ON "roles"."user_id" = "users"."id" AND "roles"."is_destroyed" = $1
INNER JOIN "groups_users" ON "users"."id" = "groups_users"."user_id"
WHERE "groups_users"."group_id" = $2 AND "users"."id" IN (82884, 82885)
AND "roles"."role_type" != $3 [["is_destroyed", "f"],
["group_id", 137375], ["role_type", 1]]
The problem is I always get all the users of the group with matching user_ids. The where.not is not effective.
I had to do something like
users_in_group = group.users.where(id: user_ids).pluck(:id)
users_in_group -= group.users.joins(:roles).where
(roles: { role_type: Role::Type::ADMIN}).pluck(:id)
I don't understand why.
If you want to exclude Admins even if they have other roles, you might use SQL EXISTS:
group.users
.where(id: user_ids)
.where("NOT EXISTS (SELECT 1 FROM roles WHERE user_id = users.id AND role_type = ?", Role::Type::ADMIN)
.pluck(:id)
And, handling typical objection to such advice: it's perfectly fine to get your hands dirty by writing fragments of SQL when you are using ActiveRecord in Rails. You shouldn't limit yourself to the (not so broad) possibilities of its DSL.
UPD.
To simplify your code, you can use Where Exists gem (disclosure: I've written it recently).
Add gem 'where_exists' to your Gemfile, run bundle install, and then the following should work:
group.users
.where(id: user_ids)
.where_not_exists(:roles, role_type: Role::Type::ADMIN)
.pluck(:id)

SQL Injection in ActiveRecord

I'm trying to build a vulnerable demo application. I'm using SQLite, and I have ruby code that looks like this:
#value = current_user.accounts.calculate(:sum, params[:column])
And the SQL generates the following by default:
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
(0.1ms) SELECT SUM("accounts"."account_value") AS sum_id FROM "accounts" WHERE "accounts"."user_id" = ? [["user_id", 1]]
Next, I put ssn) FROM users WHERE name = 'Texas'; -- into the form and I get the following:
(0.3ms) SELECT SUM(ssn) FROM users WHERE name = 'Texas'; --)) AS sum_id FROM "accounts" WHERE "accounts"."user_id" = ? [["user_id", 1]]
SQLite3::RangeException: bind or column index out of range: SELECT SUM(ssn) FROM users WHERE name = 'Texas'; --)) AS sum_id FROM "accounts" WHERE "accounts"."user_id" = ?
Completed 500 Internal Server Error in 2ms
ActiveRecord::StatementInvalid (SQLite3::RangeException: bind or column index out of range: SELECT SUM(ssn) FROM users WHERE name = 'Texas'; --)) AS sum_id FROM "accounts" WHERE "accounts"."user_id" = ?):
app/controllers/instant_calculator_controller.rb:3:in `sum'
I think the issue is that the 'user_id' section tacked onto the end as a paramiterized query is messing this up. I tried doing something like ssn) FROM users WHERE name = 'Texas'OR user_id = ?; -- just to throw that part of the query away, but that didn't seem to help.
Does anyone have any thoughts on I could make this work? I can change the code as well as the query, but I'd prefer to change the query before changing to code to make it SQLiable.
EDIT:
A bit more info. If I take the SQL that is generated and just change the last user_id to '1' so it looks like SELECT SUM(ssn) FROM users WHERE name = 'Texas'; --) AS sum_id FROM 'accounts' WHERE 'accounts'.'user_id' = 1 it works perfectly. I don't understand why this matters as everything after -- should be ignored.

Re-writing a SQL Query using Active Record or the Squeel Gem in Ruby on Rails 4

I am having a lot of problems rewriting this SQL Query in Squeel or straight Active Record using Ruby on Rails 4.
All 3 numbers in the query need to be passed to the query before execution. The SQL Query is below.
SELECT "users".*
FROM "users"
WHERE "users"."id"
IN (SELECT "users"."id" FROM "users"
INNER JOIN marketing_assets ON users.id = marketing_assets.user_id
WHERE marketing_assets.marketing_platform_id= 3
GROUP BY users.id HAVING COUNT(DISTINCT marketing_assets.marketing_platform_id) = 1)
You can find out more detail about how it is being used at Error when trying to chain class method in controller in Ruby on Rails
marketing_assets_id = 3
limit_marketing_assets = 1
User.joins(:marketing_assets).where(:marketing_assets => {:marketing_platform_id => marketing_assets_id}).group(:users => :id).having("COUNT(DISTINCT marketing_assets.marketing_platform_id) = #{limit_marketing_assets}")
Here is the query I ended up writing using Squeel. It works for me.
User.where{users.id.in(User.joins{marketing_assets}
.where((['marketing_assets.marketing_platform_id = ?' ] * platforms.count)
.join(' OR '), *platforms).group{users.id}
.having{{marketing_assets =>
{COUNT(DISTINCT(marketing_assets.marketing_platform_id)) => platforms.count }}})}

How do I create this active_records query

I have a user model and a story model. Users have many stories.
I want to create a scope that returns the twenty-five users records for users who have created the most stories today, along with the amount of stories that they have created.
I know that there are people on SO that are great with active_records queries. I also know that I am not one of those guys:-(. Help would be greatly appreciated and readily accepted!
#UPDATE with the solution
I've been working with #MrYoshiji's suggestion, and here is what i came up with (note, I'm using this query in my active_admin dashboard):
panel "Today's Top Posters" do
time_range = Date.today.beginning_of_day..Date.today.end_of_day
table_for User.joins(:stories)
.select('users.username, count(stories.*) as story_count')
.group('users.id')
.where(:stories => {:created_at => time_range})
.order('story_count DESC')
.limit(25) do
column :username
column "story_count"
end
end
And low and behold, it works!!!!
Note, when I tried a simplified version of MrYoshiji's suggestion:
User.includes(:stories)
.select('users.username, count(stories.*) as story_count')
.order('story_count DESC') #with or without the group statement
.limit(25)
I got the following error:
> User.includes(:stories).select('users.username, count(stories.*) as story_count').order('story_count DESC').limit(25)
User Load (1.6ms) SELECT DISTINCT "users".id, story_count AS alias_0 FROM "users" LEFT OUTER JOIN "stories" ON "stories"."user_id" = "users"."id" ORDER BY story_count DESC LIMIT 25
ActiveRecord::StatementInvalid: PG::Error: ERROR: column "story_count" does not exist
LINE 1: SELECT DISTINCT "users".id, story_count AS alias_0 FROM "us...
It seems like includes don't like any aliasing.
I can't test this right now, running under Windows... Can you try it?
User.includes(:stories)
.select('users.*, count(stories.*) as story_count')
.group('users.id')
.order('story_count DESC')
.where('stories.created_at BETWEEN ? AND ?', Date.today.beginning_of_day, Day.today.end_of_day)
.limit(25)
Using .includes, as suggested by MrYshiji, did not work due to aliasing problems (see the original question for more info on this). Instead, I used .joins as follows to get the query results that I wanted (note, this query is inside my active_admin dashboard):
panel "Today's Top Posters" do
time_range = Date.today.beginning_of_day..Date.today.end_of_day
table_for User.joins(:stories)
.select('users.username, count(stories.*) as story_count')
.group('users.id')
.where(:stories => {:created_at => time_range})
.order('story_count DESC')
.limit(25) do
column :username
column "story_count"
end
end