查询 MongoDB 数组

MongoDBBeginner
立即练习

介绍

在本实验中,你将学习如何在 MongoDB 中有效地查询数组。数组是 MongoDB 中的一种基本数据结构,它允许你在单个文档字段中存储值的列表。本实验将指导你掌握处理数组数据的基本技术,从简单的元素匹配到使用专门操作符的更复杂查询。你将学习如何根据数组内容、大小和元素位置查找文档,这些都是任何使用 MongoDB 的开发者必备的关键技能。

这是一个实验(Guided Lab),提供逐步指导来帮助你学习和实践。请仔细按照说明完成每个步骤,获得实际操作经验。根据历史数据,这是一个 初级 级别的实验,完成率为 100%。获得了学习者 100% 的好评率。

入门与基础数组匹配

在本实验的第一步,你将连接到 MongoDB 服务器,创建一个数据库并插入一些示例数据。然后,你将执行基础查询,通过匹配数组中的元素来查找文档。

首先,打开 MongoDB Shell (mongosh) 以与你的数据库进行交互。此命令将连接到正在运行的 MongoDB 实例。

mongosh

进入 shell 后,你将看到一个 test> 提示符。让我们切换到一个名为 arraylab 的新数据库。use 命令会在数据库不存在时创建它,并将其切换为当前上下文。

use arraylab

现在,让我们向一个名为 products 的新集合中插入一些文档。每个文档代表一个产品,并包含两个数组字段:tagscolors。将以下命令复制并粘贴到你的 shell 中:

db.products.insertMany([
  {
    name: "Laptop",
    tags: ["electronics", "computer", "work"],
    colors: ["silver", "black", "blue"]
  },
  {
    name: "Smartphone",
    tags: ["electronics", "mobile", "communication"],
    colors: ["red", "blue", "green"]
  },
  {
    name: "Headphones",
    tags: ["electronics", "audio", "music"],
    colors: ["black", "white"]
  }
]);

要查找包含数组中特定元素的文档,你可以直接查询该值。此查询将查找所有具有 "mobile" 标签的产品。

db.products.find({ tags: "mobile" });

示例输出:

[
  {
    _id: ObjectId("..."),
    name: 'Smartphone',
    tags: [ 'electronics', 'mobile', 'communication' ],
    colors: [ 'red', 'blue', 'green' ]
  }
]

你还可以查找数组与元素精确序列匹配的文档。此查询将查找 tags 数组精确为 ["electronics", "computer", "work"] 的产品。

db.products.find({ tags: ["electronics", "computer", "work"] });

示例输出:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  }
]

你可以在 mongosh shell 中继续进行下一步。要随时退出 shell,请键入 exit 并按 Enter。

使用数组查询操作符 $all$in

简单的匹配很有用,但 MongoDB 提供了强大的操作符来实现更复杂的数组查询。在本实验中,你将学习使用 $all 来匹配多个元素而不考虑顺序,以及使用 $in 来匹配列表中任意一个元素。

我们将继续使用上一步中的 products 集合。

$all 操作符会选择数组字段包含所有指定元素的文档。查询中元素的顺序无关紧要。让我们查找同时提供 "blue" 和 "silver" 颜色的产品。

db.products.find({ colors: { $all: ["blue", "silver"] } });

示例输出:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  }
]

$in 操作符会选择数组字段包含指定值中至少一个的文档。它在数组元素上起到了“或”条件的作用。让我们查找具有 "music" 标签或 "work" 标签的产品。

db.products.find({ tags: { $in: ["music", "work"] } });

示例输出:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  },
  {
    _id: ObjectId("..."),
    name: 'Headphones',
    tags: [ 'electronics', 'audio', 'music' ],
    colors: [ 'black', 'white' ]
  }
]

这些操作符在根据数组内容搜索文档时提供了更大的灵活性。

按数组大小和位置查询

有时,数组中的元素数量或特定元素的位置很重要。本实验将介绍如何使用 $size 操作符按数组大小进行查询,以及如何使用点表示法(dot notation)按元素位置进行查询。

$size 操作符会匹配数组字段具有特定元素数量的文档。让我们查找提供正好两种颜色的产品。

db.products.find({ colors: { $size: 2 } });

示例输出:

[
  {
    _id: ObjectId("..."),
    name: 'Headphones',
    tags: [ 'electronics', 'audio', 'music' ],
    colors: [ 'black', 'white' ]
  }
]

要查询数组中特定位置(索引)的元素,你可以在格式 "fieldName.index" 中使用点表示法。请记住,数组索引是从零开始的,因此第一个元素位于索引 0。此查询将查找 tags 数组中第一个元素是 "electronics" 的所有产品。

db.products.find({ "tags.0": "electronics" });

