find_by_sql not working on rails 3.1.3 - ruby-on-rails

I am writing a quite complex query in Ruby on Rails 3.1.3, and I am using find_by_sql.
But I noticed a very strange behaviour, even if I use find_by_sql with very simple queries.
Here is a simple example:
Let' say that I have two models and related tables:
Model 1: Company
Table 1: companies
fields: id, name, address
| id | name | address |
+----+------+-----------------+
| 1 | ACME | Bond street, 56 |
and:
Model 2: Employee
Table 2: employees
fields: id, name, age
| id | name | age |
+----+------+-----+
| 1 | Fred | 56 |
| 2 | Adam | 27 |
Here is what happens; if I write:
Company.find_by_sql("SELECT * FROM `employees`")
I get:
Company Load (0.3ms) SELECT * from `employees`
=> [#<Company id: 1, name: "Fred">, #<Company id: 2, name: "Adam">]
I only get the fields of employees whose names match the ones in companies (i.e., the field age is missing)!
No #attributes at all.
Is it a bug? Can anyone please help me?

The console uses pretty printing to output data from any instances returned by the query. Pretty printing is implemented automatically in the class by ActiveRecord according to the columns associated with that particular model, and won't therefore display attributes that do not belong to that particular model.
That does not mean however the attributes were not loaded into the instances. Pretty printing is just not showing them, but they are still there.
So if you just do:
Company.find_by_sql("SELECT * from employees").first.age
You should still get 56 according to your example.

try:
Employee.find_by_sql("SELECT * FROM `employees`")

If you're selecting from the employees table you will want to use the Employee model.
Try this instead
Employee.find_by_sql("SELECT id, name, age FROM `employees`")

Related

Using Crosstab to Generate Data for Charts

I'm trying to make an efficient query to create a view that will contains counts for the number of successful logins by day as well as by type of user with no duplicate users per day.
I have 3 tables involved in this query. One table that contains all successful login attempts, one table for standard user accounts, and one table for admin user accounts. All user_id values are unique across the entire database so there are no user accounts that will share the same user_id with an admin account:
TABLE 1: user_account
user_id | username
---------|----------
1 | user1
2 | user2
TABLE 2: admin_account
user_id | username
---------|----------
6 | admin6
7 | admin7
TABLE 3: successful_logins
user_id | timestamp
---------|------------------------------
1 | 2022-01-23 14:39:12.63798-07
1 | 2022-01-28 11:16:45.63798-07
1 | 2022-01-28 01:53:51.63798-07
2 | 2022-01-28 15:19:21.63798-07
6 | 2022-01-28 09:42:36.63798-07
2 | 2022-01-23 03:46:21.63798-07
7 | 2022-01-28 19:52:16.63798-07
2 | 2022-01-29 23:12:41.63798-07
2 | 2022-01-29 18:50:10.63798-07
The resulting view I would like to generate would contain the following information from the above 3 tables:
VEIW: login_counts
date_of_login | successful_user_logins | successful_admin_logins
---------------|------------------------|-------------------------
2022-01-23 | 1 | 1
2022-01-28 | 2 | 2
2022-01-29 | 1 | 0
I'm currently reading up on how crosstabs work but having trouble figuring out how to write the query based on my table setups.
I actually was able to get the values I needed by using the following query:
SELECT
to_char(s.timestamp, 'YYYY-MM-DD') AS login_date,
count(distinct u.user_id) AS successful_user_logins,
count(distinct a.user_id) AS successful_admin_logins
FROM successful_logins s
LEFT JOIN user_account u ON u.user_id= s.user_id
LEFT JOIN admin_account a ON a.user_id= s.user_id
GROUP BY login_date
However, I was told it would be even quicker using crosstabs, especially considering the successful_logins table contains millions of records. So I'm trying to also create a version of the query using crosstabs then comparing both execution times.
Any help would be greatly appreciated. Thanks!
Turns out it isn't possible to do what I was asking about using crosstabs, so the original query I have will have to do.

Change the type string in ActiveRecord polymorphic associations

I am working with an existing dataset that I cannot really change for now. Fortunately I already have a *_id and *_type column in one of the tables. The problem is that the _type column is not the name of the class I want to associate with, it's an integer.
So for example I have:
Picture:
----------------
|id|x_type|x_id|
| 1| 1 | 1 |
Employee (x_type: 1):
---------
|id|name|
| 1|foo |
Business (x_type: 2):
---------
|id|name|
| 1|foo |
So the query is going to need to look for x_type = 1 instead of 'Employee' and x_type = 2 instead of Business
Just add a migration which creates a 'type column' and update the value with Employee where x_type is 1 and Business where x_type is 2. And make sure 'type' column is the one used for your polymorphic association.

Rails ActiveRecord return records where id exists in related table

I have a Client model and a Product model where a Client has many Products and a Product belongs to a CLient.
I need to find a query that only returns Clients if they have a record in the Product table
clients table
id | name
--------------
1 | Company A
2 | Company B
3 | Company C
products table
id | name | client_id
---------------------------
1 | Product A | 1
2 | Product B | 1
3 | Product C | 3
4 | Product D | 3
5 | Product E | 1
I only need Clients 1 3
For example something like
#clients = Client.where("client exists in products") #something to this effect
Simplest but not the fastest:
Client.where(:id => Product.select(:client_id).map(&:client_id))
SQL subquery (more faster):
Client.where("EXISTS(SELECT 1 from products where clients.id = products.client_id)")
Here's another solution. It's a subquery like Valery's second solution, but without writing out the sql:
Client.where(Product.where(client_id: Client.arel_table[:id]).exists)
Here is the solution which uses Where Exists gem (disclosure: I'm its author):
Client.where_exists(:products)
Another gem that exists to do that: activerecord_where_assoc (I'm the author)
With it:
Client.where_assoc_exists(:products)
If you had to also specify some of the products, when you could do it like this:
Client.where_assoc_exists(:products, id: my_products.map(&:id))
Doing it without a gem makes it easy to do mistakes.
Read more in the documentation. Here is an introduction and examples.
Also not the fastest but is concise:
Client.where(:id => Product.pluck(:client_id))

Dynamic categories criteria with matching products rails 3

I'm working on a new rails project, and in this project I have products and product categories.
So these categories are very different from each other, to name some, Boats, Houses, Cars.
The car category might have criterias like "Mph", "Model", "Brand", "Year" and so on. Where the house category will have something like "Rooms", "Year", "City", "Postal Code" etc.
I would like this to be very dynamic, so that i would be able to add/remove criterias and add/remove categories from a backend panel.
Now to my question, i have been playing around with this, and i can't really figure out the logic of this concept, i have tried some solutions, however they are very weird and quite inefficient. Maybe some hardcore rails coder could give me a hint, on how to solve this puzzle?
So the best solution i could come up with, was this:
Four models :
_______________________
| Product.rb |
-----------------------
| id | integer |
-----------------------
| category_id | integer |
-----------------------
| Title | string |
-----------------------
| Description | text |
-----------------------
_______________________
| Category.rb |
-----------------------
| id | integer |
-----------------------
| Title | string |
-----------------------
| Description | text |
-----------------------
_______________________
| Criteria.rb |
-----------------------
| id | integer |
-----------------------
| category_id | integer |
-----------------------
| Name | string |
-----------------------
| Default | string |
-----------------------
| Description | text |
-----------------------
_______________________
| ProductInfo.rb |
-----------------------
| id | integer |
-----------------------
| product_id | integer |
-----------------------
| Name | string |
-----------------------
| Value | text |
-----------------------
How it's connected :
Criteria.rb is connected to Category.rb with a category_id and has_many/belongs_to relation
Product.rb is connected to Category.rb with a category_id and has_many/belongs_to relation
ProductInfo.rb is connected to Product.rb with a product_id and has_many/belongs_to relation.
Category.rb is the heart og this solution. The category model, both have many products and criterias.
How it should work, in reality :
In the show category page, i would first print out all the criterias for the given category.
Afterwards i would make a #products.each do |product|.
In the #products.each block, i would make a #category.criterias.each do |criteria|.
In the #category.criterias.each block, i would then run something like product.productinfos.where(:name => criteria.name).
And then run it one by one.
Conclusion, this solution do work, however i doubt that it is the best solution. It will make an extremely big loadtime, with high traffic and many data. And i will need to write very weird and unreadable code.
This is a rather long question, and it might be very confusing, so if there is anything please just say. Also, i have searched quite alot for a question like this, both on Stackoverflow, and on google but i have not been able to find anything like this.
Oluf Nielsen.
In my opinion, it's better not to define additional tables to handle this due lots of performance issues. My preference to handle such things is to use a serialized column in the products table. Ability to search directly in the database is reduced with this approach, but then you wouldn't want to do that anyway. To handle search, you have to add some sort of indexed searching mechanism. Like acts_as_ferret or even Solr or ElasticSearch.
If you are using postgres check out https://github.com/softa/activerecord-postgres-hstore
For Mysql, use the rails's built in 'store'
http://api.rubyonrails.org/classes/ActiveRecord/Store.html
class Product < ActiveRecord::Base
has_and_belongs_to_many :categories
store :settings
end
To set criteria for each category do something similar to this:
class Category < ActiveRecord::Base
has_and_belongs_to_many :products
def criteria
#criteria_list ||= self[:criteria].split('|')
#criteria_list
end
def criteria=(names)
self[:criteria] = names.join('|')
end
end
Everytime a product is added to a category, check if all of the criteria in that category is available in the product's properties hash keys. If not, add it with a default value if needed.
You can also setup accessors for the properties hash store using a proc that dynamically gets the accessor names from the all the criteria field of the categories of the product? (not sure about this, cause I haven't done this before)
You can also look into using STI (Single table Inheritance) using a type field in your products table. (It's well documented) This approach is slightly better 'cause when products move from one category to another, the properties won't change.
class Gadget < Product
store_accessor :manufacturer, :model
end
class Phone < Gadget
store_accessor :os, :touch_screen, :is_smart
end
Hope this helps
Or else, second approach would be go with a nosql database. Try mogodb with mongoid, which is quite stable. This will suite your requirements for variable attributes very well. Also you can add any other categories later with very ease.
As far as I can see, with mysql, you will end up creating multiple dbs for storing this dynamics data and that is bound hamper the performance.
UPDATE -
Apart from the point that your data can be flexible with nosql, there are many things you need to consider before shifting there. I just suggested based on fact that you need flexible database structure. Start with mogodb docs, they are good starting point.

How to make selection content an attribute for a Rails model

I am having a hard time even formulating the question I want to be answered, so here's my situation:
I'm trying to make a simple stock market plotter tool using an existing database I populate elsewhere. My app already has a nice and dynamic plotter that works with any database, but it expects data in a certain way. So say my model (database) looks like this:
Stock:
|___ticker___|___open___|___close___|___date___|
| aapl | 100 | 101 | 1/1/11 |
| aapl | 101 | 102 | 1/2/11 |
| goog | 500 | 450 | 1/1/11 |
| goog | 450 | 451 | 1/2/11 |
...
My plotter routines work off of class attributes (I think thats the terminology), which correspond to columns in the database.
I can select all the data corresponding to 'aapl', and easily plot the open and close versus date since my model has said attributues.
#stock = Stock.select_by_ticker('aapl')
>> #stock.open #=> 100 ...
>> #stock.close #=> 101 ...
>> #stock.date #= 1/1/11 ...
so the attributes would be
{open, close, date}
But if I want to compare say the closing price for different stocks, I need attributes pertaining to each stock. So basically I want to end up with a model with ticker names as attributes, each corresponding to that ticker's hunk in the database. Using easy to build scopes, I want something like:
#stock = Stock.select_close_by_ticker('aapl','goog')
attributes are:
{aapl, goog, date}
where aapl and goog contain the closing price data for just that ticker. I can run multiple database queries if I need to, for now I just want to be able to sort my data into this form. Also, it must be completely dynamic, so I can't hardcode 'aapl', 'goog' and all the millions of other tickers into my model.
Would something like:
stocks = ['appl', 'goog']
Stock.find(:conditions => ['ticker in (?)'], stocks)
work for your scenario?

Resources