在ActiveRecord 3中添加自定义SELECT

时间:2015-03-03 16:10:10

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

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

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子句而不是替换所有

2 个答案:

答案 0 :(得分:3)

除了默认的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 ...

答案 1 :(得分:0)

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

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

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

应用程序/模型/ 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