跳至主要内容

MongoDB 点号表示法教程:查询嵌套字段

学习在 MongoDB 中针对嵌套文档和数组的几种查询技巧。
· 5分钟 阅读

使用 AI 探索

如果您刚开始接触 MongoDB,很可能已经遇到文档结构变得有些复杂的情况。数据可能从扁平、接近关系型的样子,逐步演变为包含嵌套对象、嵌套数组,甚至四五层嵌套的结构。那么,当数据嵌套了好几层时,如何进行查询呢?

在这篇简短教程中,我们将了解 MongoDB 中的点号表示法,看看如何基于嵌套字段中包含特定值的数据,快速而简单地筛选文档。

所谓点号表示法,指的是用句点字符作为分隔符来分隔嵌套字段。例如:

root_field.nested_field_1.next_layer_nested_field_1

对于每一层嵌套,我们在查询时只需用一个句点字符将这一层分隔开。随着教程的推进,我们会看到更完整的示例。

如果您刚开始使用 MongoDB,务必查看 DataCamp 的Python 中的 MongoDB 入门课程。

先决条件

要顺利完成本教程,您需要已部署并配置好一个 MongoDB 实例。这意味着您已经准备好开始使用 MongoDB。同时,您至少应对在 MongoDB 中查询集合有基本了解。如果需要帮助,可以从文档开始。

在本示例中,我们将在名为 "dbinc" 的数据库和 "people" 集合中使用以下数据:

[
    {
        "name": "Nic Raboy",
        "websites": [
            {
                "type": "other",
                "name": "Portfolio",
                "url": "https://www.nraboy.com"
            },
            {
                "type": "social",
                "name": "LinkedIn",
                "url": "https://www.linkedin.com/in/nraboy"
            }
        ],
        "employment": {
            "employer": "MongoDB",
            "address": {
                "street": "1633 Broadway",
                "city": "New York",
                "state": "New York",
                "postal_code": 10019
            }
        }
    },
    {
        "name": "Tony Kim",
        "websites": [
            {
                "type": "social",
                "name": "LinkedIn",
                "url": "https://www.linkedin.com/in/hyung-kim/"
            },
            {
                "type": "coding",
                "name": "GitHub",
                "url": "https://github.com/hjkmines"
            }
        ],
        "employment": {
            "employer": "MongoDB",
            "address": {
                "street": "1633 Broadway",
                "city": "New York",
                "state": "New York",
                "postal_code": 10019
            }
        }
    }
]

上述数据包含两个文档,由于存在嵌套对象和数组,复杂度不低。顺带一提,MongoDB 在 BSON 文档中支持超过 100 层的嵌套。

如果您计划跟着操作,请确保这些文档已存在于您的 MongoDB 实例中。

在 MongoDB 中用多种方式查询数据

我们将从简单开始,逐步过渡到更复杂的情况。好消息是,这里的“复杂”并不意味着“困难”。

假设我们想查找集合中所有在 MongoDB 工作的人。我们可以执行如下查询:

use dbinc;

db.people.find({
	"employment.employer": "MongoDB"
});

在此示例中,employer 是 employment 对象内的嵌套字段。我们可以使用点号表示法来查询该字段,也就是用句点分隔字段。这与查找所有在纽约工作的人员没有区别。我们可以这样做:

use dbinc;

db.people.find({
	"employment.address.state": "New York"
});

上述查询深入到文档的第三级,但依然可以正常工作。

现在,让我们看看如何处理那个嵌套的对象数组。根据目标不同,有几种查询方式。

假设我们想返回所有列出“Portfolio”网站的人。可以执行如下查询:

use dbinc;

db.people.find({
	"websites.name": "Portfolio"
});

上述查询将返回一条结果,因为只有一个文档列出了“Portfolio”。请注意,即使这是一个数组,点号表示法依然适用。需要注意的是,“Portfolio”必须至少在该数组的某处出现一次。

接下来会更有意思。

假设我们想进一步收紧返回结果。通常我们会增加更多过滤条件,但在处理数组中的嵌套对象时,情况会有些不同。以以下为例:

use dbinc;

db.people.find({
    "websites.name": "Portfolio",
    "websites.type": "social"
});

上面的示例表示我们需要“Portfolio”和“social”同时存在于数组中,但不一定在同一个对象里。我们只得到一个结果,因为“Tony Kim”文档包含“social”,但没有“Portfolio”。如果我们需要两个字段必须出现在同一个对象中,就不能只用点号表示法。

虽然超出本教程范围,但可以使用 $elemMatch 运算符来实现:

use dbinc;

db.people.find(
    { 
        "websites": { 
            "$elemMatch": { 
                "name": "Portfolio",
                "type": "social" 
            }
        }
    }
);

由于示例数据中没有在同一个对象里同时包含匹配的 name 和 type,因此上述查询不会返回任何结果。

如果您在处理数组并且知道要使用的数组索引,仍然可以继续使用点号表示法。例如:

use dbinc;

db.people.find({
    "websites.0.name": "Portfolio"
});

上述查询使用了要匹配的数组索引。在这个场景中,我们希望返回所有文档,这些文档中数组的第一个元素的 name 为“Portfolio”。

虽然超出本教程范围,但需要指出,点号表示法并不限于只读查询。您可以在更新操作、投影以及聚合管道中像这里一样使用点号表示法。

结语

您刚刚快速了解了在 MongoDB 查询中使用点号表示法。我们学习了:

  • 点号表示法适用于嵌套对象。
  • 数组也可以使用点号表示法,但具体效果会受您对数组期望的限制影响。
  • $elemMatch 运算符用于在单个数组对象内同时匹配多个字段。
  • 如需查询数组中的特定元素,可以在点号表示法中使用数组索引。

MongoDB 集合中的数据很可能不是扁平的,这意味着在某个阶段,您很可能会遇到嵌套对象、嵌套数组、对象中的嵌套数组,或其他各种复杂组合。好消息是,MongoDB 借助其强大的查询 API,使处理这些信息变得轻而易举。

如果您尚未使用 MongoDB,可以了解MongoDB Atlas,或查看我关于通过 Docker 快速上手 MongoDB的另一篇教程。您还可以在我们的独立指南中了解MongoDB 认证。

FAQs

MongoDB 中的点号表示法是什么?

点号表示法是通过使用句点分隔符来访问 MongoDB 文档中的嵌套字段。

我能在查询中用多个嵌套字段进行过滤吗?

可以。您的匹配查询可以在全部、部分或不使用任何字段上运用点号表示法。过滤逻辑相同——只是以此方式访问嵌套项。

数组不可预测时该怎么办?

出于大小和性能考虑,您应确保数组不是无限增长的。但使用点号表示法来查询它们完全可行且依然简单。

如果我需要更新包含嵌套对象和数组的文档怎么办?

在执行更新操作时,点号表示法既可用于操作的过滤部分,也可用于变更部分。

主题