在Rails中的不同列中查找具有相同值的记录

时间:2016-12-28 16:48:26

标签: ruby-on-rails redirect

我有一个名为redirects的表,其中包含from_url列和to_url列。

它们用于将用户从一个网址重定向到另一个网址。

例如,我可能有:

id: 1
from_url: /about-us
to_url: /about

但是,如果以相反的方式创建相同的重定向,我希望防止无限循环(因为这些是在用户更改CMS中页面的URL时生成的,因此可以将其更改回原始值)。

例如:

id: 1
from_url: /about-us
to_url: /about

id: 2
from_url: /about
to_url: /about-us

在这种情况下,我想要删除第一条记录,因为我们已经用新的重定向取代了它以捕获更改,而原始重定向现在又是正确的网址。

在我的模型中,我有一个名为test_and_clean

的方法
def self.test_and_clean
  redirects = Redirect.all
  conflicts = []
  redirects.each do |redirect|
    redirects.each do |redirect2|
      # if from_url has a matching to_url (causing a loop)
      if redirect.from_url == redirect2.to_url
        conflicts.push(redirect2)
      end
    end
  end
  # destroy all the conflicts
  conflicts.each do |conflict|
    conflict.destroy
  end
end

除了将循环嵌套相当讨厌之外,这种方法存在问题,初始循环将找到两个重定向,因为from_urlto_url将在两个重定向上匹配,因此两者都将是删除。我怎样才能使它只删除后者呢?我不想依赖任何last方法,因为这可能无法保证。

2 个答案:

答案 0 :(得分:1)

以下实现不使用嵌套循环,但使用嵌套数据库查询来查找冲突(我希望您在后台作业中执行此操作)。

def self.test_and_clean
  conflicts = []

  # Find all the conflicts
  Redirect.find_each do |redirect|
    # Check if the current redirect was already detected as conflict
    unless conflicts.include?(redirect)
      conflict = Redirect.find_by(from_url: redirect.to_url, to_url: redirect.from_url)
      conflicts.push(conflict) unless conflict.nil?
    end
  end

  # destroy all the conflicts
  conflicts.each do |conflict|
    conflict.destroy
  end
end

答案 1 :(得分:0)

您可以使用以下条件在内部联接重定向表:

SELECT r1.id, r2.id FROM redirects r1
INNER JOIN redirects r2 ON r1.from_url = r2.to_url 
AND r1.to_url = r2.from_url 
AND r1.id != r2.id

Arel形式如下:

Redirects.joins("inner join #{Redirect.table_name} as r2 ON 
#{Redirect.table_name}.from_url = r2.to_url 
AND #{Redirect.table_name}.to_url = r2.from_url 
AND #{Redirect.table_name}.id != r2.id").pluck('redirects.id', 'r2.id')

这应该返回一对id的数组。从每个收集最大的id并删除那些行

Redirect.delete((pairs.collect {|pair| pair.sort.last}).uniq)

但理想情况下,对于你的情况,这应该是在保存之前验证是否有任何记录形成无限循环。