Realm 中有没有办法为嵌套对象实现 order_by

Is there a way in Realm to implement order_by for nested objects

当我使用 realm.objects() 检索父对象时,我试图检索按特定顺序排序的嵌套对象。同样重要的是,如果修改了任何嵌套对象,请保持排序顺序。

我搜索了相关问题,但它们要么已过时,要么与我描述的问题不完全相同

这是一个示例:我在 Realm 中有“用户”,每个用户都有 'task'(为清楚起见,省略了无关字段)

class task: Object {
    @objc dynamic var priority = 10
}

class user: Object {
    @objc dynamic var name = ""
    let tasks = List<task>()
}

我创建了一些用户,然后为任何给定用户附加任务(table 在我的 UI 中,每个用户的部分和该用户的任务作为该用户部分中的行,已排序按优先级)。

默认优先级从 10 开始,可以随时在 1-10 的范围内更改。

当我检索用户时:

// (1)
users = realm.objects(user.self).sorted(byKeyPath: "name")

我想检索他们按优先级排序的任务。

请注意,在初始检索用户后修改了优先级(这意味着结果的任务对象的优先级在 realm.write() 下发生了变化)。为了澄清,在我的代码中的某处,对于给定用户的任务,我执行以下操作:

realm.write() {
    task1.priority = newPriority
?

这意味着用户的任务列表应该始终按优先级排序,不需要重复上面的 (1)。我无法对 user.tasks 属性 进行排序,因为它是一个 "let" 变量。

请注意,用户对象和任务对象都有排序顺序。

我可以在更新上面的 'priority' 之后对任务执行 index(remove/add) 但还没有尝试过。然而,与其手动执行此操作(并假设它有效,并假设没有 delete/add 或 'task' 与 reshuflle 一起发生,...),这不是 ORM 背后的全部想法, 让这些事情简单明了?

无论如何都要这样做?在 Realm 上有扩展吗? ???对替代方法与嵌套对象的建议持开放态度。

很好的问题和真正展示 Realms 实时更新能力的答案。

重述问题

A user has tasks and we want to work with those tasks as an ordered list, and if there's a change to the order, keep them ordered by the new order

使用问题中出现的两个 classes,我们有一个按钮调用一个函数来查询用户的领域并将该用户存储在 class var

var myUser: UserClass!

func loadUser() {
    if let realm = gGetRealm() { //my code to connect to Realm
        let userResults = realm.objects(UserClass.self)
        if userResults.count > 0 {
            let user = userResults[0]
            self.myUser = user
        }
    }
}

然后是一个调用函数的按钮,可以简单地打印出按优先级排序的用户任务

func printOrderedTasks() {
    let sortedTasks = self.myUser.tasks.sorted(byKeyPath: "priority")
    for task in sortedTasks {
        print(task)
    }
}

所以对于这个例子,我创建了 4 个任务,并将它们添加到用户任务列表并将用户写入领域,优先级初始顺序是:10、0、1、4

然后 loadUser 加载并存储第一个可用的用户并将其分配给 class 变量。

printOrderedTasks 按升序输出任务。所以加载用户后,点击 printOrderedTasks 输出是

0
1
4
10

然后,使用 Realm Studio,将 1 更改为 6 并再次单击 printOrderedTasks,输出为

0
4
6
10

无需重新加载任何内容。

领域对象是实时更新的,所以只要您在代码中引用了它们,任何更改都会实时反映出来。

您可以通过向该对象添加一个观察者来对此进行扩展,Realm 将通知该事件的应用程序,您可以向该事件重新加载 tableView 或让用户知道更改。

编辑:

更进一步,用户任务也是实时更新对象,如果您对它们设置排序,这些结果将保持它们的排序。

例如,让我们re-write上面的代码来跟踪维护实时排序的用户任务。我已经 re-written 上面的代码并删除了用户 class var 并添加了任务 class var。请注意,我们永远不需要 re-sort 任务,排序顺序是初始设置的,从那时起它们将保持排序。

var myUserTasks: Results<TaskClass>!

func loadUserAndGetTasks() {
    if let realm = gGetRealm() {
        let userResults = realm.objects(UserClass.self)
        if userResults.count > 0 {
            let user = userResults[0]
            self.myUserTasks = user.tasks.sorted(byKeyPath: "priority")
        }
    }
}

func printTasks() {
    for t in self.myUserTasks {
        print(t)
    }
}

初始顺序如上10、0、1、4。如果我们然后使用 Realm Studio 将 1 更改为 6,然后 运行 printTasks 函数,您会看到排序是自动完成的,因为结果是实时更新的。

0
4
6
10

这里很酷的一点是您不需要继续执行任务 - 它们会保持排序顺序。

@Jay 的回答一如既往的好,但我会采纳他的建议并将其作为用户对象的固有内容。这是我 'usual' 处理此类要求的方式。

正如您所说,user 对象 tasks 属性 是一个 let,但这是定义 Realm 对象的方式。这并不是说您不能添加自己的计算属性来控制对对象的访问。

如果您只想将任务视为有序列表,则添加一个 属性 即可,例如

extension user
{
  var orderedTasks: Results<task>
  {
    return tasks.sorted(byKeyPath: "priority")
  }
}

然后总是使用 属性 来访问任务。使用扩展有我自己的风格 - 我只是将数据声明保留在 class 声明中,然后添加一个具有任何计算属性或函数的扩展 - 但你可以直接将声明添加到 class ]定义。