Docs 菜单

Docs 主页开发应用程序MongoDB Manual

$out(聚合)

在此页面上

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

获取聚合管道返回的文档并将其写入指定的collection。您可以指定输出数据库。

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

警告

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

$out 阶段使用以下语法:

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

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

    { $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 相同。字段可以是任何类型。

    granularity

    可选。如果设置 bucketRoundingSecondsbucketMaxSpanSeconds,请勿使用。

    可能的值为 seconds(默认)、minutes、和 hours

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

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

    bucketMaxSpanSeconds

    可选。与 bucketRoundingSeconds 结合使用以作为 granularity 的替代方案。设置同一存储桶中不同时间戳之间的最大时间间隔。

    可能的值为 1-31536000。

    6.3 版本中的新功能

    bucketRoundingSeconds

    可选。与 bucketMaxSpanSeconds 结合使用作为 granularity 的替代方案。必须等于 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 开始,如果集群中的所有节点将 featureCompatibilityVersion 设置为 5.0或更高版本并将读取偏好设置为从节点,则 $out 可以在副本集从节点上运行。

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

并非所有驱动程序版本都支持将$out操作定位到副本集从节点。检查驱动程序文档,了解驱动程序何时添加了对在从节点上运行的$out的支持。

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

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

如果 $out 操作指定的集合已存在,在聚合完成后,$out 阶段自动将现有集合替换为新的结果集合。具体来说,$out 操作:

  1. 创建临时集合。

  2. 将现有集合中的索引拷贝到临时集合。

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

  4. 使用 dropTarget: true 调用 renameCollection 命令,将 temp 集合重命名为目标集合。

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

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

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

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

$out 操作不更改以前的集合上存在的任何索引。如果聚合失败,$out 操作不会对预先存在的集合进行任何更改。

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

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

从 MongoDB4 开始。2 ,您可以为包含 阶段的聚合指定"majority" 读关注$out 级别 。

mongodump--oplog如果客户端在转储进程中发出包含 的聚合管道,则使用$out 将失败。有关更多信息,请参阅mongodump --oplog

限制
说明
聚合管道不能在事务中使用 $out
在7之前的 MongoDB 版本中。 0 。 3 ,聚合管道无法使用$out输出到时间序列集合。
不允许将 $out 阶段作为视图定义的一部分。如果视图定义包含嵌套管道(例如,视图定义包含 $lookup$facet 阶段),该 $out 阶段限制也适用于嵌套管道。
$lookup 阶段
您不能在 $lookup 阶段的嵌套管道中包含 $out 阶段。
$facet 阶段
$facet 阶段的嵌套管道不能包含 $out 阶段。
$unionWith 阶段
$unionWith 阶段的嵌套管道不能包含 $out 阶段。
"linearizable" 读关注 (read concern)

从 MongoDB4 开始。2 时,$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 集合中的数据进行透视,从而按作者对书名分组,然后将结果写入同样位于 test 数据库的 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 可以输出到与运行聚合的数据库不同的collection中的数据库。

以下聚合操作对 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(聚合)