getting meta data from resultset object in rails - ruby-on-rails

I am using some custom queries in rails.
code snippet looks like
#time_spent = TimeEntry.find(:all,
:joins => "INNER JOIN sometable ON x = y",
:select =>"id, subject, spent_on")
now to get values I am using
#time_spent[index][:spent_on]
#time_spent[index][:subject]
what I want is to use index numbers in place of symbols. So that at run time I don't need to know the fields in the select clause.
for e.g. i want to do some thing similar to this
#time_spent[index][1]
#time_spent[index][2]
or
If I could get metadata of resultset i can use that information
Comments please?

When #time_spent is a collection of objects, this will get the attribute's value for the index specified for the first [0] item in that collection:
#time_spent[0].attributes.values[index]
So, for example, to get the 5th attribute's value for the 2nd object in the collection:
#time_spent[1].attributes.values[4]

to get field names from result set, use attributes.keys method

Related

Using .select or .map to get an array of names from an activerecord model

I am trying to get an array of names from an activerecord model based on a search query.
I have this method in my item model.
def self.search(search)
if search
where(['lower(name) LIKE ?', "%#{search}%"])
else
Item.all
end
end
I am trying to figure out the difference between using these two lines they are both returning the same thing.
Item.search('ex').select('name').map(&:name) vs
Item.search('ex').map(&:name)
Item.search('ex').select('name').map(&:name)
In the above statement, you are selecting only the name column from the result of Item.search('ex') and then getting the names for all of them using .map(&:name).
But, in the follwing statement:
Item.search('ex').map(&:name)
You are not selecting the name column, just getting the names using .map(&:name) from the result of Item.search('ex').
And Yes, they return the exact same result.
So, if you want the array of names only from the search result, then selecting the name column is redundant. Just go with this:
Item.search('ex').map(&:name)
Or, even better, do it using pluck:
Item.search('ex').pluck(:name)
It bypasses the need for instantiating every ActiveRecord object, and instead just returns the queried values in an Array directly, which improves performance both in terms of execution time and memory consumption.
Basically what .select does is what we call a projection, which is filtering the fields that are returned by the query. However, if you do not call .select at all, Rails default to selecting all the fields from the table.
So the diference between Item.search('ex').select('name') and Item.search('ex') is that the former selects only the column name and the latter selects all the columns from the items table.
Given that, when you map all the items to grab only the name, it doesn't make any diference, since both ways of doing have name selected.
1st Query: Item.search('ex').select('name').map(&:name)
Let's take the entire statement piece by piece.
Item.search('ex') # it trigger one SQL query and return `ActiveRecord::Relation` object in Array
SELECT `items`.* FROM `items` WHERE (lower(name) LIKE '%ex%')
Next
Item.search('ex').select('name')
SELECT `items`.`name` FROM `items` WHERE (lower(name) LIKE '%ex%')
As we might can see it return ActiveRecord::Relation object in Array with selecting name fields.
Now
Item.search('ex').select('name').map(&:name)
SELECT `items`.`name` FROM `items` WHERE (lower(name) LIKE '%ex%')
Further here as you are calling #map method on ActiveRecord::Relation object which itself defined in Enumerable Module.
#map Returns a new array with the results of running block once for
every element in enum.
2nd Query: Item.search('ex').map(&:name)
SELECT `items`.* FROM `items` WHERE (lower(name) LIKE '%ex%')
Here you are calling#map method on ActiveRecord::Relation object. Further you are saying, " Hey ActiveRecord::Relation I need only name field from search object, and ActiveRecord::Relation replay back to give all name in form of array. "
For more information The clever hack that makes items.map(&:name) work.
Hope this help !!!

rails combine parameters in controller

Hopefully this is a little clearer. I'm sorry but I'm very new to coding in general. I have multiple tables that I have to query in succession in order to get to the correct array that I need. The following logic for the query is as follows:
this gives me an array based upon the store :id
store = Stores.find(params[:id])
this gives me another array based upon the param .location found in the table store where that value equals the row ID in the table Departments
department = Departments.find(store.location)
I need to preform one last query but in order to do so I need to figure out which day of the meeting is needed. In order to do this I have to create the parameter day_of_meeting found in the table Stores. I try to call it from the array above and create a new variable. In the Table Departments, I there are params such as day_1, day_2 and so on. I need to be able to call something like department.day_1 or department.day_2. Thus, I'm trying to actually create the variable by join the words "department.day_" to the variable store.day_of_meeting which would equal some integer, creating department.day_1...
which_day = ["department.day_", store.day_of_meeting].join("")
This query finds uses the value found from the variable department.day_1 to query table Meeting to find the values in the corresponding row.
meeting = Meeting.find(which_day)
Does this make my problem any clearer to understand?
findmethod can only accept parameters like Meeting.find(1) or Meeting.find("1-xx").
so, what you need is Meeting.find(department.send("day_" + store.day_of_meeting.to_s))
Hope to help!

