【问题标题】:Rails: How to retrieve all model objects along with its foreign relation model with ActiveRecord?Rails:如何使用 ActiveRecord 检索所有模型对象及其外部关系模型?
【发布时间】:2015-10-19 21:45:04
【问题描述】:

我有一个名为 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">]> 

【问题讨论】:

  • 试试这个Listing.joins(:user).limit(100)
  • 您能否发布您预期输出的示例哈希
  • .joins 不起作用。这只会将其范围限定为仅包含用户的列表,而不包括用户

标签: ruby-on-rails ruby postgresql activerecord


【解决方案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,并拥有更好的结构和灵活性,我强烈推荐这个 gem:https://github.com/rails-api/active_model_serializers

【讨论】:

  • 我看到直接 SQL 调用 postgres 的时间性能提高了 4 倍,它还使用 row_to_json() 在内部呈现 json。想知道是否有办法通过 Rails/ActiveRecord 实现这一点,不确定序列化程序是否会这样做?
  • 您得到的实际数字是多少?只有在不低于其他减速因素的情况下,db 调用的性能才重要。如果你从 0.6ms 到 2.4ms,后者是完全可以接受的,除非你正在为 S2S 调用构建高速回复。如果是面向用户的网站,这种变化是微不足道的。然而,这是相当大的性能提升。序列化程序 gem 对此无济于事。纯粹是为了加快新的发展。精心设计的单个 SQL 调用通常总是比 Rails 快
【解决方案2】:

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

关于如何在 rails 4 中进行左外连接的信息在这里 LEFT OUTER JOIN in Rails 4

所以你的情况

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

同样不是 100% 您要查找的内容,但它确实在一个查询中获取了所有信息。

【讨论】:

    猜你喜欢
    • 2019-11-20
    • 2015-01-06
    • 1970-01-01
    • 1970-01-01
    • 2013-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-30
    相关资源
    最近更新 更多