如何在 Realm 中查找孤立的列表项

How to Find Orphaned List Items in Realm

假设我有一个 Realm 架构,其中有一个 parent class 和 List 下面的孩子。像这样:

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

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

假设,随着时间的推移,每当 Kid 被删除时,它只会从 Parent object 中删除,而不会从 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 中删除相同的写入事务中从 Realm 中删除 kids:

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

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

有没有办法在 Realm 数据库中查询不属于 parent 的所有 kids?例如,如果你打开我的 Realm,你可以看到 100 kid object,但是如果你查看 parent,只有 5kid object 实际上附加到 parent object.

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

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

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 object 没有 parents 的查询将是:

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

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

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
    }
}

这个问题是

Is there a way to query the Realm database for all kids that don't belong to a parent?

好的!一行代码超级简单

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)
    }
}

编辑:

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

let foundParents = realm.objects(Parent.self).filter("ANY kids IN %@", allKids)
let kidsThatHaveParents: [Kid] = allKids.compactMap { kid in
    let x = foundParents.first { [=12=].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)

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