ruby-on-rails 活动记录::未知属性引用:使用非属性参数调用查询方法

z9gpfhce  于 2023-02-26  发布在  Ruby
关注(0)|答案(1)|浏览(112)

我尝试在Rails应用程序中使用空间距离,但总是遇到"ActiveRecord::UnknownAttributeReference:使用非属性参数调用查询方法"错误。
下面是我的代码:

def nearby_locations
  third_party_location_query = ThirdPartyLocation.where(id: id).select('geom').to_sql

  third_party.organisation.locations.active.
    select("locations.*, ROUND(ST_DistanceSphere(locations.geom, (#{third_party_location_query}))::numeric, 0)::integer distance").
    order("locations.geom <-> (#{third_party_location_query})").
    limit(10).as_json(methods: [:distance])
end

我知道这个错误是由于向order方法传递了一个非属性值而引起的,但是我不确定在这种情况下如何避免它。我怎样才能在查询中使用空间距离而不遇到这个错误呢?

lsmd5eda

lsmd5eda1#

从Rails 6.0开始,如果不传递Arel对象,就不能在order语句中使用非列引用。
在您的情况下,选项包括:

order(Arel.sql("locations.geom <-> (#{third_party_location_query})"))
# Or 
 third_party_location_query = ThirdPartyLocation.select(:geom)
    .arel.where(ThirdPartyLocation.arel_table[:id].eq(id))
 order(
  Arel::Nodes::InfixOperation.new("<->", # operator
    Locations.arel_table[:geom], # left side
    third_party_location_query)  # right side (will add parens as a subquery)  
  ))

我们甚至可以将这部分转换为arel,但它不会很漂亮

# ROUND(ST_DistanceSphere(locations.geom, (#{third_party_location_query}))::numeric, 0)::integer distance
function = Arel::Nodes::NamedFunction
operation = Arel::Nodes::InfixOperation
operation.new('::',  
  function.new('ROUND',
    [operation.new('::',
      function.new('ST_DistanceSphere',[
        Location.arel_table[:geom],
        ThirdPartyLocation.select(:geom).arel.where(ThirdPartyLocation.arel_table[:id].eq(id))
      ]),
      Arel.sql('numeric')),
    0]),
  Arel.sql('integer')).as('distance')

对于其他偶然发现这篇文章的人:
请注意Arel#sql不会执行转义。
如果由于third_party_location_query来自第三方并且可能存在危险而需要对其进行转义,则可以并且应该使用其他技术来净化此数据:
例如,如果不需要括号,则:

Arel::Nodes::InfixOperation.new("<->",
    Locations.arel_table[:geom],
    Arel::Nodes.build_quoted(third_party_location_query))

应该行得通。
如果需要括号,并且参数是单数或参数是逗号分隔的,则

third_party_location_query = "hello" 
Arel::Nodes::Grouping.new([Arel::Nodes.build_quoted(third_party_location_query)]).to_sql
#=> (N'hello')
# Or 
third_party_location_query = [1,2,3]
Arel::Nodes::Grouping.new(third_party_location_query ).to_sql
#=> (1,2,3)

根据实现的不同,还有许多其他方法可以处理所需的转义。

相关问题