2013-04-24 18 views
51

Mam dwie kolekcje. Pierwszy zbiór zawiera studentów:MongoDB: Jak sprawdzić, czy pole tablicy zawiera element?

{ "_id" : ObjectId("51780f796ec4051a536015cf"), "name" : "John" } 
{ "_id" : ObjectId("51780f796ec4051a536015d0"), "name" : "Sam" } 
{ "_id" : ObjectId("51780f796ec4051a536015d1"), "name" : "Chris" } 
{ "_id" : ObjectId("51780f796ec4051a536015d2"), "name" : "Joe" } 

Drugi zbiór zawiera kursy:

{ 
     "_id" : ObjectId("51780fb5c9c41825e3e21fc4"), 
     "name" : "CS 101", 
     "students" : [ 
       ObjectId("51780f796ec4051a536015cf"), 
       ObjectId("51780f796ec4051a536015d0"), 
       ObjectId("51780f796ec4051a536015d2") 
     ] 
} 
{ 
     "_id" : ObjectId("51780fb5c9c41825e3e21fc5"), 
     "name" : "Literature", 
     "students" : [ 
       ObjectId("51780f796ec4051a536015d0"), 
       ObjectId("51780f796ec4051a536015d0"), 
       ObjectId("51780f796ec4051a536015d2") 
     ] 
} 
{ 
     "_id" : ObjectId("51780fb5c9c41825e3e21fc6"), 
     "name" : "Physics", 
     "students" : [ 
       ObjectId("51780f796ec4051a536015cf"), 
       ObjectId("51780f796ec4051a536015d0") 
     ] 
} 

Każdy dokument Kurs zawiera students tablicę, która ma listę studentów zarejestrowanych na kurs. Kiedy uczeń przegląda kurs na stronie internetowej, musi sprawdzić, czy już zapisał się na kurs, czy nie. W tym celu, gdy kolekcja courses zostanie przeszukiwana w imieniu ucznia, musimy sprawdzić, czy tablica students zawiera już ObjectId ucznia. Czy istnieje sposób, aby określić w projekcji zapytania wyszukiwania, aby pobrać studenta ObjectId z tablicy students tylko wtedy, gdy istnieje?

Próbowałem sprawdzić, czy mogę mieć operatora $ elemMatch, ale jest on nastawiony na szereg pod-dokumentów. Rozumiem, że mogłem użyć struktury agregacji, ale wydaje się, że w tym przypadku będzie to przesadą. Struktura agregacji prawdopodobnie nie byłaby tak szybka jak pojedyncze zapytanie wyszukiwania. Czy istnieje sposób na zapytanie o zbieranie kursów, aby zwracany dokument mógł mieć formę podobną do tej?

{ 
     "_id" : ObjectId("51780fb5c9c41825e3e21fc4"), 
     "name" : "CS 101", 
     "students" : [ 
       ObjectId("51780f796ec4051a536015d0"), 
     ] 
} 

Odpowiedz

50

[ edit w oparciu o to teraz jest to możliwe w najnowszych wersjach]

[Aktualizacja Odpowiedź] można zapytać następujący sposób odzyskać nazwę klasy i legitymacji studenckiej tylko wtedy, gdy są one ALRE ady się zapisał.

db.student.find({}, 
{_id:0, name:1, students:{$elemMatch:{$eq:ObjectId("51780f796ec4051a536015cf")}}}) 

a dostaniesz z powrotem czego oczekuje:

{ "name" : "CS 101", "students" : [ ObjectId("51780f796ec4051a536015cf") ] } 
{ "name" : "Literature" } 
{ "name" : "Physics", "students" : [ ObjectId("51780f796ec4051a536015cf") ] } 

[Original Odpowiedź] To nie jest możliwe, aby robić to, co chcesz zrobić obecnie. Jest to niefortunne, ponieważ byłbyś w stanie to zrobić, gdyby uczeń był przechowywany w tablicy jako obiekt. W rzeczywistości jestem trochę zaskoczony, że używasz właśnie ObjectId(), ponieważ zawsze będzie to wymagać od ciebie sprawdzenia studentów, jeśli chcesz wyświetlić listę studentów zapisanych na dany kurs (najpierw przejrzyj listę pierwszych osób następnie wyszukaj nazwiska w kolekcji uczniów - dwa zapytania zamiast jednego!)

Gdybyś przechowywania (jako przykład) identyfikator i nazwę w tablicy oczywiście jak ten:

{ 
     "_id" : ObjectId("51780fb5c9c41825e3e21fc6"), 
     "name" : "Physics", 
     "students" : [ 
       {id: ObjectId("51780f796ec4051a536015cf"), name: "John"}, 
       {id: ObjectId("51780f796ec4051a536015d0"), name: "Sam"} 
     ] 
} 

Zapytanie wtedy byłoby po prostu:

db.course.find({ }, 
       { students : 
        { $elemMatch : 
         { id : ObjectId("51780f796ec4051a536015d0"), 
         name : "Sam" 
         } 
        } 
       } 
); 

Jeśli uczeń był Zarejestrowany tylko w CS 101 otrzymasz:

{ "name" : "Literature" } 
{ "name" : "Physics" } 
{ 
    "name" : "CS 101", 
    "students" : [ 
     { 
      "id" : ObjectId("51780f796ec4051a536015cf"), 
      "name" : "John" 
     } 
    ] 
} 
+0

Dziękuję za potwierdzenie moich przemyśleń na temat braku możliwości wysyłania zapytań do prostych tablic oraz tablic pod-dokumentów. Sądzę, że będę musiał ponownie ocenić mój schemat. Czy wiesz, czy 10Gen zamierza rozwiązać ten problem? – shargors

+0

Nie mogłem znaleźć biletu Jira na to na jira.mongodb.org i nic nie jest zaplanowane na wydanie bez biletu tam, więc powiedziałbym na pewno nie w najbliższym czasie. –

+0

A co z użyciem $ all, jak opisano w http://stackoverflow.com/questions/8145523/mongodb-find-by-multiple-array-items – Xerri

14

Wygląda na to, że operator $in spełniłby twoje oczekiwania.

Można zrobić coś takiego (pseudo-query).

if (db.courses.find({"students" : {"$in" : [studentId]}, "course" : courseId }).count() > 0) { 
    // student is enrolled in class 
} 

Alternatywnie, można usunąć klauzulę "course" : courseId i wrócić zbiór wszystkich zajęć student jest zapisany w

+0

Problem z tym podejściem polega na tym, że zwróci dokument z kursów jon tylko wtedy, gdy uczeń zapisał się na kurs. Jeśli uczeń nie jest zarejestrowany, wymagane będzie drugie zapytanie. To nie jest wydajne. – shargors

+0

Drugie zapytanie to pobranie identyfikatora obiektu ucznia? Ale nie byłoby to już znane (w końcu używasz go do zrobienia pierwszego zapytania). – xbonez

+0

Celem końcowym jest pobranie dokumentu kursu i flaga wskazująca, czy uczeń się zarejestrował. Podane zapytanie spowoduje pobranie dokumentu kursu tylko wtedy, gdy uczeń go zarejestrował. Jeśli uczeń nie zarejestrował się na kurs, potrzebne jest kolejne zapytanie w celu pobrania dokumentu kursu. – shargors

Powiązane problemy