0

Given a System model with three-dimensional coordinates x, y, and z, I have written the following instance method to give me all of the other Systems within a set range of the specific System in question. Here's the code:

def systems_within(range = '15')
  System.find_by_sql(["SELECT * FROM systems WHERE Sqrt(Pow((:x - systems.x), 2) + Pow((:y - systems.y), 2) + Pow((:z - systems.z), 2)) <= :range AND id!= :current_id", {x: x, y: y, z: z, range: range, current_id: id}])
end

Is there an ActiveRecord way of doing this?

GreenPlastik
  • 134
  • 7

2 Answers2

2

Since you need access to the current instance you'll need to keep the instance method, but you could break it up a little bit by moving parts into scopes (I'm guessing that's what you meant by ActiveRecord).

Something like this might work. This is untested code.

scope :within_range, -> (x, y, z, range = '15') {
  where("Sqrt(Pow((:x - systems.x), 2) + Pow((:y - systems.y), 2) + Pow((:z - systems.z), 2)) <= :range", {x: x, y: y, z: z, range: range})
}
scope :excluding, -> (excluded_id) { where("id <> ?", excluded_id) }

def systems_within(range = 15)
  System.excluding(id).within_range(x, y, z, range)
end
Brad Pauly
  • 336
  • 3
  • 4
1

You can let the database do less calculations by comparing against the square of the range:

square_range = range**2

The "select * from systems" is implicit, so you need only to call the where clause. I would leave such conditions with complex calculations as SQL fragment:

System.where(
   'Pow((:x - x), 2) + Pow((:y - y), 2) + Pow((:z - z), 2)) <= :square_range', 
    {x: x, y: y, z: z, square_range: square_range}
).where.not(id: current_id)

Note that you just use x instead of systems.x here, because the other :x is just parameter and not an own database object.

Put that all in scopes, like Brad Pauly suggested, is also a good idea. By the way, calling your class "System" can be dangerous as ruby has a system method to make operation system calls.

Meier
  • 3,858
  • 1
  • 17
  • 46
  • Thanks for the response. I combined this with Brad's answer about putting it in scopes. Makes it easier to use portions of it in other instances – GreenPlastik Oct 16 '15 at 23:23