Docs 菜单
Docs 主页
/ /
Atlas App Services
/ /

进行重大模式更改

在此页面上

  • Overview
  • 合作伙伴集合
  • 步骤
  • 使用聚合管道初始化合作伙伴集合
  • 为合作伙伴集合设置数据库触发器
  • 添加触发器函数
  • 开发模式和破坏性更改

如果您需要对 Atlas Device Sync 中使用的对象模式进行更改,您可以进行非中断性更改,则无需任何额外的工作。不过,中断性更改需要执行额外的步骤。中断性或破坏性更改包括重命名现有字段或更改字段的数据类型。

有关详细信息,请参阅更新数据模型。

如果需要进行中断性模式更改,您有两种选择:

  • 在后端终止 Sync,然后从头开始重新启用 Sync。

  • 创建一个合作集合,将旧数据复制到这个新集合中,并设立触发器以确保数据一致性。

    本指南的其余部分将指导您创建合作伙伴集合。

警告

终止同步后恢复同步

当您终止并重新启用 Atlas Device Sync 时,客户端将无法再同步。您的客户端必须执行客户端重置处理程序才能恢复同步。此处理程序可以放弃或尝试恢复未同步的更改。

在以下过程中,初始 集合将下面的 JSON 模式用于 Task 集合。请注意,Task 的模式包含 objectId 类型的 _id 字段:

任务模式
{
"title": "Task",
"bsonType": "object",
"required": [
"_id",
"name"
],
"properties": {
"_id": {
"bsonType": "objectId"
},
"_partition": {
"bsonType": "string"
},
"name": {
"bsonType": "string"
}
}
}

模式是相同的,只是我们希望 _id 字段是字符串:

任务模式
{
"title": "Task",
"bsonType": "object",
"required": [
"_id",
"name"
],
"properties": {
"_id": {
"bsonType": "string"
},
"_partition": {
"bsonType": "string"
},
"name": {
"bsonType": "string"
}
}
}
1

由于无法直接对同步的对象模式执行中断性更改,因此,您必须使用包含所需更改的模式创建一个合作伙伴集合。您必须确保合作伙伴集合具有与原始集合相同的数据,以便新客户端可以与旧客户端进行同步。

将数据从原始集合复制到新合作集合的推荐方法是使用聚合框架。

您可以使用mongoshell /aggregation-pipeline-builder/ /data-explorer/cloud-agg-pipeline/ 从 创建并运行聚合管道。

管道将包含以下阶段:

  1. 通过将空筛选器传递给$match 操作符来匹配初始集合中的所有文档。

  2. 使用聚合管道操作符修改初始集合的字段。 在以下示例中,使用$addFields 操作符转换数据。 使用$toString 操作符_id字段转换为string类型。

  3. 使用$out操作符并指定合作集合名称,将转换后的数据写入合作集合。 在此示例中,我们将数据写入名为TaskV2的新集合。

此处的管道与 Atlas 和 Compass 用户界面中表示的管道相同。请注意,这两个工具提供更改预览;在本例中,将 _id 字段从 ObjectId 转换为字符串:

聚合构建器的 Atlas 用户界面

以下示例显示完整的聚合管道,就像您使用 mongosh 进行转换一样:

匹配初始集合中的所有文档,并将它们输出到合作伙伴集合
use "<database-name>" // switch the current db to the db that the Task collection is stored in
collection = db.Task;
collection.aggregate([
{ $match: {} }, // match all documents in the Task collection
{
$addFields: { // transform the data
_id: { $toString: "$_id" }, // change the _id field of the data to a string type
},
},
{ $out: "TaskV2" }, // output the data to a partner collection, TaskV2
]);
2

在设置合作伙伴集合后,您可以使用该集合读取现有的数据。不过,对任一集合 的数据的任何新写入不会写入到另一个集合中。这会导致旧客户端与新客户端不同步。

要确保同时在两个集合中反映数据,您可以在每个集合上设置一个数据库触发器。在将数据写入到一个集合时,触发器的函数执行写入到伙伴集合的操作。

按照数据库触发器文档中的步骤创建一个触发器,对于所有操作类型,将 Task 集合中的数据复制到 TaskV2 集合。重复这些步骤以创建第二个触发器,将 TaskV2 集合中的数据复制到 Task 集合。