示例输出:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  },
  {
    _id: ObjectId("..."),
    name: 'Smartphone',
    tags: [ 'electronics', 'mobile', 'communication' ],
    colors: [ 'red', 'blue', 'green' ]
  },
  {
    _id: ObjectId("..."),
    name: 'Headphones',
    tags: [ 'electronics', 'audio', 'music' ],
    colors: [ 'black', 'white' ]
  }
]

现在,让我们查找第二个颜色(索引 1)是 "black" 的产品。

db.products.find({ "colors.1": "black" });

示例输出:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  }
]

查询嵌入文档的数组

MongoDB 中一个常见且强大的模式是使用包含其他文档的数组。这些被称为嵌入式文档或嵌套文档。查询这些文档需要一个特殊的操作符 $elemMatch,以确保所有条件都应用于同一个数组元素。

首先,让我们添加一些具有 specs 数组的新产品。specs 数组中的每个元素都是一个文档,包含 ramstorageprice 字段。

db.products.insertMany([
  {
    name: "Gaming Laptop",
    tags: ["electronics", "computer", "gaming"],
    specs: [
      { ram: 16, storage: 512, price: 1200 },
      { ram: 32, storage: 1024, price: 2000 }
    ]
  },
  {
    name: "Office Laptop",
    tags: ["electronics", "computer", "work"],
    specs: [
      { ram: 8, storage: 256, price: 600 },
      { ram: 16, storage: 512, price: 800 }
    ]
  }
]);

现在,假设我们想找到一个具有至少 16GB RAM 和价格低于 $1000 的配置的笔记本电脑。一个简单的查询可能看起来正确,但可能导致错误的结果。例如,db.products.find({ "specs.ram": { $gte: 16 }, "specs.price": { $lt: 1000 } }) 会匹配一个文档,如果一个嵌入式文档有足够的 RAM,而 另一个 嵌入式文档的价格足够低。

为了确保数组中的 单个 嵌入式文档满足所有条件,你必须使用 $elemMatch 操作符。

db.products.find({
  specs: {
    $elemMatch: { ram: { $gte: 16 }, price: { $lt: 1000 } }
  }
});

此查询仅正确地找到了 "Office Laptop",因为它有一个满足两个条件的 specs 元素 { ram: 16, storage: 512, price: 800 }

示例输出:

[
  {
    _id: ObjectId("..."),
    name: 'Office Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    specs: [
      { ram: 8, storage: 256, price: 600 },
      { ram: 16, storage: 512, price: 800 }
    ]
  }
]

投影数组字段

通常,你不需要查询返回整个文档。投影(Projection)允许你指定要包含或排除的字段。对于数组,MongoDB 提供了诸如 $slice 之类的特殊投影操作符来控制返回哪些数组元素。

$slice 投影操作符返回数组的一个子集。你可以指定一个正数来获取前 N 个元素,或一个负数来获取后 N 个元素。

让我们检索所有产品,但为每个产品只返回 name 和前两个 tagsfind 的第一个参数 {} 是一个匹配所有文档的空查询文档。第二个参数是投影文档。我们使用 _id: 0 来排除默认的 _id 字段。

db.products.find({}, { name: 1, tags: { $slice: 2 }, _id: 0 });

示例输出:

[
  { "name": "Laptop", "tags": ["electronics", "computer"] },
  { "name": "Smartphone", "tags": ["electronics", "mobile"] },
  { "name": "Headphones", "tags": ["electronics", "audio"] },
  { "name": "Gaming Laptop", "tags": ["electronics", "computer"] },
  { "name": "Office Laptop", "tags": ["electronics", "computer"] }
]

另一个有用的投影操作符是位置 $ 操作符。当你查询数组时,此操作符确保在投影的数组字段中只返回匹配查询条件的第一个元素。

让我们查找带有 "mobile" 标签的产品,并仅从 tags 数组中返回 name 和那个匹配的特定标签。

db.products.find({ tags: "mobile" }, { name: 1, "tags.$": 1, _id: 0 });

示例输出:

[{ "name": "Smartphone", "tags": ["mobile"] }]

这对于只检索大型数组的相关部分而无需在你的应用程序中处理整个数组非常高效。

总结

在本实验中,你学习了 MongoDB 中查询数组的基本技术。你从基本的元素匹配开始,然后进阶使用 $all$in$size 等强大的数组操作符。你还探索了如何按元素位置进行查询,以及如何使用 $elemMatch 正确查询嵌入式文档的数组。最后,你学习了如何使用 $slice 和位置 $ 操作符投影特定的数组元素来塑造查询的输出。这些技能对于在 MongoDB 中有效管理和检索复杂文档结构的数据至关重要。