MongoDB:как узнать, содержит ли поле массива элемент?


у меня есть две коллекции. Первый сборник содержит студентов:

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

вторая коллекция содержит курсы:

{
        "_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")
        ]
}

каждый документ курса содержит students массив, который имеет список студентов, зарегистрированных для курса. Когда студент просматривает курс на веб-странице, он должен увидеть, если он уже зарегистрировался на курс или нет. Для того, чтобы сделать это, когда courses коллекция получает запрос от имени студента, нам нужно выяснить, если students массив уже содержит ObjectId студента. Есть ли способ указать в проекции запроса на поиск, чтобы получить student ObjectId С students массив, только если она есть?

Я попытался посмотреть, могу ли я использовать оператор $elemMatch, но он ориентирован на массив поддокументов. Я понимаю, что я мог бы использовать aggregation framework, но кажется, что в этом случае это будет излишним. Aggregation framework, вероятно, будет не так быстро, как один запрос на поиск. Быть там способ запроса коллекции курсов, чтобы возвращаемый документ мог быть в форме, подобной этой?

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

2 ответа:

[ edit на основе этого теперь возможно в последних версиях]

[обновленный ответ] вы можете запросить следующий способ, чтобы вернуть имя класса и студенческий билет, только если они уже зарегистрированы.

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

и вы получите обратно то, что вы ожидали:

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

[оригинальный ответ] невозможно сделать то, что вы хотите сделать в настоящее время. Это прискорбно, потому что вы могли бы сделать это, если бы студент был сохранен в массив как объект. На самом деле, я немного удивлен, что вы используете только ObjectId (), как это будет всегда требуется, чтобы вы искали студентов, если вы хотите отобразить список студентов, обучающихся на определенном курсе (сначала посмотрите список идентификаторов, а затем посмотрите имена в коллекции студентов - два запроса вместо одного!)

если вы хранили (в качестве примера) идентификатор и имя в массиве курса следующим образом:

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

ваш запрос тогда будет просто быть:

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

если бы этот студент был зарегистрирован только в CS 101, вы бы вернулись:

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

кажется $in оператор будет служить вашим целям просто отлично.

вы могли бы сделать что-то вроде этого (псевдо-запрос):

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

кроме того, вы можете удалить "course" : courseId предложение и получить обратно набор всех классов, в которых студент зарегистрирован.