基于子对象列表中的值的领域筛选结果

Realm filter results based on values in child object list

这是我的 Realm 对象的样子:

class Restaurant: Object {
    @objc dynamic var name: String? = nil
    let meals = List<Meal>()
}

class Meal: Object {
    @objc dynamic var mealName: String? = nil
    let tag = RealmOptional<Int>()
}

我正在尝试获取所有带有某些标签的餐点(我知道我可以针对特定标签过滤所有 Meal 类型的 Realm 对象),但目标是获取所有 Restaurant对象并根据标签值过滤它的 Meal 个子对象。

我试过这样过滤:

restaurants = realm.objects(Restaurant.self).filter("meals.@tags IN %@", selectedTags)

但这行不通。有没有一种方法可以根据子对象列表中的值过滤结果?

为了澄清问题,这是一个过滤应该如何工作的例子 selectedTags = [1, 2, 3] 这是保存在 Realm 中的整个 Restaurant 模型。

[Restaurant {
    name = "Foo"
    meals = [
        Meal {
            mealName = "Meal 1"
            tag = 1
        },
        Meal {
            mealName = "Meal 2"
            tag = 2
        },
        Meal {
            mealName = "Meal 7"
            tag = 7
        }
    ]
}]

过滤应该return这个:

[Restaurant {
    name = "Foo"
    meals = [
        Meal {
            mealName = "Meal 1"
            tag = 1
        },
        Meal {
            mealName = "Meal 2"
            tag = 2
        }
    ]
}]

简而言之,你无法做到你所要求的。至少不在 Realm 查询中(因此如果这很重要,则可以从更新通知中受益)。毫无疑问,您可以通过非 Realm 过滤来制作包含您想要的内容的某种结构。

为了更好地回答问题,让我们首先考虑您要生成的查询结果。正如你所说,你上面的尝试是行不通的。但是您正试图通过让一些 Meal 符合某些条件来过滤 Restaurant;这可能是可以实现的,但是您对 Restaurant 类型的结果查询将生成 Restaurant 的列表。每家餐厅仍将拥有其所有 Meal 的自然 属性,并且需要再次对餐点应用相同的过滤器。

尽管向 Restaurant class 生成符合您条件的 Meal 的视图。

例如

extension Restaurant
{
  var importantMeals : Results<Meal>
  {
    return meals.filter(...)
  }
}

所以我认为有两种选择。

  1. 遍历所有Restaurant对象,如果importantMeals属性不为空,则将其添加到您自己的数据结构(集合或数组)中。然后在需要时使用相同的 属性 生成餐单。或者您可以使用非 Realm 过滤器为您生成该查询。例如。 realm.objects(Restaurant.self).compactMap {[=20=]}.filter { ![=20=].importantMeals.isEmpty }
  2. 或者,根据您的条件 (realm.objects(Meal.self).filter(...)) 过滤所有 Meal。然后,您可以将 LinkingObjects 属性 添加到您的 Meal class 中,使 Restaurant 的集合具有相关的 Meal

正确的方法取决于您希望如何使用结果,但我建议方法 1 适合您。请注意,如果顺序对您很重要(例如,在 UITableView 中显示),您可能希望在使用之前对查询产生的结果进行排序,因为无法保证每个查询的对象顺序都相同执行。

这是一种可能的解决方案 - 为每个用餐对象添加对餐厅的反向引用

class Restaurant: Object {
    @objc dynamic var name: String? = nil
    let meals = List<Meal>()
}

class Meal: Object {
    @objc dynamic var mealName: String? = nil
    let tag = RealmOptional<Int>()
    @objc dynamic var restaurant: Restaurant?  //Add this
}

然后使用您想要的标签查询该餐厅的餐点。

let results = realm.objects(Meal.self).filter("restaurant.name == %@ AND tag IN %@", "Foo", [1,2])

也可以利用 LinkingObjects,但这取决于需要什么样的查询以及餐厅和膳食之间的关系 - 在这种情况下我假设是一对多。

如果您想要所有餐厅,那么 LinkingObjects 是您的不二之选。

编辑:

想到另一个解决办法。这将在不添加引用或反向关系的情况下工作,并且 return 一组使用所选标​​签用餐的餐馆。

let selectedTags = [1,2]
let results = realm.objects(Restaurant.self).filter( {
    for meal in [=12=].meals {
        if let thisTag = meal.tag.value { //optional so safely unwrap it
            if selectedTags.contains(thisTag) {
                return true //the tag for this meal was in the list, return true
            }
        } else {
            return false //tag was nil so return false
        }
    }
    return false
})