As of Rails 6.0 you cannot use a non-column reference in an order
statement without passing an Arel
object.
In your case options include:
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)
))
We can even convert this part to arel but it wouldn't be pretty
# 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')
For others that stumble across this post:
Please know the Arel#sql
will not perform escaping.
If third_party_location_query
needs to be escaped, because it comes from a third party and could be dangerous, using other techniques can and should be used to sanitize this data:
For instance if the parens are not needed then:
Arel::Nodes::InfixOperation.new("<->",
Locations.arel_table[:geom],
Arel::Nodes.build_quoted(third_party_location_query))
should work.
If the parens are needed and the argument is singular or arguments are comma separated. Then
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)
There are many other ways to handle the needed escaping depending on implementation.