Rails ActiveRecord::Relation 不断命中数据库

Dar*_*isa 1 ruby ruby-on-rails

我有一个简单的控制器方法:

products = Product.where(name: name, color: color, size: size, available: true)
Run Code Online (Sandbox Code Playgroud)

它返回一个 Product::ActiveRecord_Relation 对象。我想取出第一个物体并拉出一个字段,例如products.first.product_code

但是这个和我尝试过的其他一些方法重新查询数据库。我试过了:

products[0].product_code
products.take.product_code
Run Code Online (Sandbox Code Playgroud)

所有这些都重新查询数据库,两次命中数据库。一次为哪里和一个上场。有没有一个不会影响数据库的简单解决方案?

有效的是将 ActiveRecord 转换为数组 ( products.to_a.[0].product_code) 但这似乎效率低下。

以下是显示两个单独命中的服务器日志:

在此处输入图片说明

这是我的控制器方法供参考:

  def update_selection
    size = params[:size]
    color = params[:color]
    name = params[:product_name]
    products = Product.where(name: name, color: color, size: size, available: true)
    product_code = products.empty? ? 'sold out' : products.first.product_code
    respond_to do |format|
      format.json { render json: { count: products.length, code: product_code}}
    end
  end
Run Code Online (Sandbox Code Playgroud)

Mit*_*tch 5

我最好的猜测是,在分配给products. ActiveRecord 关系是惰性的,这意味着它们直到需要该查询的结果时才查询数据库。

就您的问题而言 - 这一行:

products = Product.where(name: name, color: color, size: size, available: true)

不需要从数据库中实际提取任何数据,因此不执行查询。

products.first.product_code,另一方面,将导致查询触发。

编辑:

我相信问题来自使用.exists?(这是一个总是触发查询的 ActiveRecord 方法)。如果您要检查查询是否返回任何结果,请尝试使用.present?.any?代替.exists?

编辑2:

好的,感谢发布代码。你有几个选择。

  1. 使用.to_a

products = Product.where(name: name, color: color, size: size, available: true).to_a

这会将所有内容加载products到内存中,如果您希望有少量产品,这很有意义。这只会触发一个查询。

  1. 您可以将此行更改为仅触发一个查询:

product_code = products.first.try(:product_code) || 'sold out'

这更节省内存(因为您最多将一个加载product到内存中),但使用两个查询 ( products.count is the other one)。