我有一个模型Item
可以通过创建Vote
进行投票。 Vote
有一个属性:ip
,用于记录用户的IP地址。如果一个Item的Votes数组包含一个特定的ip,则该ip被阻止创建另一个投票。
模型:
class Thing < ActiveRecord::Base
has_many :votes
end
class Vote < ActiveRecord::Base
belongs_to :thing
end
items_controller:
def vote
@item = Item.find(params[:id])
if (!@item.votes.map(&:ip).include? request.remote_ip)
Vote.create!(ip: request.remote_ip, item_id: @item.id)
end
end
这个模型现在还不慢。但我担心当我开始获得数百万票时会发生什么。在一个例子中,我正在获取当前ip尚未投票的每个项目的数组。随着选票数量的增加,这将是一个疯狂的记录数量。有谁知道更好的方法吗?
我有一个想法如下:
1。)用用户的IP加载一系列投票:@votes = Vote.where(ip: request.remote_ip)
2。)创建与这些投票相关联的项目(@items
)的数组
3。)&#34;减去&#34;来自@items
的{{1}}来创建新数组Item.all
4.)检查@unvoted_items
是否包含当前项目
这会是一个更有效的模型吗?谁能想到更好的一个?
答案 0 :(得分:1)
你可以做得更简单:
@item.votes.where(ip: request.remote_ip).exists?
只要您的投票表上有[:item_id, :ip]
的索引,这就会很快。
此外,您应该考虑使索引成为唯一的(或者您将容易受到竞争条件的影响)。根据使用模式,您可以选择依赖唯一索引:
begin
@item.votes.create!(ip: request.remote_ip, item_id: @item.id)
rescue ActiveRecord::RecordNotUnique
# take whatever action is required upon a duplicate entry
end
如果大多数插入成功,那么这将节省几乎总是成功的检查。
答案 1 :(得分:1)
如果你有多个rails服务器,我认为你会提到“数百万”,这个机制将不起作用。更好的方法是使用唯一索引强制对数据库中的IP进行限制。启动会快得多。
答案 2 :(得分:1)
以下将在数据库中进行搜索,而无需全部检索;
if Vote.where(item_id: @item.id, ip: request.remote_ip).exists?
end
答案 3 :(得分:1)
所有当前答案都需要向控制器添加条件逻辑。 Rails具有验证唯一数据库记录的内置方法。只需在模型上添加唯一性验证:
validates_uniqueness_of :ip, message: 'has already voted'
现在在您的控制器中,您只需要处理两种可能的结果:有效或无效。这是一个例子:
def create
@vote = Vote.new(item_id: @item.id, ip: request.remote_ip)
if @vote.save
redirect_to @vote
else
render 'new'
end
end
如果有人试图投票两次,@vote.errors.messages
将如下所示:
{ ip: ['has already voted'] }
这些错误通常以表格形式显示。
有关ActiveRecord验证的更多信息,请参阅http://guides.rubyonrails.org/active_record_validations.html。