简体   繁体   English

在Rails 4中将find_by_sql语法转换为正确的Active Record AREL语法

[英]Converting find_by_sql syntax to proper Active Record AREL syntax in Rails 4

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. 给定一个具有三维坐标x,y和z的系统模型,我编写了以下实例方法,以为我提供所讨论特定系统的设定范围内的所有其他系统。 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? 是否有ActiveRecord的方法?

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). 由于您需要访问当前实例,因此需要保留实例方法,但是您可以通过将部件移到作用域中来对其进行分解(我想这就是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

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. “从系统中选择*”是隐式的,因此您只需要调用where子句。 I would leave such conditions with complex calculations as SQL fragment: 我将这些条件留给SQL片段进行复杂的计算:

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. 请注意,此处仅使用x而不是systems.x ,因为另一个:x只是参数,而不是自己的数据库对象。

Put that all in scopes, like Brad Pauly suggested, is also a good idea. 像Brad Pauly所建议的那样,将所有范围都放在这也是一个好主意。 By the way, calling your class "System" can be dangerous as ruby has a system method to make operation system calls. 顺便说一句,调用您的类“ System”可能很危险,因为ruby有一个进行操作系统调用的system方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM