Rails:如何使用ActiveRecord检索所有模型对象及其外部关系模型?

时间:2015-07-29 04:58:53

标签: ruby-on-rails ruby postgresql activerecord

我有一个名为Listing的模型:

class Listing < ActiveRecord::Base
  belongs_to :user
end

我想检索100个列表以及每个列表的用户作为列表哈希中的子哈希,所有这些都使用ActiveRecord进行单个SQL调用。这就是我现在直接使用PostgreSQL的方式:

def listings
  listings = ActiveRecord::Base.connection.execute("select row_to_json(t) as listing from (select *, row_to_json(users) as user from listings INNER JOIN users ON listings.user_id = users.id LIMIT 100) t")
  render json: {listings:listings,listing_count:Listing.count}
end

没有循环列表来检索他们的用户,这非常耗时。 谢谢!

修改

我按照建议尝试了这个,但它没有返回用户:

  2.2.1 :012 >  Listing.joins(:user).limit(1)
      Listing Load (0.6ms)  SELECT  "listings".* FROM "listings" INNER JOIN "users" ON "users"."id" = "listings"."user_id" LIMIT 1
     => #<ActiveRecord::Relation [#<Listing id: 1, user_id: 1, deleted: false, rent: "$950", deposit: "$950", availability: "6/18/2015", min_duration: "11", male_count: "0", female_count: "1", longitude: "-73.9767700960917", latitude: "40.75831025967895", location: "Midtown Center", comments: "Sequi acidus utor sublime cito autus suasoria. Ips...", photos: "1.jpg, 2.jpg, 3.jpg", cat: false, dog: false, doorman: true, large_room: true, apartment: true, house: false, garden: true, personal_bathroom: false, dog_friendly: true, cat_friendly: false, laundry: false, gym: true, elevator: true, renovated: true, furnished: false, smoking: true, smoke_free: false, air_conditioning: false, utilities_included: true, four_twenty_friendly: false, gay_friendly: false, vegan_friendly: false, vegetarian: true, kosher: false, girls_only: false, guys_only: true, created_at: "2015-07-17 20:09:21", updated_at: "2015-07-17 20:09:21">]> 

2 个答案:

答案 0 :(得分:1)

为什么单个SQL调用?我们避免N + 1加载是否足够?

时间复杂度几乎相同(O表示常数):

单个SQL调用:1 * O(n) 两个SQL调用:2 * O(n)

这里,O只是一个不同的常数。就可接受的运行时复杂性而言,因为它仍然是O(n)。

以此为前提:

听起来你想做的是预加载用户关联。 你可以这样做:

class Listing < ActiveRecord::Base
  belongs_to :user

  def self.to_hash_with_user
    relation = includes(:user)
    relation.map{ |record|
      hash = record.as_json
      hash['user'] = record.user.as_json
      hash
    }     
  end
end

现在你可以这样使用它了

# Get some relation, e.g. Listing.limit(100)
# Because of lazy loading, this will not do a db call yet
listings = Listing.limit(100)
render json: listings.to_hash_with_user.to_json

有关预加载记录的更多信息:http://blog.arkency.com/2013/12/rails4-preloading/

如果你想清理你返回的JSON,并且有更好的结构和灵活性,我强烈推荐这个宝石:https://github.com/rails-api/active_model_serializers

答案 1 :(得分:0)

左外连接应该这样做。它不会将其作为subhash返回,而是在响应中获得100个哈希值,每个哈希值包含所有用户和所有列表信息。

有关如何在rails 4中执行左外连接的信息 LEFT OUTER JOIN in Rails 4

所以在你的情况下

Listing.joins('LEFT OUTER JOIN users ON listing.user_id = users.id limit 100')

再次不是100%你正在寻找但它确实在一个查询中获得所有信息。