rails support method for PSQL array values to ruby array? - ruby-on-rails

Pretty straight forward, not looking to reinvent the wheel but is there rails method that turns results from a PSQL array to a ruby array.
example
results = ActiveRecord::Base.connection.execute("select array_agg(id) from users;").to_a
# => [{"array_agg"=>"{1,2,3}"}]
thing.call(results[0]["array_agg"])
# => ["1", "2","3"]

ActiveRecord::Base.connection.execute returns PG::Result object
You need ActiveRecord::Result that have cast_values method (it uses deserialize under the hood)
exec_query does this job
ActiveRecord::Base.connection.exec_query("select array_agg(id) from users;").cast_values
# => [[1, 2, 3]]

Related

why does find with an array of ids return an Array whereas `where` with an array of ids returns an ActiveRecord::Relation

When I do:
[29] pry(main)> u=User.where("id in (?)",[1,2])
=> ActiveRecord::Relation
[31] pry(main)>
whereas:
[33] pry(main)> u=User.find([2,3])
...
[34] pry(main)> u.class
=> Array
[35] pry(main)>
I know that both of these are very similar but why doesn't find with an array return an ActiveRecord::Relation. Is there some deep reason? I would have thought that find with an array of id's would return an ActiveRecord::Relation.
thx
Actually in rails find calls find_with_ids method which in-turn calls find_some in case you passes an array as argument in it and find_some works as below:-
result = where(table[primary_key].in(ids)).to_a
i.e. coverting activerecord:relation object to an array. Hence result provides an array.
You may look at their source codes

Combine two ActiveRecord::Relation with OR, not AND, returning a Relation and not an Array to be able to paginate later

a and b are ActiveRecord::Relation objects which return the same type of objects(Micropost objects in this case)
a.class
=> ActiveRecord::Relation
b.class
=> ActiveRecord::Relation
a.first
=> Micropost(...)
b.first
=> Micropost(...) #They both return the same type of objects
c=a+b
c.class
=> Array #This is not what i'm looking for
c=a|b
c.class
=> Array #Not what i'm looking for either
c=(a or b)
c.class
=> ActiveRecord::Relation #But it is just a, so it's wrong
c==a
=> true
a.merge(b)
=> [] #It merges with AND which in my case results in an empty array
Is there some way to "OR" both Relation objects and return another Relation object in Rails 3.2.11? Do I need to use some gem such as squeel to do it?
If it can't be done: Why it can't be done? Can I paginate the array resulting from a|b without loading every record from the database?
If it can be done: Could anyone please write an example of how to do it?
Thanks in advance.
EDIT: I copied the wrong variables into the block of code, b was not an ActiveRecord::Relation, it was actually an array and that is why a.merge(b) returned [].
My fault.
TLDR: Question is wrong, and array can be paginated :)
For you question: Can I paginate the array resulting from a|b without loading every record from the database?
Yes, you can paginate array. Only you need to do is require 'will_paginate/array' file where your are using paginate for array. If you are using in many place then its better to add it in config/initializers. And its array so it won't load every record from database each time (i.e.) query is fired only once. To load association you can use eager load on array using include.

How to get a single column's values into an array

Right now I'm doing something like this to select a single column of data:
points = Post.find_by_sql("select point from posts")
Then passing them to a method, I'd like my method to remain agnostic, and now have to call hash.point from within my method. How can I quickly convert this into an array and pass the data set to my method, or is there a better way?
In Rails 3.2 there is a pluck method for this
Just like this:
Person.pluck(:id) # SELECT people.id FROM people
Person.pluck(:role).uniq # unique roles from array of people
Person.distinct.pluck(:role) # SELECT DISTINCT role FROM people SQL
Person.where(:confirmed => true).limit(5).pluck(:id)
Difference between uniq and distinct
You should use the pluck method as #alony suggested. If you are stuck before Rails 3.2 you can use the ActiveRecord select method together with Array#map:
Post.select(:point).map(&:point)
#=> ["foo", "bar", "baz"]
before Ruby 1.9 you'd have to do .map{|x| x.title} though, because Symbol#to_proc (aliased by the unary & operator) is not defined in earlier versions of Ruby.
If you see the definition of select_values , then it using 'map(&:field_name)'
def select_values(arel, name = nil)
result = select_rows(to_sql(arel), name)
result.map { |v| v[0] }
end
The common and general Rails way to collect all the fields values in array is like :
points = Post.all(:select => 'point').map(&:point)
points = Post.all.collect {|p| p.point}

