文档菜单
文档首页
/
MongoDB 手册
/ / /

$out(聚合)

本页

  • 定义
  • 语法
  • 行为
  • 示例
$out

将聚合管道返回的文档写入指定的集合。您可以指定输出数据库。

$out阶段必须是管道中的最后一个阶段$out运算符允许聚合框架返回任何大小的结果集。

警告

如果$out操作指定的集合已存在,则在聚合完成后,$out阶段会原子性地用新的结果集合替换现有集合。有关详细信息,请参阅替换现有集合

输出阶段 $out 有以下语法

  • $out 可以接受一个字符串来指定仅输出集合(即输出到同一数据库中的集合)

    { $out: "<output-collection>" } // Output collection is in the same database
  • $out 可以接受一个文档来指定输出数据库以及输出集合

    { $out: { db: "<output-db>", coll: "<output-collection>" } }
  • 从 MongoDB 7.0.3 和 7.1 开始,$out 可以接受一个文档来输出到时间序列集合:

    { $out:
    { db: "<output-db>", coll: "<output-collection>",
    timeseries: {
    timeField: "<field-name>",
    metaField: "<field-name>",
    granularity: "seconds" || "minutes" || "hours" ,
    }
    }
    }

    重要

    更改时间序列粒度

    创建时间序列集合后,您可以使用 collMod 方法修改其粒度。但是,您只能增加每个桶覆盖的时间跨度。您不能减少它。

    字段
    描述
    db

    输出数据库名。

    • 对于副本集或独立服务器,如果输出数据库不存在,$out 也会创建数据库。

    coll

    输出集合名。

    timeseries

    一个文档,指定写入时间序列集合时使用的配置。必须提供 timeField。其他所有字段都是可选的。

    timeField

    写入时间序列集合时必须提供。.. include:: /includes/time-series/fact-time-field-description.rst

    metaField

    可选。包含每个时间序列文档元数据的字段的名称。在指定的字段中的元数据应该是用于标记唯一文档序列的数据。元数据很少或几乎不会改变。指定字段的名称不得为 _id 或与 timeseries.timeField 相同。字段可以是任何数据类型。

    尽管 metaField 字段是可选的,但使用元数据可以提高查询优化。例如,MongoDB自动为新集合在 metaFieldtimeField 字段上创建组合索引。如果您不为此字段提供值,数据将仅基于时间进行桶划分。

    granularity

    可选。如果设置了 bucketRoundingSecondsbucketMaxSpanSeconds,则不要使用。

    可能的值是 seconds(默认值)、minuteshours

    granularity 设置为与连续传入时间戳之间的时间最接近的值。这通过优化MongoDB在集合中存储数据的方式来提高性能。

    有关粒度和桶间隔的更多信息,请参阅为时间序列数据设置粒度。

    bucketMaxSpanSeconds

    可选。与bucketRoundingSeconds一起使用,作为粒度的替代方案。设置同一桶中时间戳之间的最大时间间隔。

    可能值为1-31536000。

    新特性版本6.3.

    bucketRoundingSeconds

    可选。与bucketMaxSpanSeconds一起使用,作为粒度的替代方案。必须等于bucketMaxSpanSeconds

    当文档需要新的桶时,MongoDB将文档的时间戳值按此间隔向下舍入,以设置桶的最小时间。

    新特性版本6.3.

重要

  • 您不能指定分片集合作为输出集合。管道的输入集合可以是分片的。要将输出输出到分片集合,请参阅$merge

  • $out运算符不能将结果写入固定集合。

  • 如果您修改了一个包含Atlas Search索引的集合,您必须首先删除并重新创建搜索索引。考虑使用$merge代替。

MongoDB提供了两个阶段,$merge$out,用于将聚合管道的结果写入集合。以下总结了这两个阶段的功能

$out
  • 可以输出到同一数据库或不同数据库中的集合。

  • 可以输出到同一数据库或不同数据库中的集合。

  • 如果输出集合尚不存在,则创建新集合。

  • 如果输出集合尚不存在,则创建新集合。

  • 如果输出集合已存在,则完全替换输出集合。

  • 可以将结果(插入新文档、合并文档、替换文档、保留现有文档、失败操作、使用自定义更新管道处理文档)合并到现有集合中。

    可以替换集合的内容,但前提是聚合结果包含与集合中所有现有文档匹配的匹配项。

  • 不能输出到分片集合。然而,输入集合可以是分片的。

  • 可以输出到分片集合。输入集合也可以是分片的。

  • 从MongoDB 7.0.3和7.1版本开始,可以输出到时间序列集合。

  • 不能输出到时间序列集合。

  • 对应于SQL语句

    • INSERT INTO T2 SELECT * FROM T1
    • SELECT * INTO T2 FROM T1
  • 对应于SQL语句

    • MERGE T2 AS TARGET
      USING (SELECT * FROM T1) AS SOURCE
      ON MATCH (T2.ID = SOURCE.ID)
      WHEN MATCHED THEN
      UPDATE SET TARGET.FIELDX = SOURCE.FIELDY
      WHEN NOT MATCHED THEN
      INSERT (FIELDX)
      VALUES (SOURCE.FIELDY)
    • 创建/刷新物化视图

