MongoDB:如何确定数组字段是否包含元素?

时间:2013-04-24 17:26:07

标签: 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"),
        ]
}

3 个答案:

答案 0 :(得分:61)

[编辑基于此现在可以在最近的版本中使用]

[更新后的答案]您可以查询以下方式,只有在已经注册的情况下才能取回课程名称和学生ID。

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和名称,如下所示:

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

答案 1 :(得分:21)

似乎$in运算符可以很好地满足您的目的。

你可以这样做(伪查询):

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

或者,您可以删除"course" : courseId子句并返回学生注册的所有课程集。

答案 2 :(得分:0)

我正在尝试通过提出问题陈述和解决方案来进行解释。希望对您有帮助

问题陈述:

查找所有已发布产品,其名称应类似于ABC产品或PQR产品,且价格应低于15 /-

解决方案:

以下是需要注意的条件

  1. 产品价格应低于15
  2. 产品名称应为ABC产品或PQR产品
  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
                )
            );