Rails find :include in a multi DB environment - ruby-on-rails

I am making a site in RoR and I am inside a multi DB environment. By that, I mean that some of my models are linked to MSSQL tables, and some others are linked to MYSQL tables.
It works well for the most part, but when I use the 'include' option in a find method, I get a very weird mix of SQL. Let me show you an example:
[SELECT * FROM "viewInfoClient" WHERE ("viewInfoClient".`NoClient` IN (6044196,5000652,0204392)) ]
MSSQL uses " between tables and columns name
MYSQL uses `
When I use the :include option in a MYSQL model, it will try and go read the corresponding results in the MSSQL model table. Since the NoClient link field comes from my MYSQL model, it gets mixed and MSSQL throws an error which is logical.
[unixODBC][FreeTDS][SQL Server]Incorrect syntax near '`'
Any idea how I can solve this problem?
CLIENT MODEL (MSSQL Database)
class Client < ActiveRecord::Base
establish_connection "mssql_#{RAILS_ENV}"
set_table_name "viewInfoClient"
set_primary_key "NoClient"
has_many :billets, :foreign_key => 'noclient', :primary_key => 'NoClient'
end
BILLET MODEL (MySQL Database)
class Billet < ActiveRecord::Base
belongs_to :client, :foreign_key => 'noclient'
end
AFFECTED STATEMENT
Could be anything using :include in it between the 2.
Example from the Billet model
def findall
find(:all, :include => 'client', :conditions => 'bin_id = 1')
end
Will return:
SELECT * FROM "viewInfoClient" WHERE ("viewInfoClient".`NoClient` IN (6044196,5000652,0204392))
Where (6044196,5000652,0204392) are the 3 records that have bin_id = 1 in the Billet model.
I remove everything else from my models to shorten the code, but this is it basically. I can reproduce it from any model that uses a MySQL - MSSQL link.

Related

Rails mongoid has_one queries

In User model there is has_one relation to Professional. In the professional model I have one Array field named industries.
I need to take all values where professional industries in "IT"
I tried User.where(:"professional.industries".in => ["IT"])
But Its not working. Any sugestions..??
In order for your query to work you should use
class User
embeds_one :professional
end
If you are sure that Professional should be a separate collection you could use something like:
uids = Professional.where(:"industries".in => ["IT"]).distinct(:user_id)
users = User.where(:_id.in => uids)

Rails :scope across databases

I have three models, Alarms, Sites, Networks. They are connected by belongs_to relationships, but they live in diferent databases
class Alarm < ActiveRecord::Base
establish_connection :remote
belongs_to :site, primary_key: :mac_address, foreign_key: :mac_address
end
class Site < ActiveRecord::Base
establish_connection :remote
belongs_to :network
end
class Network < ActiveRecord::Base
establish_connection :local
end
I wish to select all alarms belonging to a particular network. I can do this usng raw sql within a scope as follows:
scope :network_alarms, lambda { |net|
#need to ensure we are interrogating the right databases - network and site are local, alarm is remote
remote=Alarm.connection.current_database
local=Network.connection.current_database
Alarm.find_by_sql("SELECT network.* FROM #{local}.network INNER JOIN #{local}.site ON network.id = site.network_id INNER JOIN #{remote}.alarm on #{remote}.alarm.mac_address = site.mac_address WHERE site.network_id=#{net.id}")
}
and this works fine. However, this scope returns an array, so I can't chain it (for example, to use with_paginate's #page method). So
Is there a more intelligent way of doing this join. I have tried using join and where statements, for example (this is one of many variations I have tried):
scope :network_alarms, lambda { |net| joins(:site).where("alarm.mac_address = site.mac_address").where('site.network_id=?', net) }
but the problem seems to be that the rails #join is assuming that both tables are in the same database, without checking the connections that each table is using.
So the answer is simple when you know how...
ActiveRecord::Base has a table_name_prefix method which will add a specific prefix onto the table name whenever it is used in a query. If you redefine this method to add the database name onto the front of the table name, the SQL generated will be forced to use the correct database
so, in my original question we add the following method definition into tables Alarm, Site and Network (and anywhere else it was required)
def self.table_name_prefix
self.connection.current_database+'.'
end
we can then build the joins easily using scopes
scope :network_alarms, lambda { |net| joins(:site => :network).where('site.network_id=?', net) }
Thanks to srosenhammer for the original answer (here : Rails 3 - Multiple database with joins condition)
Steve

has_many :through - sql statement using NULL id

