【问题标题】:Adding custom SELECT in ActiveRecord 3在 ActiveRecord 3 中添加自定义 SELECT
【发布时间】:2015-05-04 09:07:31
【问题描述】:

我正在尝试使用自定义查询的结果(两个地理点之间的距离)作为模型属性。最终目标是能够使用该列过滤和排序行,例如:

Model.with_distance(point).order(:distance).first

我最初以为我可以在范围内使用select;这是一个没有消毒的较短版本:

scope :with_distance, ->(point) { select("ST_DISTANCE(models.point_in_model, ST_GeographyFromText('#{point.to_s}')) AS distance") }

但是,该范围仅选择 distance 列,而不检索模型属性:

> Model.with_distance(point)
[#<Model >, #<Model >,...]

models.* 添加到select 调用可以解决问题,但代价是强制检索每个查询中的整行并忽略其他select 调用。

如何添加一个 SELECT 子句而不是替换所有子句

【问题讨论】:

    标签: ruby-on-rails postgresql activerecord ruby-on-rails-3.2 postgis


    【解决方案1】:

    除了默认的SELECT models.*,ActiveRecord 通常添加 SELECT 子句,而不是替换它们。

    > Project.select(:id).select(:name)
    => Project Load (0.5ms)  SELECT id, name FROM "projects"
    

    显然,当您想要“默认 SELECT 以及我要求的任何其他选项”时,这无济于事。

    您可以尝试select_star 范围

    scope :select_star, -> { select("models.*") }
    scope :select_star, -> { select(self.arel_table[Arel.star]) } # Arel version, if feeling adventurous
    

    您可以在需要所有列的地方使用哪个?

    > Model.with_distance(point).select_star
    => SELECT ST_DISTANCE(models.point_in_model, ST_GeographyFromText('0,0')) AS distance, models.* FROM ...
    

    【讨论】:

    • 我从未使用过它,但 Arel 听起来很有趣。然而,这迫使开发人员在大多数查询的末尾添加 .select_star ,而我希望它与 Rails 相同...使用默认的“全选”方法对查询进行原型设计更容易,然后通过添加 SELECT 进行优化在需要时调用。
    【解决方案2】:

    我对@9​​87654321@ 的行为有同样的问题。让我们总结一下这些不好的地方:

    • 默认情况下,当查询正在构建并且我们之前从未运行过select 时,Arel.star 用于获取所有列。
    • 有时我们不希望 ActiveRecord/ARel 添加“*”,例如。我们正在构建一些中间查询或统计查询,而该星号只是增加了我们数据库的工作量(并且还可能影响在属性等存在时运行的 ruby​​ 回调)。

    我认为一种解决方案(在 Rails 4.2.4 上测试,也可以轻松应用于 Rails 3)是将以下代码添加到您的模型或 ActiveRecord::Base:

    app/models/item.rb

    ...
    # include "items".* by default
    default_scope ->{select(arel_table[Arel.star])}
    # when we dont want to use all columns
    scope :no_columns, ->{except(:select)}
    ...
    

    现在,例如,我们在 app/models/item.rb 中有以下范围

    ...
    scope :some_business_logic_scope, ->{
        # doing some big query
        joins(...).
        where(...).
        select(...) # some virtual attributes
        # we dont need to run the following line any more
        # select(self.arel_table[Arel.star])
    }
    ...
    

    用法

    Item.select(:id) # selects all columns
    Item.no_columns.select(:id) # selects only column id
    Item.no_columns.some_business_logic_scope # selects only required columns
    Item.some_business_logic_scope # selects all attributes including those added by some_business_logic_scope
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-13
      • 1970-01-01
      • 1970-01-01
      • 2018-11-10
      • 1970-01-01
      • 2023-03-08
      • 1970-01-01
      相关资源
      最近更新 更多