rails 5 enum where "like" - activerecord

I'm trying to query an activerecord model enum and use the like operator in the where method, and it just doesnt work. Is there some trick to allow me to query an enum this way? Works fine on regular columns. Here it is in the console.
Regular string column (title) works as shown below
irb(main):092:0> Proposal.select(:id,:department,:status).where('title like "test%"')
Proposal Load (0.3ms) SELECT "proposals"."id", "proposals"."department", "proposals"."status" FROM "proposals" WHERE (title like "test%") LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Proposal id: 7, department: 1, status: "In Progress">, #<Proposal id: 61, department: 2, status: "Won">]>
However, trying it on an enum, gives no results.
irb(main):094:0> Proposal.select(:department,:status).where('status like "Wo%"')
Proposal Load (0.3ms) SELECT "proposals"."department", "proposals"."status" FROM "proposals" WHERE (status like "Wo%") LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation []>
Any idea why I can't use like operator on enum? I'm trying to use this to filter a view with datatables.net server side processing.

Enum stores data as integer like 0,1,2,3... Then rails map number to enum value defined in model. That is the reason why you doesn't get result

Related

i need to get a user id from a query result

am querying from my User table using the user name, how do i get the user id from the query object
User.where(user_name: "john")
my goal is to get the User id: 5, i thought it was as easy as (.id), but thats not working
=> #<ActiveRecord::Relation [#<User id: 5, user_name: "john", user_city: "India", created_at: "2019-01-19 18:02:32", updated_at: "2019-01-19 18:02:32">]>
Materialized where will return a collection, so you can get first record with .first
User.where(:user_name => "john").first&.id
Or use find_by which will return first record which satisfies condition.
User.find_by(:user_name => "john")&.id
If you're really only trying to get the id (i.e., not using any other attributes of the object) it's usually better to formulate this as a .pluck. This requires less time for ActiveRecord to instantiate, and makes the query job take better use of your database indices. As long as User#user_name is unique (and I'd hope so!) it will return an array of length 1.
User.where(user_name: "John").first.&id
# SELECT "users".* FROM "users" WHERE "users"."user_name" = "John"
# ORDER BY "users" ASC LIMIT 1
# => 1 or nil
User.where(user_name: "John").pluck(:id).first
# SELECT "users"."id" FROM "users" WHERE "users"."user_name" = "John"
# => 1 or nil
Unfortunately, find_by and its helpers don't work this way with pluck, and both of these statements instead result in errors
User.find_by(user_name: "John").pluck(:id)
User.find_by_user_name("John").pluck(:id)

group method for activerecord. Docs are confusing?