We recently upgraded our rails app from version 3.0.3 to 3.1.0. The application runs successfully for the most part as it did before with one major exception. We have a many-to-many relationship between two models, SurveyDatum and SubGroup, joined via a model called SubGroupSurveyDatum. Here is the code for each:
class SurveyDatum < ActiveRecord::Base
has_many :sub_group_survey_data
has_many :sub_groups, :through => :sub_group_survey_data
end
class SubGroup < ActiveRecord::Base
has_many :sub_group_survey_data
has_many :survey_data, :through => :sub_group_survey_data
end
And as you might expect:
class SubGroupSurveyDatum < ActiveRecord::Base
belongs_to :survey_datum
belongs_to :sub_group
end
If I have a SurveyDatum object that I retrieved previously from the database (lets call it 'sd'), and I invoke the sub_groups method (sd.sub_groups), this is the resulting sql query generated by active record:
SELECT `sub_groups`.* FROM `sub_groups` INNER JOIN `sub_group_survey_data` ON `sub_groups`.`id` = `sub_group_survey_data`.`sub_group_id` WHERE `sub_group_survey_data`.`survey_datum_id` IS NULL
The "IS NULL" part is obviously where the id of my survey data object is supposed to go, however active record fails to use it. The object does indeed have an id, since as mentioned it was persisted and retrieved from the database. This problem only cropped up after we moved to rails 3.1, so I assume there's something I've not done properly in accordance with the new version, but I have no idea. Any ideas? Thank you in advance for your help!
Hmm I used rails 3.1.0 and tried to replicate but all was well. The only case was when I manually set id = nil on the record retrieved from the db. Then I got:
SELECT "authors".* FROM "authors" INNER JOIN "relations" ON "authors"."id" = "relations"."author_id" WHERE "relations"."post_id" IS NULL
What database are you using? I was trying this with sqlite3. Also watch out for certain gems especially those that work with ActiveRecord. I had trouble with this in the past.
We discovered the issue. I had forgotten that the survey_data table has a composite primary key. When we upped to version 3.2.3, and added in the SurveyDatum model the following:
set_primary_key :id
The query finally built and executed properly.

Rails error: object does not support #inspect

Scratching my head here... If I try the following with rails:
User.limit(25).includes([:addresses])
I get:
User Load (109.5ms) EXEC sp_executesql N'SELECT TOP (25) [User].* FROM [User]'
Address Load (112.3ms) EXEC sp_executesql N'SELECT [Address].* FROM [Address] WHERE [Address].[UserId] IN (N''1'', N''2'', N''3'', N''6'', N''7'', N''8'', N''9'', N''11'', N''12'', N''16'', N''17'', N''18'', N''19'', N''20'', N''21'', N''22'', N''24'', N''25'', N''26'', N''27'', N''28'', N''29'', N''30'', N''31'', N''34'')'
(Object doesn't support #inspect)
=>
If I instead do
#users = User.limit(25).joins([:addresses])
this works fine, however an inner join is not what I want not to mention I'm actually trying to do this through sunspot_rails which only supports includes.
I'm not entirely sure what is up with this error. I suspect it might be due to the fact that this is a legacy MSSQL database so the naming conventions are nowhere near what rails likes. However I'm at a loss as to what the specific issue is.
The models are as follows, stripped down to the minimum which is verified to reproduce the problem:
class User < ActiveRecord::Base
set_table_name "User"
set_primary_key "ID"
has_many :addresses, :foreign_key => 'UserId'
end
class Address < ActiveRecord::Base
set_table_name "Address"
set_primary_key "ID"
belongs_to :user
end
That is it. There is other code on the user model for searching and authentication, but I've commented that all out and verified it has no impact on this problem. I'm stuck on this, any help is appreciated. The app is using rails 3.1, with activerecord-sqlserver-adapter and tiny_tds.
Solved...
:foreign_key => "UserID", not :foreign_key => "UserId"
For some reason, the case difference does not seem to bother rails when doing non eager loading, or with .joins, but does with .includes. Doh.

Creating AR model for Drupal database

how do I change my activerecord model default behavior for the find method?
For example, i want to search for all books inside drupal nodes database, but drupal uses only one table for all data, and uses the 'type' column to find out the type
class Book < ActiveRecord::Base
set_table_name 'node'
def find(*args)
:conditions => {:type => 'book'}
super
end
end
this is the correct approach to solve this problem?
I've solved this creating a model for Node and using named_scopes
the result was this
class Node
set_table_name 'node'
set_primary_key 'nid'
named_scope :book, :conditions => {:type => 'book'}
# if i don't overwrite this method, i would get an error when i try to use the type column
def self.inheritance_column
"rails_type"
end
end
it works, but doesn't look like the rails way of doing stuff. If I get enought time soon, I'll try to write a library to access drupal data using something like acts_as_drupal_node
now i can fetch all book entries using:
#books = Node.book.all

Resources