ActiveRecord的find_each方法和Postgres

4
我收到了以下错误信息:
PGError: ERROR:  operator does not exist: character varying >= integer
LINE 1: ...CT  "games".* FROM "games"  WHERE ("games"."uuid" >= 0) ORDE...
                                                             ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
: SELECT  "games".* FROM "games"  WHERE ("games"."uuid" >= 0) ORDER BY "games"."uuid" ASC LIMIT 1000

当我尝试这样做时:

Game.find_each do |game|
  # ...
end

我有一个字符串(UUID)作为我的模型的主键:

class Game < ActiveRecord::Base
  self.primary_key = 'uuid'

  before_create do |game|
    game.uuid = UUIDTools::UUID.timestamp_create().to_s if game.uuid.blank?
  end
end

我不知道为什么ActiveRecord要放入那个WHERE子句,但它完全是不必要的,并且导致了类型错误(因为它是一个字符串列,而不是整数列)。
那么,我应该如何避免这种情况呢?是否有什么我应该在我的模型定义中放置的内容?还是我应该避免使用find_each并使用其他方法?这是一个Rake任务,只是遍历所有条目并查找一些附加信息...
3个回答

4

0

我觉得没有任何参数的 find_each 方法会返回一个 id >= 0 的 find_by_id。尽管 ActiveRecord 使用了正确的列,但在你的情况下,它似乎不知道该列是 varchar 类型,而不是整数类型。

你可以尝试使用另一种 find 方法,或者尝试给 find_each 添加一些条件。

这对于使用字符串作为主键的问题可能是相关的: http://railsforum.com/viewtopic.php?id=11110

祝好


0

这个博客帖子有你的 bug 修复:

在 lib/clean_find_in_batches.rb 中

module CleanFindInBatches

  def self.included(base)
    base.class_eval do
      alias :old_find_in_batches :find_in_batches
      alias :find_in_batches :replacement_find_in_batches
    end
  end

  # Override due to implementation of regular find_in_batches
  # conflicting using UUIDs
  def replacement_find_in_batches(options = {}, &block)
    relation = self
    return old_find_in_batches(options, &block) if relation.primary_key.is_a?(Arel::Attributes::Integer)
    # Throw errors like the real thing
    if (finder_options = options.except(:batch_size)).present?
      raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order].present?
      raise "You can't specify a limit, it's forced to be the batch_size" if options[:limit].present?
      raise 'You can\'t specify start, it\'s forced to be 0 because the ID is a string' if options.delete(:start)
      relation = apply_finder_options(finder_options)
    end
    # Compute the batch size
    batch_size = options.delete(:batch_size) || 1000
    offset = 0
    # Get the relation and keep going over it until there's nothing left
    relation = relation.except(:order).order(batch_order).limit(batch_size)
    while (results = relation.offset(offset).limit(batch_size).all).any?
      block.call results
      offset += batch_size
    end
    nil
  end

end

还有在 config/initializers/clean_find_in_batches.rb 文件中

ActiveRecord::Batches.send(:include, CleanFindInBatches)

这个版本在处理大型数据集时比原始版本慢得多,因为它使用了OFFSET。您还可能会得到不一致的结果。更多细节请参见:https://www.citusdata.com/blog/2016/03/30/five-ways-to-paginate/ - Marcin Bilski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接