Docs 菜单

Docs 主页Atlas App Services

进行重大模式更改

在此页面上

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

如果您需要对 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

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

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

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

管道将包含以下阶段:

  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 应用可以对客户端代码和同步对象架构进行重大变更

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

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

← 更新数据模型