:include searches with the wrong value - mysql

I have a number of models in a Rails project that are linked to a user, and I'm running into a loading problem I try to get all of the user's data displayed on a page.
My model declarations look sort of like this:
class User < ActiveRecord::Base
validates_presence_of :server_user_id
has_many :setup_notes, :foreign_key => "server_user_id"
has_many :closure_votes, :foreign_key => "user_id"
end
class SetupNote < ActiveRecord::Base
belongs_to :user, :foreign_key => "server_user_id"
end
I should note that in the SQL table for closure votes, user_id is the same value as server_user_id in the table for users.
When I try using the :include symbol in a Rails query, it ends up using user.id for the search value when I need to find closure votes and setup notes through user.server_user_id. In other words,
me = User.first(:conditions => ["server_user_id = ?", 12610], :include => :setup_notes)
generates the MySQL query
SELECT `setup_notes`.* FROM `setup_notes` WHERE (`setup_notes`.server_user_id = 1)
Which returns an empty set. Is there any way to format the Rails query/models to send the relevant SQL query, or do I need to write a raw query for all of the models associated with the users (and if that's the case, how do I do that?)
Thanks!

If I understand correctly (that User has a server_user_id field), in the User model you have to set the :primary_key option like so:
class User < ActiveRecord::Base
validates_presence_of :server_user_id
has_many :setup_notes, :foreign_key => "server_user_id", :primary_key => "server_user_id"
has_many :closure_votes, :foreign_key => "user_id", :primary_key => "server_user_id"
end

Related

Reduce two SQL queries to one in activerecord maybe using joins

Two models User and Article have a relation as:
User has_one Article
Article belongs_to User
-User has a field called 'status'
-Article has a field called 'thing_i_need'
class User < ActiveRecord::Base
has_one :article, foreign_key: request_id
# status :integer
end
class Article < ActiveRecord::Base
belongs_to :user, foreign_key: request_id
# thing_i_need :string
end
Query: User.where(status: 'xyz').last.article.thing_i_need
Currently, two queries are fired to get the 'thing_i_need'
Any way to do it in one query ?
Does this help ?
You can try this
User.joins(:article).where(status: 'xyz').pluck('articles.thing_i_need').last
I'd recommend reading select & pluck query methods from the doc, earlier I was trying select in place of pluck and it didn't work.
Article.includes('user').where('users.status = ? and articals.request_id = users.request_id', 'xyz').first.thing_i_need
You may use user joins
Article.find(
:all,
:joins => :users,
:conditions => {
:users => {
:status => 'xyz'
}
}
).last.thing_i_need

Why isn't this second-level eager loading working?

I have three models, Subrating, Thing, and Category. Subrating belongs_to Thing, and Thing has_many Categories.
I have a list of Subratings, and I'm trying to eager-load the Categories that are associated with the Things that are associated with each Subrating.
This is what I tried:
controller
#subratings = Subrating.all( :include => { :thing => :categories })
view
<% #subratings.sort_by(&:rating).reverse.each do |subrating| %>
<%= subrating.thing.categories.sort_by(&:thing_count).second.name %>
<% end %>
But it doesn't solve my n+1 problem. I'm not even sure whether the database is lazy-loading Things or Categories or both, but this is the line that keeps reappearing hundreds of times in my server:
CACHE (0.0ms) SELECT COUNT(*) FROM "things" INNER JOIN "category_things" ON "things"."id" = "category_things"."thing_id" WHERE "category_things"."category_id" = $1 [["category_id", 1]]
What am I doing wrong?
associations
class Subrating < ActiveRecord::Base
belongs_to :thing
end
class Thing < ActiveRecord::Base
has_many :category_things
has_many :categories, :through => :category_things
has_many :subratings
end
class Category < ActiveRecord::Base
has_many :category_things
has_many :things, :through => :category_things
end
class CategoryThing < ActiveRecord::Base
belongs_to :category
belongs_to :thing
end
Maybe you should try to call includes on the Model class like:
#subratings = Subrating.includes(thing: [:categories])
as documented in http://apidock.com/rails/ActiveRecord/QueryMethods/includes
although it also should work the way you are trying it, since .all is an alias for .find(:all) but then i would propose to try:
#subratings = Subrating.all( :includes => { :thing => [:categories] })
(note that i changed include to includes and made thing point to an array)
#subratings = Subrating.includes(:thing => :categories)
This will also work for you and give you all records of substrings table.

ActiveRecord many_to_many with custom table and column name

I'm trying to map in Sinatra with ActiveRecord my MySQL dB.
The db doesn't follow the ActiveRecord convention, so I need to map everything adding attributes to my Classes.
Everything is ok with one to many and one to one association, but I have problems with m to m.
I have an article table named "Eve_Articles"
I have a product tablet named "Eve_product"
I have a join table named "Eve_Articles_Product" where tree_id is the article ID and prod_id is the Product ID.
I create the models:
class Article < ActiveRecord::Base
self.inheritance_column = :_type_disabled
has_one :image, class_name: 'Image', foreign_key: 'tree_id'
has_and_belongs_to_many :products,
end
class Product< ActiveRecord::Base
self.inheritance_column = :_type_disabled
has_and_belongs_to_many :articles,
end
But I don't know how to define the custom join table with custom keys.
Use join_table option. Also you will have to set the table_name for Article and Product since its not following the rails convention
You will have to set the table name after the habtm call according to this line from documentation.
WARNING: If you’re overwriting the table name of either class, the
table_name method MUST be declared underneath any
has_and_belongs_to_many declaration in order to work.
class Article < ActiveRecord::Base
self.inheritance_column = :_type_disabled
has_one :image, class_name: 'Image', foreign_key: 'tree_id'
has_and_belongs_to_many :products, :join_table => "Eve_Articles_Product", :association_foreign_key => 'tree_id', :foreign_key => 'prod_id'
self.table_name = "Eve_Products"
end
class Product< ActiveRecord::Base
self.inheritance_column = :_type_disabled
has_and_belongs_to_many :articles, :join_table => "Eve_Articles_Product", :association_foreign_key => "prod_id", :foreign_key => 'tree_id'
self.table_name = "Eve_Articles"
end

Reverse relationship in join table

I have a user friendship model that I want to write a lookup for
is a habtm relationship called peers, relating 2 users together. A relationship is a single connection (Joe <-> Steve, not Joe -> Steve and Steve -> Joe).
My join table is as follows:
user_id
peer_id
Both store a user id in the relationship. Below is the HABTM on the user.
has_and_belongs_to_many :peers, class_name: 'User',
foreign_key: 'user_id', association_foreign_key: 'peer_id',
join_table: 'users_peers'
I am trying to figure out the finder sql that will allow this record in the join table to show both sides.
user_id = steve.id
peer_id = joe.id
to show the relationships when I call joe.peers and steve.peers. Currently, steve.peers returns joe, but joe.peers shows nothing.
Generally relationships are best expressed as one way, or a pair of one-way relationships. This is because in most relational databases, it's easy to establish an A to B or B to A relationship, but not both with one record. You basically need two queries unless you make a lot of assumptions and hack around.
In my experience, using has_and_belongs_to_many isn't going to make your life easier as it's a relic from Rails 1.0 that isn't nearly as good as the has_many :through method that replaced it. In your case this is how that would play out:
class Node < ActiveRecord::Base
has_many :peers_of,
:class_name => 'Peer',
:foreign_key => :of_user_id
has_many :peers_to,
:class_name => 'Peer',
:foreign_key => :to_user_id
has_many :peers,
:through => :peers_of,
:source => :to_user
has_many :peers_with,
:through => :peers_to,
:source => :of_user
end
class Peer < ActiveRecord::Base
belongs_to :of_user,
:class_name => 'User'
belongs_to :to_user,
:class_name => 'User'
end
The semantics get a little messy, so you'll probably want to adjust them. The idea here is to establish a bi-directional relationship when adding a "peer", where that consists of a pair of A->B and B->A records.
For the purposes of querying you would only fetch, for instance, #user.peers and not have to worry about the peers_with inverse relationship as that should produce identical results if you've maintained data integrity.
You could just write the sql by hand:
class PeerRelation
belongs_to :user1, :class_name=>"User"
belongs_to :user2, :class_name=>"User"
end
class User
def set_peer(user)
user1_id, user2_id = [self.id, user.id].sort
PeerRelation.find_or_create_by_user1_id_and_user2_id(user1_id, user2_id)
end
def peers
User.joins("inner join peer_relations on
peer_relations.user1_id = #{self.id} or
peer_relations.user2_id = #{self.id}")
end
end
But tadman's approach is smarter from a data-integrity perspective, and is more in line with what a DBA would tell you. (see my comment to your question)

ActiveRecord - pre fetch single records from association

Consider the following models..
class Product < ActiveRecord::Base
has_many :pricings
end
class Pricing < ActiveRecord::Base
belongs_to :server
end
Pricing is a historical tracking table for prices of products, so there may potentially be hundreds of price points captured over time. What I want to add is a way to get only the current pricing for the product.
While i can add a has_one relation to the model like the following:
has_one :current_pricing, :class_name => "Pricing", :foreign_key => "product_id",
:order => 'created_at DESC'
This will fetch me all the Pricings for the current product before returning me only the first record.
I am looking for a way to accomplish this in a more efficient manner and was hoping that someone would have input or previous experience doing something similar.
You could use a named scope. Put this at the top of the Pricing model:
class Pricing < ActiveRecord::Base
named_scope :current, lambda { |product| { :conditions => { :product_id => product.id }, :order => 'created_at DESC', :limit => 1 } }
end
Then to get the current pricing of a product, you'd call "Pricing.current(product).first".
In addition to the named scope on pricing, you could add an accessor method to the Product model like so:
class Product < ActiveRecord::Base
def current_pricing
Pricing.current(self).first
end
end
You're looking for a named_scope.Define this in your Pricing model.Supply it with 1 parameter, the product_id.
named_scope :latest_price, lambda { |*args| {:conditions => {:product_id => args.first}, :order => "created_at DESC", :limit => 1} }
Excellent screencast on this by the way.
Add a :limit to the query?