get sliced results after sql execution

I am developing a Rails app.
after I execute a sql command like following:
sql="select * from some_tbl;"
rslt = ActiveRecord::Base.connection.execute(sql)
how can I get sliced results from rslt?
I mean for example, if the rslt.size is 200, I would like to get 20 results start from the 5th one (which like the array operation arr[5,20]), how to do it?
Try to convert rslt to Array:
...
rslt.to_a[5,20]
If you don't need to fetch all the instances of the table, you can do:
Model.offset(5).limit(20)
You should read that, it will probably help: http://guides.rubyonrails.org/active_record_querying.html#limit-and-offset
The object returned from ActiveRecord::Base.connection.execute is in fact an Array which means you can use rslt[5,20] to obtain the 20 results starting at the 6th element.
I'd also like to point out that you can use find_in_batches which is an API provided by ActiveRecord.
The example given on the API page is:
Person.where("age > 21").find_in_batches do |group|
sleep(50) # Make sure it doesn't get too crowded in there!
group.each { |person| person.party_all_night! }
end
EDIT: sorry it didn't work for you. direct from my rails (3.0.10) console
ruby-1.8.7-p299 :006 > sql = "select * from domains"
=> "select * from domains"
ruby-1.8.7-p299 :007 > ActiveRecord::Base.connection.execute(sql).class
=> Array
ruby-1.8.7-p299 :008 > ActiveRecord::Base.connection.execute(sql).length
=> 18
ruby-1.8.7-p299 :010 > ActiveRecord::Base.connection.execute(sql)[5,20].length
=> 13
You dont need to use sql in rails you use ActiveRecord to create, read, update and delete entries in the database. You should read the beginners guides from http://guides.rubyonrails.org/ especially the Model section will be interresting for you because its about dealing with the datbase!

ActiveRecord :select is messing with my data types

So I have this very simple snippet.
Topic.all(:select => 'count(*) as cnt')[0].cnt # behaves the same on all models
"500" # What, a string?
It seems that for some reason ActiveRecord is coercing the count to a string. In fact, I notice it coerces everything in the select list missing from the original object to a string.
Why does this happen?
What is a monkey patch to fix this so it works as expected?
Please: I do not want to use Topic.count, I have some tricky grouping and selecting I need to do.
FYI using ActiveRecord 2.3.5
Proof that the info is in the raw connection:
ree-1.8.7-2009.10 > r = ActiveRecord::Base::connection.raw_connection.query(
"select 1").fetch_field.type == Mysql::Field::TYPE_LONGLONG
=> true
ree-1.8.7-2009.10 > r = ActiveRecord::Base::connection.raw_connection.query(
"select 'a'").fetch_field.type == Mysql::Field::TYPE_VAR_STRING
=> true
And when using the Stmt APIs you get stuff with the right types back automatically
ree-1.8.7-2009.10 > s = ActiveRecord::Base::connection.raw_connection.stmt_init
=> #<Mysql::Stmt:0xbd083dc>
ree-1.8.7-2009.10 > s.prepare("select 1, 'hello'")
=> #<Mysql::Stmt:0xbd083dc>
ree-1.8.7-2009.10 > s.execute
=> #<Mysql::Stmt:0xbd083dc>
ree-1.8.7-2009.10 > s.fetch
=> [1, "hello"]
ree-1.8.7-2009.10 >
But ... the Active Record MySql Adapter uses the query API that returns strings.
ree-1.8.7-2009.10 > cnn.query("select 1,2,3 union all select 1,2,3").each{|r| p r}
["1", "2", "3"]
["1", "2", "3"]
It might actually be because there is no easy way to assume what the value is, so String is 'safe'. Anything in a select that does not map to a column can't be introspected for a type.
The problem is MySQL. When MySQL returns the result set, the result set does not have information as to what types are the columns. ActiveRecord can only do the best it can: treat everything as a String.
ActiveRecord can treat columns coming back from a "normal" query (SELECT id FROM topics) as the correct type because ActiveRecord asked the DB for the types of the columns.

Resources