3

触发器需要使用在触发器触发时运行的支持函数。此处,我们需要创建两个函数:正向迁移函数和反向迁移函数。

正向迁移触发器侦听 Task 集合中的插入、更新和删除,修改这些内容以反映 TaskV2 集合的模式,然后将它们应用于 TaskV2 集合。

要侦听对 TaskV2 集合的更改并将它们应用于 Task 集合,请为 TaskV2 集合的触发器编写反向迁移函数。反向迁移采用与上一步相同的思路。

在前向迁移函数中,我们检查哪个操作触发了该函数:如果操作类型为Delete (意味着 Task集合中的文档已被删除),则 TaskV 2集合中的文档也会被删除。 如果操作类型是Write (插入或修改)事件,则会创建聚合管道。 在管道中,使用$match操作符提取 Task集合中插入或修改的文档。 然后,将提取的文档转换为遵循TaskV2集合的模式。 最后,使用$merge操作符将转换后的数据写入TaskV2集合:

copyTaskObjectToTaskV2 函数
exports = function (changeEvent) {
const db = context.services.get("mongodb-atlas").db("ExampleDB");
const collection = db.collection("Task");
// If the event type is "invalidate", the next const throws an error.
// Return early to avoid this.
if (!changeEvent.documentKey) { return; }
// The changed document's _id as an integer:
const changedDocId = changeEvent.documentKey._id;
// If a document in the Task collection has been deleted,
// delete the equivalent object in the TaskV2 collection:
if (changeEvent.operationType === "delete") {
const tasksV2Collection = db.collection("TaskV2");
// Convert the deleted document's _id to a string value
// to match TaskV2's schema:
const deletedDocumentID = changedDocId.toString();
return tasksV2Collection.deleteOne({ _id: deletedDocumentID })
}
// A document in the Task collection has been created,
// modified, or replaced, so create a pipeline to handle the change:
const pipeline = [
// Find the changed document data in the Task collection:
{ $match: { _id: changeEvent.documentKey._id } },
{
// Transform the document by changing the _id field to a string:
$addFields: {
_id: { $toString: "$_id" },
},
},
// Insert the document into TaskV2, using the $merge operator
// to avoid overwriting the existing data in TaskV2:
{ $merge: "TaskV2" }]
return collection.aggregate(pipeline);
};

反向迁移函数执行的步骤与上一步中的示例类似。如果在一个集合中删除某个文档,还会在另一个集合中删除该文档。如果操作类型是写入事件,则会提取 TaskV2 中的更改的文档,进行转换以与 Task 集合模式匹配,然后将其写入到 Task 集合中:

copyTaskV2ObjectToTask 函数
exports = function (changeEvent) {
const db = context.services.get("mongodb-atlas").db("ExampleDB");
const collection = db.collection("TaskV2");
// If the event type is "invalidate", the next const throws an error.
// Return early to avoid this.
if (!changeEvent.documentKey) { return; }
// The changed document's _id as a string:
const changedDocId = changeEvent.documentKey._id;
// If a document in the TaskV2 collection has been deleted,
// delete the equivalent object in the Task collection
if (changeEvent.operationType === "delete") {
const taskCollection = db.collection("Task");
// Convert the deleted document's _id to an integer value
// to match Task's schema:
const deletedDocumentID = parseInt(changedDocId);
return taskCollection.deleteOne({ _id: deletedDocumentID })
}
// A document in the Task collection has been created,
// modified, or replaced, so create a pipeline to handle the change:
const pipeline = [
// Find the changed document data in the Task collection
{ $match: { _id: changedDocId } },
{
// Transform the document by changing the _id field
$addFields: {
_id: { $toInt: "$_id" },
},
},
{ $merge: "Task" }
]
return collection.aggregate(pipeline);
};

适用于 2023 年 9 月 13 日之后创建的 App Services 应用程序。

2023 年 9 月 13 日之后创建的处于开发模式的 App Services 应用可以对客户端代码和同步对象架构进行重大变更

有关在开发模式中进行重大更改的详细信息,请参阅开发模式。

开发模式不适合生产使用。如果您使用开发模式,请确保在将应用程序迁移到生产环境之前将其禁用。

后退

更新数据模型