在 MongoDB 领域同步中建模子集合

Modeling sub-collections in MongoDB Realm Sync

我是 MongoDB 和 MongoDB 领域同步的新手。我一直在关注 Realm Sync tutorial and Realm data model docs,但我想了解更多,所以我调整了 Atlas 集合结构如下。

Projects > Tasks // i.e. tasks is a sub-collection in each project.

我不知道如何提出可以支持 Atlas 子集合的 Realm Sync Schema。 我想到的最好的是一个模式,其中 Tasks 被建模为 Project 中的数组。但是,我担心对于任务很多的项目,这会达到 16MB(虽然很多!)文档限制。

{
  "bsonType": "object",
  "properties": {
    "_id": {
      "bsonType": "objectId"
    },
    "_partition": {
      "bsonType": "string"
    },
    "name": {
      "bsonType": "string"
    },
    "tasks": {
      "bsonType": "array",
      "items": {
          "bsonType": "object",
          "title": "Task",
          "properties": {
              "name": {
                "bsonType": "string"
              },
              "status": {
                "bsonType": "string"
              }
          }
      }
    }
  },
  "required": [
    "_id",
    "_partition",
    "name",
  ],
  "title": "Project"
}

期待如何以正确的方式对子集合建模。

编辑

这是我的客户端 Realm 模型。

import Foundation
import RealmSwift

class Project: Object {
    @objc dynamic var _id: String = ObjectId.generate().stringValue
    @objc dynamic var _partition: String = "" // user.id
    @objc dynamic var name: String = ""
    var tasks = RealmSwift.List<Task>()
    override static func primaryKey() -> String? {
        return "_id"
    }
}

class Task: EmbeddedObject {
    @objc dynamic var name: String = ""
    @objc dynamic var status: String = "Pending"
}

关于CRUD操作,我只是新建一个项目,读取已有项目如下。

// Read projects
realm.objects(Project.self).forEach { (project) in
   // Access fields     
}
        
// Create a new project
try! realm.write {
    realm.add(project)
}

您的代码看起来很棒,而且您的方向是正确的,所以这个答案更多的是关于建模的解释和建议,而不是硬代码。

首先,Realm 对象是 lazily loaded,这意味着它们仅在使用时加载。数以万计的对象对设备内存的影响很小。所以假设您有 10,000 个用户并且您 'load them all in'

let myTenThousandUsers = realm.objects(UserClass.self)

嗯,没什么大不了的。然而,这样做

let someFilteredUsers = myTenThousandUsers.filter { [=11=].blah == "blah" }

将(可能)造成问题 - 如果 returns 10,000 个用户 他们都加载到内存中 可能会使设备不堪重负。这是一个 Swift 函数和 'converting' 通常应避免使用 Swift 的领域惰性数据(取决于用例)

观察这段代码使用Swift.forEach

realm.objects(Project.self).forEach { (project) in
   // Access fields     
}

可能会导致问题,具体取决于对这些项目对象执行的操作 - 如果它们很多,将它们用作 tableView 数据源可能会很麻烦。

第二个是关于每个文档 16Mb 限制的问题。为清楚起见,Atlas 文档是这样的

{
   field1: value1,
   field2: value2,
   field3: value3,
   ...
   fieldN: valueN
}

其中值可以是任何 BSON 数据类型,例如其他文档、数组和文档数组。

在您的结构中,var tasks = RealmSwift.List<Task>() 其中任务是 embedded object。虽然概念上嵌入的对象是对象,但我相信它们计入单个文档限制,因为它们是嵌入的(如果我错了请纠正我);随着它们数量的增加,封闭文档的大小也会增加 - 请记住,16Mb 的文本是一个巨大的文本,因此 would/could 相当于每个项目数百万个任务。

简单的解决办法是不要嵌入它们,让它们独立存在。

class Task: Object {
    @objc dynamic var _id: String = ObjectId.generate().stringValue
    @objc dynamic var _partition: String = "" 
    @objc dynamic var name: String = ""
    @objc dynamic var status: String = "Pending"
    override static func primaryKey() -> String? {
        return "_id"
    }
}

那么每个可以16Mb,一个'unlimited number'可以关联一个项目。嵌入式对象的一个​​优点是一种级联删除,当父对象被删除时,子对象也被删除,但是从项目到任务是一对多的关系——删除一堆属于父对象的任务很容易。

哦 - 另一种不使用嵌入式对象的情况 - 特别是对于这个用例 - 它们不能具有索引属性。 Indexing 可以大大加快某些查询的速度。