从MongoDB 5.0版本开始,如果集群中所有节点都将功能兼容性版本设置为5.0或更高,并且将读取偏好设置为辅助,则$out可以在副本集的辅助节点上运行。

$out语句的读操作在辅助节点上执行,而写操作仅在主节点上执行。

并非所有驱动程序版本都支持将$out操作的目标设置为副本集的辅助节点。请查阅您的驱动程序文档,以了解您的驱动程序何时添加了对在辅助节点上运行$out的支持。

如果不存在,$out操作将创建一个新的集合。

集合在聚合完成之前不可见。如果聚合失败,MongoDB不会创建集合。

如果由 $out 操作指定的集合已存在,则在聚合完成后,$out 阶段会原子性地用新的结果集合替换现有的集合。具体来说,$out 操作

  1. 创建一个临时集合。

  2. 将现有集合的索引复制到临时集合中。

  3. 将文档插入到临时集合中。

  4. 使用带有 dropTarget: truerenameCollection 命令将临时集合重命名为目标集合。

如果指定的集合存在并且 $out 操作指定了 timeseries 选项,则有以下限制

  1. 现有集合必须是时间序列集合。

  2. 现有集合不能是视图。

  3. $out 阶段中包含的 timeseries 选项必须与现有集合上的选项完全匹配。

$out 操作不会更改前一个集合上存在的任何索引。如果聚合失败,则 $out 操作不会对现有的集合进行任何更改。

如果您的 coll 集合使用模式验证并且 validationAction 设置为 error,则使用 $out 插入无效文档会引发错误。 $out 操作不会对现有的集合进行任何更改,并且聚合管道返回的文档不会添加到 coll 集合中。

如果管道产生的文档违反了任何唯一索引,包括原始输出集合中 _id 字段的索引,则管道将无法完成。

如果 $out 操作修改了带有 Atlas Search 索引的集合,您必须删除并重新创建搜索索引。请考虑使用 $merge 代替。

您可以为包含 读取关注度 阶段的聚合指定 "majority" 级别。

当客户端在导出过程中发出包含 $out 的聚合管道时,以 mongodump 开始并带有 --oplog 的命令将失败。有关更多信息,请参阅 mongodump --oplog

限制
描述
聚合管道不能在 事务 中使用 $out
$out 阶段不允许作为视图定义的一部分。如果视图定义包含嵌套管道(例如,视图定义包含 $lookup$facet 阶段),则此 $out 阶段限制也适用于嵌套管道。
$lookup 阶段
不能在 $lookup 阶段的 嵌套管道 中包含 $out 阶段。
$facet 阶段
$facet 阶段的 嵌套管道 不能包含 $out 阶段。
$unionWith 阶段
$unionWith 阶段的嵌套管道不能包含 $out 阶段。
"linearizable" 读取关注点

$out 阶段不能与读取关注点 "linearizable" 结合使用。如果您为 "linearizable" 读取关注点指定了 db.collection.aggregate(),则不能在管道中包含 $out 阶段。

test 数据库中,创建一个包含以下文档的集合 books

db.getSiblingDB("test").books.insertMany([
{ "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 },
{ "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 },
{ "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 },
{ "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 },
{ "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
])

如果 test 数据库不存在,则插入操作将创建数据库以及 books 集合。

以下聚合操作将 test 数据库中 books 集合中的数据旋转,按作者分组标题,然后将结果写入同一数据库中的 authors 集合。

db.getSiblingDB("test").books.aggregate( [
{ $group : { _id : "$author", books: { $push: "$title" } } },
{ $out : "authors" }
] )
第一阶段 ($group)

$group 阶段按 authors 分组,并使用 $push 将标题添加到 books 数组字段

{ "_id" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }
{ "_id" : "Homer", "books" : [ "The Odyssey", "Iliad" ] }
第二阶段($out
$out 阶段将文档输出到 test 数据库中的 authors 集合。

要查看输出集合中的文档,请运行以下操作

db.getSiblingDB("test").authors.find()

该集合包含以下文档

{ "_id" : "Homer", "books" : [ "The Odyssey", "Iliad" ] }
{ "_id" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }

注意

对于副本集或独立服务器,如果输出数据库不存在,$out 也会创建数据库。

$out 可以将输出输出到与聚合运行不同的数据库中的集合。

以下聚合操作将 books 集合中的数据进行转换,按作者分组标题,然后将结果写入 reporting 数据库中的 authors 集合

db.getSiblingDB("test").books.aggregate( [
{ $group : { _id : "$author", books: { $push: "$title" } } },
{ $out : { db: "reporting", coll: "authors" } }
] )
第一阶段 ($group)

$group 阶段按 authors 分组,并使用 $push 将标题添加到 books 数组字段

{ "_id" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }
{ "_id" : "Homer", "books" : [ "The Odyssey", "Iliad" ] }
第二阶段($out
$out 阶段将文档输出到 reporting 数据库中的 authors 集合。

要查看输出集合中的文档,请运行以下操作

db.getSiblingDB("reporting").authors.find()

该集合包含以下文档

{ "_id" : "Homer", "books" : [ "The Odyssey", "Iliad" ] }
{ "_id" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }

返回

$merge