首页 > 解决方案 > 用于在 3 个具有关系的深层表中查询/添加记录的领域 Swift 代码

问题描述

我在 Realm 中创建了三个表,它们之间确定了关系(LBContests <-> LBQuizzes <-> LBLeaders),每个表都是多对多的关系。这是为了代表排行榜,其中“LBContests”中的“LBQuizzes”有“LBLeaders”,LBLeaders 包含用户名(为了简单起见,我省略了跟踪分数的其他字段详细信息)。

import Foundation
import RealmSwift

class LBContests: Object {
    @objc dynamic var contestName: String = ""
    let childrenLBQuizzes = List<LBQuizzes>()
}

class LBQuizzes: Object {
    @objc dynamic var quizName: String = ""
    let childrenLBLeaders = List<LBLeaders>()
    var parentContest = LinkingObjects(fromType: LBContests.self, property: "childrenLBQuizzes")
}

class LBLeaders: Object {
    @objc dynamic var userName: String = ""
    var parentQuizzes = LinkingObjects(fromType: LBQuizzes.self, property: "childrenLBLeaders")
}

知道了竞赛名称和测验名称后,我试图确定如何在 LBLeaders 中添加与 LBQuizzes 相关的用户名(使用已知的 quizName)并进一步与 LBContests 相关(使用已知的竞赛名称)。

我尝试过搜索、阅读、观看视频——但它们都是为了简单的一两级关系,我需要处理 3 级关系。我只能将 LBQuizzes 附加到 LBContests.childrenLBQuizzes,但无法找到一种方法将 LBLeaders 记录附加到具有子 LBQuizzes 记录及其 childrenLBLeaders 记录的 LBContests 记录... (对于已知的竞赛名称和测验名称)

我知道我的部分问题是我不完全理解差异以及何时使用对象、结果和列表...我可以执行过滤器来获取结果,但我需要一个对象来引用子关系和/或追加记录...

有人可以请:

  1. 识别 Swift Realm 代码以添加已知相关 LBQuizzes 和 LBContests 的 LBLeader 记录(用户名)

  2. 概述正确使用对象、结果和列表的方式、时间和原因

  3. 提供有关如何将对象、结果和列表一起使用或明确确定它们是否不能一起使用的见解(这是我的理解)-您从或正在过滤的对象中获得结果和/或列表,但它们不能从一个到另一个(即你不能像对象一样使用结果或列表)

标签: swiftrealmtable-relationships

解决方案


让我看看我能不能把它们绑在一起。

1) 你有一个已知的比赛和一个已知的测验,你想在测验中添加一个新的 LB 领导者。有几种方法可以解决这个问题,但这里有一个很长的答案:

首先,我设置了两个竞赛,竞赛 0 有两个测验,竞赛 1 有一个测验。将其打印到控制台会导致

contest 0
  quiz 0
  quiz 1
contest 1
  quiz 2

然后,假设我想添加一个新的 LBLeader,John,来参加比赛 0,测验 1。这是代码。

let knownContest = "contest 0"
let knownQuiz = "quiz 1"
let leaderToAdd = LBLeaders()
leaderToAdd.userName = "John"

let contestResults = realm.objects(LBContests.self).filter("contestName == %@", knownContest)
if let thisContest = contestResults.first {
    let quizResults = thisContest.childrenLBQuizzes.filter("quizName == %@", knownQuiz)
    if let thisQuiz = quizResults.first {
        try! realm.write {
            thisQuiz.childrenLBLeaders.append(leaderToAdd)
        }
    }
}

并且当打印相同的信息时,John 被添加到比赛 0、测验 1。

contest 0
  quiz 0
  quiz 1
    John
contest 1
  quiz 2

还有其他方法可以完成相同的事情(例如较短的答案)。例如,这一个班轮返回相同的测验并利用反向关系

let quizResults = realm.objects(LBQuizzes.self).filter("quizName == %@ AND ANY parentContest.contestName == %@", knownQuiz, knownContest)

2) 对象、结果和列表是三个不同的 Realm 概念,它们将在Realm 文档中进行介绍。

笼统地说:

Realm 对象是对象的单个实例,例如 Person 或 Dog。(有很多东西被认为是“领域对象”,但为简洁起见,我们将对其进行限制)

领域结果(对象)是从领域数据库返回的对象集。请记住,如我的第一个解决方案所示,首先返回了竞赛结果,但随后我可以在 quizResults 中获取这些对象的子集。此外,Realm 结果是实时更新的 - 它们保持“附加”到数据库,因此当对象进入或离开结果范围时,结果总是反映这一点。

列表是定义一对多关系的“容器”。与数组非常相似,并且有很多相同的功能。一个人有很多狗,所以这将是一个很好的使用列表,您似乎正在这样做。一个问题是 LinkingObjects 的反比关系。注意它的 LinkingObjects(复数),所以如果你利用它,请记住它也意味着狗可以有很多人。优点是它会在写入数据时自动创建这些关系,但缺点是它不是单一的逆关系,因此您必须进行相应的编码(在我的示例中使用 .first )。

3)巨大的问题,在 SO 的范围内并不能真正回答,因为需要大量的文档来解释所有这些交互。

一些值得思考的食物:

主键通常可以使事情变得更容易和更快,因为特定对象可以通过其唯一的主键来处理关系,而不是依赖于查询,在这种情况下,是测验名称。

例如,如果您的对象都有一个主键,那么在特定测验中添加一个新的领导者就变成了

class LBQuizzes: Object {
    @objc dynamic var quiz_id = UUID().uuidString

然后您可以通过其 quiz_id 获取该特定对象并添加领导者。

let quizId = //whatever the primary key is of this quiz object
let quizObject = realm.object(ofType: LBQuizzes.self, forPrimaryKey: quizId)
try! realm.write {
    quizObject?.childrenLBLeaders.append(leaderToAdd)
}

推荐阅读