来自Mongo聚合的Mongoid instanciate模型并标记为现有记录

时间:2016-04-17 15:56:48

标签: mongodb mongoid rails-cells

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

我有不同的类/集合,它们遵循一些通用接口(我出于几个原因不想使用继承),我试图将它们作为单个集合进行渲染。我有一个从这三个类中抽样的代码

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将返回哈希数组,而不是(模型)对象数组。我希望情况会是这样,然后我会使用单元构建器来处理模型之间的其他细微差别

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

编辑:

我实际上仍然可以使用to_a并使用键_type来查找相应的常量/模型。现在的问题是,如何使用哈希来实现模型,而不会在true上返回new_record?

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.

3 个答案:

答案 0 :(得分:1)

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

类似的东西(你会想要整理一下,这只是一个让你入门的黑客):

# 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 %>

答案 1 :(得分:1)

要回答您编辑的问题,您可以将其设置为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来知道要更新的记录。

答案 2 :(得分:1)

最好的方法是使用Mongoid::Document的类方法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

如说明中所述:

  

仅当从数据库中加载或属性已经过类型转换时,才能实例化新对象。

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