Подтвердить что ты не робот

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. Есть ли способ указать в проекции запроса на поиск для извлечения студента ObjectId из массива students только в том случае, если он есть?

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

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

Ответ 1

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

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

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, затем посмотрите имена в коллекции студентов - два запроса вместо одного!)

Если вы хранили (в качестве примера) идентификатор и имя в массиве курсов, например:

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

Ответ 2

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

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

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

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

Ответ 3

Я пытаюсь объяснить, поставив постановку задачи и ее решение. Надеюсь, это поможет

Постановка задачи:

Найдите все опубликованные продукты, название которых, например, ABC Product или PQR Product, и цена должны быть ниже 15 / -

Решение:

Ниже приведены условия, о которых необходимо позаботиться

  1. Цена товара должна быть меньше 15
  2. Название продукта должно быть либо ABC Product, либо PQR Product.
  3. Товар должен быть в опубликованном состоянии.

Ниже приведено утверждение, которое применяет вышеуказанный критерий для создания запроса и выборки данных.

$elements = $collection->find(
             Array(
                [price] => Array( [$lt] => 15 ),
                [$or] => Array(
                            [0]=>Array(
                                    [product_name]=>Array(
                                       [$in]=>Array(
                                            [0] => ABC Product,
                                            [1]=> PQR Product
                                            )
                                        )
                                    )
                                ),
                [state]=>Published
                )
            );