Modifying the returned value of find_by_sql

So I am pulling my hair over this issue / gotcha. Basically I used find_by_sql to fetch data from my database. I did this because the query has lots of columns and table joins and I think using ActiveRecord and associations will slow it down.
I managed to pull the data and now I wanted to modify returned values. I did this by looping through the result ,for example.
a = Project.find_by_sql("SELECT mycolumn, mycolumn2 FROM my_table").each do |project|
project['mycolumn'] = project['mycolumn'].split('_').first
end
What I found out is that project['mycolumn'] was not changed at all.
So my question:
Does find_by_sql return an array Hashes?
Is it possible to modify the value of one of the attributes of hash as stated above?
Here is the code : http://pastie.org/4213454 . If you can have a look at summarize_roles2() that's where the action is taking place.
Thank you. Im using Rails 2.1.1 and Ruby 1.8. I can't really upgrade because of legacy codes.
Just change the method above to access the values, print value of project and you can clearly check the object property.
The results will be returned as an array with columns requested encapsulated as attributes of the model you call this method from.If you call Product.find_by_sql then the results will be returned in a Product object with the attributes you specified in the SQL query.
If you call a complicated SQL query which spans multiple tables the columns specified by the SELECT will be attributes of the model, whether or not they are columns of the corresponding table.
Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
> [#<Post:0x36bff9c #attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
Source: http://api.rubyonrails.org/v2.3.8/
Have you tried
a = Project.find_by_sql("SELECT mycolumn, mycolumn2 FROM my_table").each do |project|
project['mycolumn'] = project['mycolumn'].split('_').first
project.save
end

ActiveRecord find and only return selected columns

edit 2
If you stumble across this, check both answers as I'd now use pluck for this
I have a fairly large custom dataset that I'd like to return to be echoe'd out as json. One part is:
l=Location.find(row.id)
tmp[row.id]=l
but I'd like to do something like:
l=Location.find(row.id).select("name, website, city")
tmp[row.id]=l
but this doesn't seem to be working. How would I get this to work?
thx
edit 1
alternatively, is there a way that I can pass an array of only the attributes I want included?
pluck(column_name)
This method is designed to perform select by a single column as direct SQL query Returns Array with values of the specified column name The values has same data type as column.
Examples:
Person.pluck(:id) # SELECT people.id FROM people
Person.uniq.pluck(:role) # SELECT DISTINCT role FROM people
Person.where(:confirmed => true).limit(5).pluck(:id)
see http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-pluck
Its introduced rails 3.2 onwards and accepts only single column. In rails 4, it accepts multiple columns
In Rails 2
l = Location.find(:id => id, :select => "name, website, city", :limit => 1)
...or...
l = Location.find_by_sql(:conditions => ["SELECT name, website, city FROM locations WHERE id = ? LIMIT 1", id])
This reference doc gives you the entire list of options you can use with .find, including how to limit by number, id, or any other arbitrary column/constraint.
In Rails 3 w/ActiveRecord Query Interface
l = Location.where(["id = ?", id]).select("name, website, city").first
Ref: Active Record Query Interface
You can also swap the order of these chained calls, doing .select(...).where(...).first - all these calls do is construct the SQL query and then send it off.
My answer comes quite late because I'm a pretty new developer. This is what you can do:
Location.select(:name, :website, :city).find(row.id)
Btw, this is Rails 4

Problem with sorting by row ( special case )

I have a requirement for sorting Contacts records by primary_contact_no.
My Contact fields contain primary_contact_no ,email , mobile_no.
this is no brainier....
BUT my view requires me to show mobile_no under Contact Number(view label) when primary_contact_no is not present.
Contacts.find(:all, :order => "primary_contact_no")
Now When i sort it by primary_contact , in the view , the records where these fields are absent get replaced with mobile_no but since they are already sorted by contact_no they appear at the bottom of the search result.
How can i combine the two results ( in case primary_contact is not present and carry out search on the combined record )
Is there any other solution to the problem where i can combine the row search records or something like that???
P.S.
I have used will paginate.
You could order once you retrieve them from the database.
So
contacts = Contact.all
u.sort!{|a,b| a.con_number<=> b.con_number}
Then in your Contact Model
def con_number
primary_contact_no||mobile_no
end
MySQL and PostgreSQL both have COALESCE function, so you can do something like:
Contacts.find(:all, :order => "COALESCE(primary_contact_no,mobile_no)")
to sort the records as you want. But beware, using sql functions and raw sql has its caveats. If you decide to switch databases, you have to check if each raw sql and sql function you used like this is supported in your new RDBMSI.
I would not sort the records in my application, as that means, I can not use pagination of will paginate to select limited data and have to retrieve full set of records, sort them and then use the relevant records based on pagination parameters. It will increase the response time consistently as the contacts table grows.

Resources