Как найти потерянные элементы списка в царстве - PullRequest
0 голосов
/ 02 мая 2020

Допустим, у меня есть схема Realm, в которой у меня есть родительский класс и дети под ним в List. Например:

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

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

Предположим, что со временем всякий раз, когда Kid удалялся, он удалялся только из объекта Parent и не удалялся из Царства:

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

Я знаю, что могу удалить kids из Королевства в той же транзакции записи, что и удаление из parent:

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

... но у меня есть причины этого не делать.

Есть ли способ запросить базу данных Realm для всех kids, которые не принадлежат parent? Например, если бы вы открыли мое Царство, вы могли бы видеть 100 kid объектов, но если вы посмотрите на родителей, только 5 из kid объектов фактически привязаны к parent объекту.

У меня есть особый случай использования Realm, когда я на самом деле не хочу удалять дочерние элементы List, если я не знаю, что у них нет родителя. Можно ли запросить Царство для всех без родителей kid с?

Ответы [ 2 ]

2 голосов
/ 02 мая 2020

Если вы не используете 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
    }
}
0 голосов
/ 02 мая 2020

Этот вопрос

Есть ли способ запросить базу данных 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)

Единственным недостатком этого является то, что я использовал Набор, чтобы вычесть детей, у которых есть родители. Это загрузило бы всех детей в память, минуя ленивый аспект загрузки царства. Другой вариант - просто удалить каждого ребенка, у которого есть родитель, из списка kidsToLook for realm.

...