2017-01-24 23 views
1

我有一个模型叫Update其中包含列:version这是一个字符串。Rails:使用ActiveRecord与自定义比较器

class Update < ApplicationRecord 
    validates_presence_of :version 
end 

version列包含字符串值如1.0.1。使用Gem::Version,你可以比较的版本号是这样的:

Gem::Version.new('2.1') > Gem::Version.new('2.0.1') 

我想做一个查询在那里我可以version得到所有的记录超过一定的数量。有没有办法将自定义方法传递给.where以便我可以使用版本比较器?如:

class Update < ApplicationRecord 
    def version_is_greater_than(that_version) 
    Gem::Version.new(self.version) > Gem::Version.new(that_version) 
    end 
end 

...

Update.where(&:version_is_greater_than) 

正如你可以看到,上述代码不能正常工作,因为我需要一个版本字符串传递到我似乎无法做到这一点的方法通过&:。有没有更好的方法来做到这一点,以便我可以比较.where调用中的值?我试图远离分裂大/小/等。数字,因为我只是在那里重新发明轮子。

+0

我正在使用Postgres。简单的'1.2.3'格式是可以接受的。这与我的任何宝石版本无关 - 但我认为比较器至少足够满​​足我的需求。 –

+0

我觉得这很容易做到'Update.all.select {| u | Gem :: Version.new(u.version)> Gem :: Version.new(“2.0”)}'但我只是想知道是否有一种方法将它与'.where'结合起来,以根据其他属性也是如此。 –

回答

1

您不能在数据库中使用Ruby方法,并且在数据库中执行where中的所有内容。但是,您可以在数据库内部实现简单的major.minor.patch版本号比较。

PostgreSQL支持阵列列和阵列由元件比较元件所以像:如预期的数据库内

array[1,2,3] < array[1,5,0] 

工作(即与Ruby中的[1,2,3] < [1,5,0]相同)。


一种方法是将你的字符串列转换为整数数组,发送版本号到数据库中的数组,并让数据库做比较。迁移如:

connection.execute(%q{ 
    alter table updates 
    alter column version 
    set type int[] using regexp_split_to_array(version, '\\.')::int[] 
}) 

应照顾数据库。然后,你可以说:

def version_is_greater_than(that_version) 
    self.version > that_version.split('.').map(&:to_i) 
end 

或查询数据库:

def self.version_is_greater_than(that_version) 
    where('version > array[?]', that_version.split('.').map(&:to_i)) 
end 

你需要更新的读取和写入version s到说明新类型的version列,以及代码。


或者,你可以离开version在数据库中的字符串,并使用regexp_split_to_arraywhere电话时你需要比较的东西:

where(%q{ 
    regexp_split_to_array(version, '\\.')::int[] > array[?] 
}, that_version.split('.').map(&:to_i)) 

有PL /红宝石,允许你在Ruby中编写存储过程,但是它已经七年没有更新了,并且假设Ruby 1.8.7是最新的。我们可以假装它不存在。

0

不,你不能这样使用它。

&运算符将一个方法转换为一个proc,并在您调用一个接受一个块的方法(如那些可枚举的方法)时将它传递给参数。查看此线程获取更多信息。

what is the functionality of "&: " operator in ruby?

where方法不带块。当您致电where(...)时,您基本上称为WhereChain.new(...)。 Refeter这个位置:

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/query_methods.rb

什么你正在尝试做的可以通过包装方法进入SQL对象,或者只是一个返回SQL字符串的方法来实现。但不幸的是,我不认为sql(特别是mysql)对字符串(或varchar)有比较操作,如>,<。所以你不能在sql中本地执行它,这意味着,你将无法生成一个sql字符串并将它传递给where子句来实现此目的。请参阅SQL Server文档在这里:

https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_greater-than

由于使用的情况下,也有版本特定的宝石更新的数量非常有限,所以恕我直言,这是安全快速刚刚获得所有记录,并在操作红宝石级别。