【问题标题】:How to Find Orphaned List Items in Realm如何在 Realm 中查找孤立的列表项
【发布时间】:2020-05-02 00:53:10
【问题描述】:

假设我有一个 Realm 架构,其中我有一个父类和子类,在 List 中。像这样:

class Parent: Object{
  @objc dynamic var name = ""
  let kids = List<Kid>()
}

class Kid: Object{
  @objc dynamic var name = ""
}

假设随着时间的推移,每当删除 Kid 时,它只会从 Parent 对象中删除,而不会从 Realm 中删除:

let realm = try! Realm()
let parent = realm.objects(Parent.self)

realm.beginWrite() 

for kid in parent.kids{
  if let index = parent.kids.index(of: kid){
    parent.kids.remove(at: index)
  }
}

try! realm.commitWrite()

我知道我可以在与从parent 中删除相同的写入事务中从领域中删除kids

let kids = parent.kids
realm.delete(kids)

...但我有理由不这样做。

有没有办法在 Realm 数据库中查询所有不属于 parentkids?例如,如果您要打开我的 Realm,您可以看到 100 kid 对象,但如果您查看父对象,则只有 kid 对象中的 5 实际附加到 parent 对象。

我有一个 Realm 的特殊用例,我实际上不想删除子 List 项目,除非我知道它们没有父项。是否可以查询所有无父kids 的领域?

【问题讨论】:

  • 这很简单,所以我添加了另一个答案。

标签: realm realm-list


【解决方案1】:

除非您使用LinkingObjects,否则无法直接查询没有父母的孩子的领域。如果现在更改您的数据模型还不算太晚,那么我建议您使用它们。

class Parent: Object{
  @objc dynamic var name = ""
  let kids = List<Kid>()
}

class Kid: Object{
  @objc dynamic var name = ""
  let parents = LinkingObjects(fromType: Parent.self, property: "kids")
}

当您将Kid 添加到Parent.kids 领域时,会自动为您处理Kid.parents 关系。当您删除Parent 时,Kid.parents 将为空(假设Kid 只有一个Parent)。 LinkingObjects 的优点在于您可以将它们合并到您的查询中。因此,要查找所有没有父对象的 Kid 对象,查询将是:

func fetchKidsWithoutParents1() -> Results<Kid> {
    let realm = try! Realm()
    return realm.objects(Kid.self).filter("parents.@count == 0")
}

如果不使用LinkingObjects,则必须查询所有Kid 对象和所有Parent 对象,并查看Kid 是否存在于任何Parent.kids List 中。这种方法有两个缺点。第一个是所有Kid 对象将在您手动过滤它们时加载到内存中。另一个是您不能利用领域通知,因为生成的孩子不会存储在领域Result 对象中。请注意,以下示例假定不存在两个具有相同名称的 Kid 对象:

func fetchKidsWithoutParents2() -> [Kid] {
    let realm = try! Realm()
    let kids = realm.objects(Kid.self)
    let parents = realm.objects(Parent.self)
    return kids.filter { kid in
        parents.filter("SUBQUERY(kids, $kid, $kid.name == %@).@count > 0", kid.name).count == 0
    }
}

【讨论】:

  • 太疯狂了,我刚刚得到了同样的解决方案,然后来到这里寻找我正在做的确切事情。 :) 我添加了LinkingObjects 来完成此操作(这样的更改甚至不需要领域迁移)所以看起来我正在走上正轨。非常感谢你! :)
  • @CliftonLabrum 我喜欢这个答案,但它并不完全准确。这没有办法直接查询没有父母的孩子的领域是不正确的。您可以轻松找到没有父母的孩子,就好像您查询他们结果的计数将为 0。我在答案中展示了一个示例。话虽如此 - 这仍然是一个很好的答案和解决方案。
  • @Jay,也许我的措辞可以更准确一些。最初的问题是是否可以为所有没有父母的孩子查询一个领域?我解释查询领域的方式是为所有孩子运行一个查询没有父母的孩子。这就是我所说的直接。除非您使用 LinkingObjects,否则您必须查询所有孩子,然后遍历每个孩子并过滤没有父母的孩子(有多种方法可以做到这一点,例如下面的解决方案)。也许我应该说您可以查询没有父母的孩子,但您必须进行一些手动过滤。
  • 明白了。那里的问题有点含糊,但我认为您对此的看法可能是准确的,而 LinkingObjects 是要走的路。我在我的问题中添加了一个编辑,因此可以“使用单个查询”完成(而不是使用多个查询进行迭代),然后依靠 Swift(和 RAM)来获得最终结果。
【解决方案2】:

这个问题是

有没有办法为所有不知道的孩子查询 Realm 数据库 属于父母?

当然!一行代码超级简单。

let results = realm.objects(Parent.self).filter("ANY kids == %@", thisKid)

当然,评估看看有没有结果

if results.count == 0 {
    print("kid: \(thisKid?.name) has no parents.")
} else {
    print("found parents for kid: \(thisKid?.name)")
    for kid in results {
        print(kid)
    }
}

编辑:

在上面的代码中,您可以遍历每个孩子并查看它是否有父级,如果没有则删除它。作为一个挑战,一些代码将删除所有没有父母的孩子而不是迭代如何。 allKids 是一个领域列表。

let foundParents = realm.objects(Parent.self).filter("ANY kids IN %@", allKids)
let kidsThatHaveParents: [Kid] = allKids.compactMap { kid in
    let x = foundParents.first { $0.kids.contains( kid ) }
    if x != nil { //this is just for clarity, could be shortened
        return kid
    }
    return nil
}

let haveParentSet = Set(kidsThatHaveParents)
let kidsToCheckSet = Set(allKids)

let kidsToRemove = kidsToCheckSet.subtracting(haveParentSet)

这样做的一个缺点是我使用 Set 来减去有父母的孩子。这会将所有孩子加载到内存中,绕过领域的延迟加载方面。另一种选择是从 kidsToLook for realm 列表中删除每个有父母的孩子。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-18
    • 1970-01-01
    • 2010-11-15
    • 1970-01-01
    • 2022-08-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多