定义
兼容性
可以使用 $unwind 查找托管在以下环境中的部署:
- MongoDB Atlas:用于云中 MongoDB 部署的完全托管服务
MongoDB Enterprise:基于订阅、自我管理的 MongoDB 版本
MongoDB Community:源代码可用、免费使用且可自行管理的 MongoDB 版本
语法
传递字段路径(Field Path)操作数或文档操作数以展开数组字段。
字段路径操作数
您可以将数组字段路径(Field Path)传递给 $unwind。使用该语法时,如果字段值为 null、缺失或空数组,则 $unwind 不会输出文档。
{ $unwind: <field path> }如需指定字段路径,在字段名称前加上美元符号 $,并用引号括起来。
带选项的文档操作数
您可以将文档传递给 $unwind 以指定选项。
{ $unwind: { path: <field path>, includeArrayIndex: <string>, preserveNullAndEmptyArrays: <boolean> }}字段 | 类型 | 说明 |
|---|---|---|
字符串 | 数组字段的字段路径。如需指定字段路径,请在字段名称前加上美元符号 | |
字符串 | 可选。新字段的名称,用于保存该元素的数组索引。名称不能以美元符号 | |
布尔 | 可选。
默认值为 |
行为
非数组字段路径
当 path 的值未解析为数组时,$unwind 的行为如下:
如果该值未缺失、不是
null且不是空数组,则$unwind会按原样使用该值输出单个文档。如果指定了
includeArrayIndex,则数组输入的索引为0,非数组输入的索引为null。列表中靠后的文档的索引大于0。如果该值缺失、为
null或空数组,则$unwind遵循 keepNullAndEmptyArrays 选项。当指定includeArrayIndex并保留文档时,索引为null。
缺失字段
如果为输入文档中不存在的字段指定路径,或者该字段为空数组,则默认情况下,$unwind 会忽略此输入文档,并且不会为该输入文档输出文档。
请使用 preserveNullAndEmptyArrays 选项输出数组字段缺失、为 null 或空数组的文档。
示例
本页上的示例使用来自 sample_mflix 数据集的数据。有关如何将此数据集加载到您的自管理 MongoDB 部署中的详细信息,请参阅 加载示例数据集。如果您对示例数据库进行了任何修改,您可能需要删除并重新创建数据库,才能运行此页面上的示例。
展开数组
以下聚合使用 $unwind 阶段为 Inception 电影文档的 genres 数组中的每个元素输出一个文档:
db.movies.aggregate( [ { $match: { title: "Inception" } }, { $project: { _id: 0, title: 1, genres: 1 } }, { $unwind: "$genres" }] )[ { genres: 'Action', title: 'Inception' }, { genres: 'Mystery', title: 'Inception' }, { genres: 'Sci-Fi', title: 'Inception' }]每个输出文档都与输入文档完全相同,只是 genres 字段的值不同,该字段现在包含原始 genres 数组中的单个元素。
缺少或非数组值
以下聚合使用 $unwind 阶段处理四个电影文档。两个文档 ("La porta del cielo" 和 "Neecha Nagar") 没有 genres 字段:
db.movies.aggregate( [ { $match: { title: { $in: [ "Inception", "Brave", "La porta del cielo", "Neecha Nagar" ] } } }, { $project: { _id: 0, title: 1, genres: 1 } }, { $unwind: { path: "$genres" } }] )[ { genres: 'Animation', title: 'Brave' }, { genres: 'Adventure', title: 'Brave' }, { genres: 'Comedy', title: 'Brave' }, { genres: 'Action', title: 'Inception' }, { genres: 'Mystery', title: 'Inception' }, { genres: 'Sci-Fi', title: 'Inception' }]在
"Brave"和"Inception"文档中,genres是一个填充数组。$unwind为每个元素返回一个文档。"La porta del cielo"和"Neecha Nagar"文档中没有genres字段,因此$unwind不会为它们返回任何文档。
注意
{ path: <FIELD> } 语法是可选的。以下 $unwind 操作是等效的。
db.<COLLECTION>.aggregate( [ { $unwind: "<FIELD>" } ])db.<COLLECTION>.aggregate( [ { $unwind: { path: "<FIELD>" } } ])preserveNullAndEmptyArrays 和 includeArrayIndex
preserveNullAndEmptyArrays 和 includeArrayIndex 示例使用来自 sample_mflix.movies 集合的文档。
preserveNullAndEmptyArrays
以下 $unwind 操作使用 preserveNullAndEmptyArrays 选项来纳入 genres 字段缺失的文档。
db.movies.aggregate( [ { $match: { title: { $in: [ "Inception", "Brave", "La porta del cielo", "Neecha Nagar" ] } } }, { $project: { _id: 0, title: 1, genres: 1 } }, { $unwind: { path: "$genres", preserveNullAndEmptyArrays: true } }] )[ { title: 'La porta del cielo' }, { title: 'Neecha Nagar' }, { genres: 'Animation', title: 'Brave' }, { genres: 'Adventure', title: 'Brave' }, { genres: 'Comedy', title: 'Brave' }, { genres: 'Action', title: 'Inception' }, { genres: 'Mystery', title: 'Inception' }, { genres: 'Sci-Fi', title: 'Inception' }]includeArrayIndex
以下$unwind 操作使用 includeArrayIndex 选项以在输出中纳入数组索引。
db.movies.aggregate( [ { $match: { title: "Inception" } }, { $project: { _id: 0, title: 1, genres: 1 } }, { $unwind: { path: "$genres", includeArrayIndex: "genreIndex" } }] )[ { genres: 'Action', title: 'Inception', genreIndex: Long('0') }, { genres: 'Mystery', title: 'Inception', genreIndex: Long('1') }, { genres: 'Sci-Fi', title: 'Inception', genreIndex: Long('2') }]按未展开值分组
以下管道将 genres 数组展开,并按类型对生成的文档进行分组,以统计每种类型的电影数量:
db.movies.aggregate( [ // First Stage { $match: { title: { $in: [ "The Dark Knight", "Inception", "Interstellar", "Brave" ] } } }, // Second Stage { $project: { _id: 0, title: 1, genres: 1 } }, // Third Stage { $unwind: "$genres" }, // Fourth Stage { $group: { _id: "$genres", movieCount: { $sum: 1 } } }, // Fifth Stage { $sort: { movieCount: -1 } }] )[ { _id: 'Adventure', movieCount: 2 }, { _id: 'Action', movieCount: 2 }, { _id: 'Drama', movieCount: 2 }, { _id: 'Sci-Fi', movieCount: 2 }, { _id: 'Crime', movieCount: 1 }, { _id: 'Animation', movieCount: 1 }, { _id: 'Comedy', movieCount: 1 }, { _id: 'Mystery', movieCount: 1 }]展开多个数组字段
您可以在单个管道中多次应用 $unwind,以展开包含多个数组字段的文档。以下操作先展开 genres 数组,然后展开 cast 数组,为每个流派-演员组合生成一个扁平文档,然后按流派分组以计算每个流派中的总演员出场次数:
db.movies.aggregate( [ // First Stage { $match: { title: { $in: [ "Inception", "The Dark Knight", "Interstellar" ] } } }, // Second Stage { $project: { _id: 0, title: 1, genres: 1, cast: 1 } }, // Third Stage { $unwind: "$genres" }, // Fourth Stage { $unwind: "$cast" }, // Fifth Stage { $group: { _id: "$genres", castAppearances: { $sum: 1 } } }] )[ { _id: 'Adventure', castAppearances: 4 }, { _id: 'Crime', castAppearances: 4 }, { _id: 'Action', castAppearances: 8 }, { _id: 'Drama', castAppearances: 8 }, { _id: 'Mystery', castAppearances: 4 }, { _id: 'Sci-Fi', castAppearances: 8 }]本页上的C#示例使用Atlas示例数据集中的 sample_mflix数据库。要学习;了解如何创建免费的MongoDB Atlas 群集并加载示例数据集,请参阅MongoDB .NET/ C#驱动程序文档中的入门。
以下 Movie 类对 sample_mflix.movies 集合中的文档进行建模:
public class Movie{ public ObjectId Id { get; set; }
public int Runtime { get; set; } public string Title { get; set; }
public string Rated { get; set; }
public List<string> Genres { get; set; }
public string Plot { get; set; } public ImdbData Imdb { get; set; }
public int Year { get; set; }
public int Index { get; set; } public string[] Comments { get; set; } [BsonElement("lastupdated")] public DateTime LastUpdated { get; set; }}注意
用于 Pascal Case 的 ConventionPack
此页面上的 C# 类在其属性名称中使用 Pascal 命名法,而 MongoDB 集合中的字段名称则使用 camel 命名法。为了解决这种差异,可以在应用程序启动时使用以下代码注册一个 ConventionPack:
var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() };ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);要使用MongoDB .NET/ C#驱动程序将 $unwind 阶段添加到聚合管道,请对 PipelineDefinition对象调用 Unwind() 方法。
以下示例创建了一个管道阶段,用于遍历每个输入 Movie文档中的 Genres字段。对于 Genres字段中的每个值,该阶段都会创建新的 Movie文档,并使用输入文档中的 Genres 值填充其 Genres字段。
var pipeline = new EmptyPipelineDefinition<Movie>() .Unwind(m => m.Genres);您可以使用 AggregateUnwindOptions 对象来自定义 Unwind() 方法的行为。
以下示例执行与上一示例相同的操作,但还包括以下选项:
PreserveNullAndEmptyArrays确保输出中包含Genres字段中包含空大量的文档。IncludeArrayIndex选项将名为Index的新字段添加到每个输出文档中。该字段的值是输入文档的Genres大量中Genres字段值的大量索引。
var pipeline = new EmptyPipelineDefinition<Movie>() .Unwind(m => m.Genres, new AggregateUnwindOptions<Movie>() { PreserveNullAndEmptyArrays = true, IncludeArrayIndex = new ExpressionFieldDefinition<Movie, int>( m => m.Index) });本页上的 Node.js 示例使用 Atlas 示例数据集中的 sample_mflix数据库。要学习如何创建免费的MongoDB Atlas 集群并加载示例数据集,请参阅MongoDB Node.js驱动程序文档中的入门。
要使用MongoDB Node.js驱动程序将 $unwind 阶段添加到聚合管道,请在管道对象中使用 $unwind操作符。
以下示例创建了一个管道阶段,用于遍历每个输入 movie文档中的 genres字段。对于 genres字段中的每个值,该阶段都会创建新的 movie文档,并使用输入文档中的 genres 值填充其 genres字段。然后,该示例运行聚合管道:
const pipeline = [{ $unwind: "$genres" }];
const cursor = collection.aggregate(pipeline);return cursor;您可以自定义 $unwind 方法的行为。以下示例执行与上一示例相同的操作,但还包括以下选项:
preserveNullAndEmptyArrays确保输出中包含genres字段中包含空大量的文档。includeArrayIndex向每个输出文档添加一个名为index的新字段。该字段包含输入文档的genres字段中genres值的大量索引。
const pipeline = [ { $unwind: { path: "$genres", preserveNullAndEmptyArrays: true, includeArrayIndex: "index" } }];
const cursor = collection.aggregate(pipeline);return cursor;了解详情
有关相关阶段和表达式,请参阅 $group、$sum、$sort 和 $multiply。
有关完整示例,请参阅展开数组和分组数据教程。