2010-10-08 18 views
22

Usando Mongoid, vamos a decir que tengo las siguientes clases:¿Cómo hacer referencia a un documento incrustado en 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 

Como se puede ver, estoy tratando de modelar un entorno de mundo del juego simple donde un mapa incrusta lugares, y un jugador hace referencia a un solo lugar como su lugar actual.

uso de este enfoque, estoy recibiendo el siguiente error cuando intento hacer referencia a la "localización" atributo de la clase de jugador:

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

Mi opinión es que esto es debido a que el documento de ubicación está incrustado decisiones es difícil hacer referencia fuera del alcance de su documento de incrustación (el Mapa). Esto tiene sentido, pero ¿cómo puedo modelar una referencia directa a un documento incrustado?

Respuesta

17

Dado que los mapas son su propia colección, deberá iterar sobre cada colección de mapas que busque la ubicación a la que se hace referencia en su reproductor.

No puede acceder a los documentos incrustados directamente. Tienes que ingresar a través de la colección y trabajar hacia abajo.

Para evitar iterar todos los mapas, puede almacenar tanto la referencia de ubicación como la referencia de mapa en su documento de jugador. Esto le permite encadenar los criterios que seleccionan su Mapa y luego la Ubicación dentro de él. Debes codificar un método en tu clase de jugador para manejar esto.

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

Por lo tanto, similar a cómo usted contestó sí mismo, excepto que aún se podía almacenar la location_id en el documento jugador en lugar de utilizar los attribs coord.

Otra forma sería poner Mapas, Ubicaciones y Reproductores en sus propias colecciones en lugar de incrustar la Ubicación en su colección de Mapas. Entonces podría usar relaciones de referencia sin hacer nada sofisticado ... sin embargo, realmente utiliza una base de datos jerárquica como si fuera una base de datos relacional en este momento ...

+3

Creo que no necesita almacenar el ID del mapa por separado: 'Maps.where ('locations._id' => player.location_id)' - simplemente asegúrese de configurar los índices adecuados (index maps by ' locations._id ') – colllin

+0

Oh, aún necesitaría encontrar manualmente el objeto de ubicación incrustado, pero al menos ya no depende de esa ubicación que pertenece a un mapa en particular. No estoy seguro si eso es un problema. – colllin

0

Mi solución actual es hacer lo siguiente:

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 

Esto funciona, pero se siente como un Kluge.

11

VOTE por favor para la función de "colecciones virtuales" en el problema de MongoDB perseguidor:

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

es la segunda característica más solicitada, pero todavía no se ha programado para su liberación. Tal vez si suficientes personas lo votan y lo mueven al número uno, el equipo de MongoDB finalmente lo implementará.

6

En mi caso de uso, no hay necesidad de que el objeto externo haga referencia al documento incrustado. Del grupo de usuarios monógamos, encontré la solución: usar referenciado en el documento incrustado y SIN referencia en el documento externo.

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 
+3

Esto es perfectamente factible porque está haciendo referencia a una colección de nivel superior dentro de una colección incrustada. Lo que el OP quiere hacer es "hacer referencia a una colección incrustada desde fuera de su colección principal", que es algo completamente diferente. Por ejemplo, si necesita acceder a un PageObject incrustado de su Sprite ... tendrá que ir a la colección de páginas principales para encontrarlo. O como en mi respuesta, puedes almacenar tanto una página como una referencia de PageObject en tu Sprite para que no tengas que iterar sobre toda la colección de páginas. –

0

Pensar fuera de la caja, usted podría hacer su propia ubicación de documentos y utilizar Mongoid Alize para generar automáticamente los datos incorporados en el documento de mapa de sus documentos de ubicación.

https://github.com/dzello/mongoid_alize

La ventaja de este método es que se llega consultas eficientes cuando las condiciones son adecuadas, y la referencia más lenta consultas sobre el documento original basado cuando no hay otra manera.

Cuestiones relacionadas