【问题标题】:MongoDB: How to find out if an array field contains an element?MongoDB:如何找出数组字段是否包含元素?
【发布时间】:2013-04-18 09:13:16
【问题描述】:

我有两个系列。第一个集合包含学生:

{ "_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。有没有办法在查找查询的投影中指定仅在存在时才从 students 数组中检索学生 ObjectId

我尝试查看是否可以使用 $elemMatch 运算符,但它适用于一组子文档。我知道我可以使用聚合框架,但在这种情况下它似乎有点矫枉过正。聚合框架可能不会像单个查找查询那样快。有没有办法查询课程集合,以便返回的文档可以是类似这样的形式?

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

【问题讨论】:

    标签: mongodb


    【解决方案1】:

    [编辑基于这一点现在可以在最近的版本中]

    [更新答案]只有在已经注册的情况下,您可以通过以下方式查询班级名称和学生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"
            }
        ]
    }
    

    【讨论】:

    • 感谢您确认我对无法查询简单数组以及子文档数组的想法。我想我将不得不重新评估我的架构。你知道 10Gen 是否计划解决这个问题?
    • 我无法在 jira.mongodb.org 中找到这方面的 Jira 票证,并且没有任何安排在没有票证的情况下发布,所以我会说绝对不会很快。
    • stackoverflow.com/questions/8145523/…中所述使用$all怎么样
    • $all 是用于查询的运算符 - 查询不是问题,问题正在返回,所以我们正在讨论的是投影运算符。
    • course 集合中同时存储名称和对象ID 的问题在于,您将学生姓名存储在多个位置,这可能会导致不同步。在这种情况下,您的查询将返回不完整的数据。如果服务器负载和硬件不是很薄,那么获取名称的额外查询可能更可取。您仍然可以将学生 ObjectIds 存储为对象以允许 OP 查询:{"id":...}
    【解决方案2】:

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

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

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

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

    【讨论】:

    • 这种方法的问题在于,只有当学生注册了课程时,它才会从课程集合中返回一个文档。如果学生尚未注册,则需要进行第二次查询。这效率不高。
    • 第二个查询是检索学生的对象ID?但这不是已经知道的吗(毕竟,您正在使用它进行第一个查询)。
    • 最终目标是检索课程文档并有一个标志来指示学生是否已注册。您提供的查询将仅在学生已注册的情况下检索课程文档。如果学生尚未注册课程,则需要另一个查询来检索课程文档。
    • 我认为数据模型没有任何问题。我怀疑没有两个查询就没有真正的方法来实现你想要的,因为在一天结束时,你会问你的数据库两个问题:给我课程文档并且是参加课程的学生。即使在传统的关系数据库中,您也会面临同样的问题。
    • 迟到的响应(对不起!),但这从来都不是 Mongo 的目标。 Mongo 是存储非关系的、无模式的数据对象。 AFAIK,它没有尝试减少所需的查询数量。
    【解决方案3】:

    我试图通过提出问题陈述和解决方案来解释。我希望它会有所帮助

    问题陈述:

    查找所有已发布的产品,名称为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
                    )
                );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多