【问题标题】:How to reference an embedded document in Mongoid?如何在 Mongoid 中引用嵌入的文档?
【发布时间】:2011-04-22 21:10:36
【问题描述】:

使用 Mongoid,假设我有以下类:

class Map
  include Mongoid::Document

  embeds_many :locations
end

class Location
  include Mongoid::Document

  field :x_coord, :type => Integer
  field :y_coord, :type => Integer

  embedded_in      :map, :inverse_of => :locations
end


class Player
  include Mongoid::Document

  references_one   :location
end

如您所见,我正在尝试建模一个简单的游戏世界环境,其中地图嵌入了位置,玩家将单个位置引用为他们当前的位置。

使用这种方法,当我尝试引用 Player 类的“位置”属性时出现以下错误:

Mongoid::Errors::DocumentNotFound: Document not found for class Location with id(s) xxxxxxxxxxxxxxxxxxx.

我的理解是,这是因为 Location 文档是嵌入的,因此很难在其嵌入文档(地图)的范围之外进行引用。这是有道理的,但是我如何对嵌入文档的直接引用进行建模呢?

【问题讨论】:

    标签: ruby mongodb mongoid


    【解决方案1】:

    因为地图是它们自己的集合,所以您需要遍历每个地图集合,在其中搜索您的播放器被引用的位置。

    您无法直接访问嵌入的文档。您必须通过收藏进入并继续往下走。

    为避免迭代所有地图,您可以在 Player 文档中存储位置参考和地图参考。这允许您链接选择您的地图的标准,然后选择其中的位置。你必须在你的 Player 类上编写一个方法来处理这个。

    def location
      self.map.locations.find(self.location_id)
    end
    

    因此,类似于您自己回答的方式,只是您仍然可以将 location_id 存储在播放器文档中,而不是使用坐标属性。

    另一种方法是将地图、位置和玩家放在他们自己的集合中,而不是将位置嵌入到您的地图集合中。然后你可以使用引用关系而不做任何花哨的事情......但是你真的只是使用分层数据库,就像此时它是一个关系数据库......

    【讨论】:

    • 我相信您不需要单独存储地图 ID:Maps.where('locations._id' => player.location_id) -- 只要确保您设置了正确的索引(按 'locations._id' 索引地图)
    • 哦 - 您仍然需要手动查找嵌入的位置对象,但至少您不再依赖于属于特定地图的那个位置。不确定这是否是个问题。
    【解决方案2】:

    请为 MongoDB 问题跟踪器上的“虚拟集合”功能投票:

    http://jira.mongodb.org/browse/SERVER-142

    这是第二个最受请求的功能,但仍未计划发布。也许如果有足够多的人投票支持它并将其排在第一位,那么 MongoDB 团队最终会实施它。

    【讨论】:

      【解决方案3】:

      在我的用例中,外部对象不需要引用嵌入的文档。从 mongoid 用户组中,我找到了解决方案:在嵌入式文档上使用 referenced_in,在外部文档上使用 NO reference。

      class Page
        include Mongoid::Document
        field :title
      
        embeds_many :page_objects
      end
      
      class PageObject
        include Mongoid::Document
        field :type
      
        embedded_in       :page,    :inverse_of => :page_objects
        referenced_in     :sprite
      end
      
      class Sprite
        include Mongoid::Document
        field :path, :default => "/images/something.png"
      end
      
      header_sprite = Sprite.create(:path => "/images/header.png")
      picture_sprte = Sprite.create(:path => "/images/picture.png")
      
      p = Page.create(:title => "Home")
      p.page_objects.create(:type => "header", :sprite => header_sprite)
      p.page_objects.first.sprite == header_sprite
      

      【讨论】:

      • 这是完全可行的,因为您从嵌入式集合中引用顶级集合。 OP 想要做的是“从其父集合之外引用嵌入式集合”,这是完全不同的事情。例如,如果您需要从 Sprite 访问嵌入的 PageObject……您必须通过父 Page 集合才能找到它。或者就像在我的回复中一样,您可以在 Sprite 中同时存储 Page 和 PageObject 引用,这样您就不必遍历整个 Page 集合。
      【解决方案4】:

      我目前的解决方法是执行以下操作:

      class Map
        include Mongoid::Document
      
        embeds_many     :locations
        references_many :players, :inverse_of => :map
      end
      
      class Player
        referenced_in :map
        field :x_coord
        field :y_coord
      
        def location=(loc)
          loc.map.users << self
          self.x_coord = loc.x_coord
          self.y_coord = loc.y_coord
          self.save!
        end
      
        def location
          self.map.locations.where(:x_coord => self.x_coord).and(:y_coord => self.y_coord).first
        end  
      end
      

      这行得通,但感觉像个笨蛋。

      【讨论】:

        【解决方案5】:

        跳出框框思考,您可以将 Location 设为自己的文档,并使用 Mongoid Alize 从您的 Location 文档中自动生成地图文档中的嵌入数据。

        https://github.com/dzello/mongoid_alize

        这种方法的优点是,当条件合适时,您可以获得高效的查询,而当没有其他方法时,您可以对原始文档进行较慢的基于引用的查询。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-08-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-04-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多