2010-10-08 15 views
22

Korzystanie Mongoid, powiedzmy, że mam następujących klas:Jak odwoływać się do osadzonego dokumentu w 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 

Jak widać, staram się modelować prosty świat gry środowisko, w którym mapa osadza lokalizacje, a gracz odwołuje pojedynczą lokalizację jako ich obecne miejsce.

Stosując to podejście, ja otrzymuję następujący błąd, gdy próbuję odwołać „Location” atrybut klasy zawodnik:

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

Rozumiem, że to dlatego, że jest osadzony dokument Lokalizacja making trudno jest odwoływać się poza zakresem jego dokumentu do osadzania (mapa). Ma to sens, ale jak mogę modelować bezpośrednie odniesienie do osadzonego dokumentu?

Odpowiedz

17

Ponieważ Mapy są ich własną kolekcją, należy powtórzyć każdy zbiór Map w poszukiwaniu lokalizacji, do której odnosi się Twój Odtwarzacz.

Nie można uzyskać bezpośredniego dostępu do dokumentów osadzonych. Musisz wejść przez kolekcję i odejść.

Aby uniknąć powtarzania wszystkich map, możesz przechowywać zarówno odwołanie do lokalizacji, jak i odwołanie do mapy w dokumencie odtwarzacza. Dzięki temu możesz łączyć kryteria, które wybierają Twoją Mapę, a następnie Lokalizację w niej. Musisz zakodować metodę na swojej klasie Player, aby sobie z tym poradzić.

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

Tak, podobny do tego, jak odpowiedziałeś sobie chyba można jeszcze zapisać location_id w dokumencie zamiast odtwarzacza za pomocą attribs COORD.

Innym sposobem jest umieszczenie Map, lokalizacji i odtwarzaczy w swoich zbiorach zamiast osadzania lokalizacji w kolekcji Map. Wtedy możesz użyć relacji referencyjnych, nie robiąc nic nadzwyczajnego ... jednak tak naprawdę po prostu używając hierarchicznej bazy danych, lubisz relacyjną bazę danych w tym momencie ...

+3

Uważam, że nie musisz przechowywać osobnego identyfikatora mapy: 'Maps.where ('location._id' => player.location_id)' - upewnij się tylko, że skonfigurowałeś odpowiednie indeksy (mapy indeksów według ' locations._id ') – colllin

+0

Och - nadal musisz ręcznie znaleźć osadzony obiekt lokalizacji, ale przynajmniej nie jesteś już zależny od tej lokalizacji należącej do konkretnej mapy. Nie jestem pewien, czy to jest problem. – colllin

0

Moje bieżące obejście jest wykonanie następujących czynności:

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 

To działa, ale czuje się jak Kluge.

11

PROSZĘ Idź i GŁOSUJ do funkcji "kolekcje wirtualne" w wydaniu MongoDB tracker:

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

to 2nd najbardziej pożądanych funkcji, ale jeszcze nie zostały zaplanowane do wydania. Może jeśli wystarczająco dużo osób zagłosuje na to i przeniesie go na numer jeden, zespół MongoDB w końcu go wdroży.

6

W moim przypadku użycia nie ma potrzeby, aby obiekt zewnętrzny odwoływał się do dokumentu osadzonego. Z grupy użytkowników mongoid, znalazłem rozwiązanie: Użyj referenced_in na osadzonym dokumencie i NO referencji na zewnętrznym dokumencie.

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

Jest to całkowicie wykonalne, ponieważ odwołujesz się do kolekcji najwyższego poziomu z osadzonej kolekcji. To, co OP chce zrobić, to "odwołać się do osadzonej kolekcji spoza jego kolekcji nadrzędnej", co jest zupełnie inną rzeczą. Na przykład, jeśli potrzebujesz dostępu do osadzonego elementu PageObject ze swojego Sprite ... musisz przejść przez kolekcję strony nadrzędnej, aby ją znaleźć. Lub, podobnie jak w mojej odpowiedzi, możesz przechowywać referencje Page i odniesienie do PageObject w twoim Sprite, więc nie musisz powtarzać całej kolekcji Page. –

0

Myślenie poza pole, można zrobić Skąd własny dokument i użyć Mongoid Alize do automatycznego generowania danych osadzonych w dokumencie mapy z dokumentami Lokalizacja.

https://github.com/dzello/mongoid_alize

Zaletą tej metody jest to, że można uzyskać wydajne zapytania, gdy warunki są odpowiednie, a wolniej referencyjna oparta zapytań na oryginalnym dokumencie, gdy nie ma innego wyjścia.