进行重大模式更改
Overview
如果您需要对 Atlas Device 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" } } }
步骤
使用聚合管道初始化合作伙伴集合
由于无法直接对同步的对象模式执行中断性更改,因此,您必须使用包含所需更改的模式创建一个合作伙伴集合。您必须确保合作伙伴集合具有与原始集合相同的数据,以便新客户端可以与旧客户端进行同步。
将数据从原始集合复制到新合作集合的推荐方法是使用聚合框架。
您可以使用mongoshell /aggregation-pipeline-builder/ 或 /data-explorer/cloud-agg-pipeline/ 从 创建并运行聚合管道。
管道将包含以下阶段:
通过将空筛选器传递给$match 操作符来匹配初始集合中的所有文档。
使用聚合管道操作符修改初始集合的字段。 在以下示例中,使用$addFields 操作符转换数据。 使用$toString 操作符将
_id
字段转换为string
类型。使用$out操作符并指定合作集合名称,将转换后的数据写入合作集合。 在此示例中,我们将数据写入名为
TaskV2
的新集合。
此处的管道与 Atlas 和 Compass 用户界面中表示的管道相同。请注意,这两个工具提供更改预览;在本例中,将 _id
字段从 ObjectId 转换为字符串:
以下示例显示完整的聚合管道,就像您使用 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 ]);
添加触发器函数
触发器需要使用在触发器触发时运行的支持函数。此处,我们需要创建两个函数:正向迁移函数和反向迁移函数。
正向迁移触发器侦听 Task 集合中的插入、更新和删除,修改这些内容以反映 TaskV2 集合的模式,然后将它们应用于 TaskV2 集合。
要侦听对 TaskV2 集合的更改并将它们应用于 Task 集合,请为 TaskV2 集合的触发器编写反向迁移函数。反向迁移采用与上一步相同的思路。
在前向迁移函数中,我们检查哪个操作触发了该函数:如果操作类型为Delete
(意味着 Task集合中的文档已被删除),则 TaskV 2集合中的文档也会被删除。 如果操作类型是Write
(插入或修改)事件,则会创建聚合管道。 在管道中,使用$match操作符提取 Task集合中插入或修改的文档。 然后,将提取的文档转换为遵循TaskV2
集合的模式。 最后,使用$merge操作符将转换后的数据写入TaskV2
集合:
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
集合中:
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 应用可以对客户端代码和同步对象架构进行重大变更。
有关在开发模式中进行重大更改的详细信息,请参阅开发模式。
开发模式不适合生产使用。如果您使用开发模式,请确保在将应用程序迁移到生产环境之前将其禁用。