我有一个名为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_url
和to_url
将在两个重定向上匹配,因此两者都将是删除。我怎样才能使它只删除后者呢?我不想依赖任何last
方法,因为这可能无法保证。
答案 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)
但理想情况下,对于你的情况,这应该是在保存之前验证是否有任何记录形成无限循环。