Mongoid 从 Mongo 聚合实例化模型并标记为现有记录

Mongoid instanciate models from Mongo aggregations and mark as existing record

由于 Mongoid API 没有使 MongoDB $sample 操作可见,我不得不手动 运行 使用 Mongo 驱动程序,我不知道如何处理结果。

我有不同的 classes/collections,它们遵循一些公共接口(出于多种原因我不想使用继承),并且我正在尝试将它们呈现为单个集合。我有一个从这三个 类

中抽取样本的代码
entries = [Class1, Class2, Class3].inject([]) do |array, clazz|
  entries << clazz.collection.aggregate([ { '$sample': { size: 10 } } ])    
end

这给了我一个包含三个不同 Mongo::Collection::View::Aggregation 的数组。我想以某种方式合并这些对象并能够实例化对象,以便我可以在我的视图中使用它们(例如使用单元格)

<%= cell(:profile, collection: entries) %>

使用entries.to_a将return一个散列数组而不是(模型)对象数组。我希望情况会如此,然后我会使用 cells builder 来处理模型之间的其他细微差别

builds do |model, options|
    case model
    when Class1; Class1Cell
    when Class2; Class2Cell
    when Class3; Class3Cell
  end

编辑:

我其实还能用to_a,用键_type找到对应的Constant/Model。现在的新问题是如何用散列实例化一个模型,它在 new_record?

上不 return true
sample = entries.to_a.first
  instance = Utility.resolve_class(sample[:_type]).new(entry_hash)
  # Problem is...
  instance.new_record? # => returns true, but since it comes from the DB it means it has already been persisted so it should return false.

Cells 适用于任何 PORO。因此,实现所需内容的最简单方法是创建一个 class 来表示模型文件中所需的数据。只需将其创建为普通 ruby class。您可以隐藏用于创建聚合并返回一组 classes 作为 class 方法的数据查询方法。

类似的东西(你会想要整理它,这只是一个让你开始的技巧):

# some PORO
class Record
   attr_accessor :field_1, :field_2, :field_3

   def self.build
       # your existing code
       entries = [Class1, Class2, Class3].inject([]) do |array, clazz|
        entries << profile_collection.collection.aggregate([ { '$sample': { size: 10 } } ])    
       end

       array_of_objects = []

       # now for each record within the aggregate create an object
       entries.each do |obj|
          new_poro = self.new
          obj.keys.each do |key|
             new_poro.self.instance_variable_set(key, obj[key])
          end
          array_of_objects.push new_poro
       end  
       return array_of_objects 
    end
end


# to run it in your controller
@records_in_objects_for_cells = Record.build

# in your views
<%= cell(:record, collection: records_in_objects_for_cells %>

要回答您编辑过的问题,您只需将其设置为 false。变量是 new_record,如此处所示 (http://www.rubydoc.info/github/mongoid/mongoid/Mongoid/Stateful:new_record%3F)。

所以:

r = MongoRecord.find_by(x:y)
e = r.new(e)
e.new_record?
=> true
e.new_record = false
e.new_record? 
=> false

MongoId 使用此标志来了解它是否持久化。如果发生持久性事件,它使用 _id 来了解要更新的记录。

最好的方法是使用 Mongoid::Document 的 class 方法 instantiate:

Person.instantiate(document)
# or even
Person.instantiate({firstname: 'John', lastname: 'Doe'})

或者你的例子:

entries = [Class1, Class2, Class3].inject([]) do |array, clazz|
  entries << clazz.collection.aggregate([
    { '$sample': { size: 10 } }
  ]).map do |document|
    clazz.instantiate(document)
  end    
end

如描述中所述:

Instantiate a new object, only when loaded from the database or when the attributes have already been typecast.

此外,它采用 selected_fields 作为第二个参数,这有助于让它知道只从数据库加载了给定的字段。