I don't get what this means in the Rails tutorial:
group(*args) public
Allows to specify a group attribute:
User.group(:name)
=> SELECT "users".* FROM "users" GROUP BY name
Returns an array with distinct records based on the group attribute:
User.group(:name) [User id: 3, name: "Foo", ...>, #User id: 2, name: "Oscar", ...>]
I don't see the grouping with the example they gave...
Group is most useful (I think) if you are trying to count stuff in your database or if you join multiple tables. Let me give a few examples.
1.
If you want to know how many users there are in your data base with each name then you can do:
User.group(:name).count
this will return a hash looking something like this:
{ ann: 4, bert: 15, cecilia: 3 ... }
I do not know why there are so many Berts in your database but anyway...
2.
If your users have related records (for instance cars) the you can use this to get the first car included in your activerecord model (the reason it will be the first is because of how group works and is further explained in the link below)
User.joins(:cars).select('users.*, cars.model as car_model, cars.name as car_name').group('users.id')
Now all records in this result will have a method called car_model and one called car_name.
You can count how many cars each user has with one single query.
User.joins(:cars).select('users.*, count(cars.id) as car_count').group('users.id')
Now all records will have a car_count.
For further reading: Mysql group tutorial
Hope this shed enough light over groups for you to try them out a little bit. I do not think you can fully understand them until you worked with them a little bit.

rails find_by_name getting wrong result

I'm working on a rails 2 project. I'm trying to fetch a record from tags table by using find_by_* . Its giving different result.
May I know why is this working like this?
In my model:
existing = user.tags.find_by_name(tag)
in Log:
SELECT * FROM `tags` WHERE (`tags`.`name` = 'Ror') AND (`tags`.user_id = 1) LIMIT 1
RuntimeError (#<Tag id: 980191043, user_id: 1, name: "rOr", created_at: "2014-09-09 12:18:55", updated_at: "2014-09-09 12:18:55">):
Are you using MySQL?
If so it is likely it is doing a case insensitive comparison. Whether MySQL is case sensitive is based around the field collation of the column: http://dev.mysql.com/doc/refman/5.0/en/charset-column.html

Multiple queries on single object in DataMapper

When I try to run the following code, DataMapper calls for 3 queries in just these two lines. Can anyone explain why it would do this?
#u = User.first(:uid => 1, :fields => [:uid, :name])
json #u
This calls the following queries:
SELECT "uid", "name" FROM "users" WHERE "uid" = 1 ORDER BY "uid" LIMIT 1
SELECT "uid", "email" FROM "users" WHERE "uid" = 1 ORDER BY "uid"
SELECT "uid", "accesstoken" FROM "users" WHERE "uid" = 1 ORDER BY "uid"
It is worth noting that datamapper has a validation on name for being => unique
Also, the accesstoken is lazily loaded so it should only be queried when asked for specifically, which must be happening when serializing it to a json object.
EDIT:
I have added my model class for clarification. I just want one query made for the uid and name without having to extract them individually from the object. Maybe this is the only way?
property :uid, Serial
property :name, String
property :email, String
property :accesstoken, Text
ANSWER:
Use the dm-serializer gem that has this support built-in
https://github.com/datamapper/dm-serializer
The first query is invoked by your User.first... call. Notice the fields it's selecting are what you requested - uid and name
The second and third queries are getting run in the json serialization, as it's lazy loading each property you didn't already load.
So you either need to do a custom serialization to only output uid and name for your users, or you should just remove the field selection from your initial query so it all gets loaded at once.
Update:
To do a custom serialization with datamapper, you can use the dm-serializer gem https://github.com/datamapper/dm-serializer and call #u.to_json(only: [:uid, :name])
Alternatively in this simple case you could just build the serialized object you want yourself, for which there are many examples: Rails3: Take controll over generated JSON (to_json with datamapper ORM)

Subqueries in activerecord

With SQL I can easily do sub-queries like this
User.where(:id => Account.where(..).select(:user_id))
This produces:
SELECT * FROM users WHERE id IN (SELECT user_id FROM accounts WHERE ..)
How can I do this using rails' 3 activerecord/ arel/ meta_where?
I do need/ want real subqueries, no ruby workarounds (using several queries).
Rails now does this by default :)
Message.where(user_id: Profile.select("user_id").where(gender: 'm'))
will produce the following SQL
SELECT "messages".* FROM "messages" WHERE "messages"."user_id" IN (SELECT user_id FROM "profiles" WHERE "profiles"."gender" = 'm')
(the version number that "now" refers to is most likely 3.2)
In ARel, the where() methods can take arrays as arguments that will generate a "WHERE id IN..." query. So what you have written is along the right lines.
For example, the following ARel code:
User.where(:id => Order.where(:user_id => 5)).to_sql
... which is equivalent to:
User.where(:id => [5, 1, 2, 3]).to_sql
... would output the following SQL on a PostgreSQL database:
SELECT "users".* FROM "users" WHERE "users"."id" IN (5, 1, 2, 3)"
Update: in response to comments
Okay, so I misunderstood the question. I believe that you want the sub-query to explicitly list the column names that are to be selected in order to not hit the database with two queries (which is what ActiveRecord does in the simplest case).
You can use project for the select in your sub-select:
accounts = Account.arel_table
User.where(:id => accounts.project(:user_id).where(accounts[:user_id].not_eq(6)))
... which would produce the following SQL:
SELECT "users".* FROM "users" WHERE "users"."id" IN (SELECT user_id FROM "accounts" WHERE "accounts"."user_id" != 6)
I sincerely hope that I have given you what you wanted this time!
I was looking for the answer to this question myself, and I came up with an alternative approach. I just thought I'd share it - hope it helps someone! :)
# 1. Build you subquery with AREL.
subquery = Account.where(...).select(:id)
# 2. Use the AREL object in your query by converting it into a SQL string
query = User.where("users.account_id IN (#{subquery.to_sql})")
Bingo! Bango!
Works with Rails 3.1
Another alternative:
Message.where(user: User.joins(:profile).where(profile: { gender: 'm' })
This is an example of a nested subquery using rails ActiveRecord and using JOINs, where you can add clauses on each query as well as the result :
You can add the nested inner_query and an outer_query scopes in your Model file and use ...
inner_query = Account.inner_query(params)
result = User.outer_query(params).joins("(#{inner_query.to_sql}) alias ON users.id=accounts.id")
.group("alias.grouping_var, alias.grouping_var2 ...")
.order("...")
An example of the scope:
scope :inner_query , -> (ids) {
select("...")
.joins("left join users on users.id = accounts.id")
.where("users.account_id IN (?)", ids)
.group("...")
}

Resources