【问题标题】:Execute method on mongoid scope chain在 mongoid 作用域链上执行方法
【发布时间】:2013-04-22 15:13:26
【问题描述】:

我需要使用 Rails 和 MongoId 获取一些随机文档。由于我计划拥有非常大的集合,我决定在每个文档中放置一个“随机”字段并使用该字段选择文档。我在模型中写了如下方法:

def random(qty)
  if count <= qty
    all
  else
    collection = [ ]
    while collection.size < qty
      collection << where(:random_field.gt => rand).first
    end
    collection
  end
end

这个函数确实有效,并且集合中充满了 qty 个随机元素。但是当我尝试像这样的范围使用它时:

User.students.random(5)

我明白了:

undefined method `random' for #<Array:0x0000000bf78748>

如果我尝试使该方法类似于我得到的 lambda 范围:

undefined method `to_criteria' for #<Array:0x0000000df824f8>

鉴于我对在 random 之后应用任何其他范围不感兴趣,我如何在链中使用我的方法?

提前致谢。

【问题讨论】:

    标签: ruby-on-rails activerecord model mongoid


    【解决方案1】:

    我最终使用以下内容扩展了 Mongoid::Criteria 类。不知道是不是最好的选择。实际上我认为它很慢,因为它至少执行 qty 个查询。

    我不知道 not_in 是否可用于普通的 ActiveRecord 模块。但是,如果需要,您可以删除 not_in 部分。这只是减少查询数量的优化。

    对于文档数量比 qty 多一倍(或更多)的集合,您应该有准确的 qty 个查询。

    module Mongoid
      class Criteria
    
        def random(qty)
          if count <= qty
            all
          else
            res = [ ]
            ids = [ ]
            while res.size < qty
              el = where(:random_field.gt => rand).not_in(id: ids).first
    
              unless el.nil?
                res << el
                ids << el._id
              end
            end
            res
          end
        end
    
      end
    end
    

    希望你觉得这很有用:)

    【讨论】:

    • 有趣的解决方案。过去我使用pluck 提取所有id(相对较快),然后sample(n) 对id 进行随机抽样。然后Model.find(ids)拉出随机的n个模型。