基于子对象列表中的值的领域过滤器结果

时间:2019-04-06 23:20:34

标签: swift realm

这是我的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
        }
    ]
}]

过滤应返回以下内容:

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

2 个答案:

答案 0 :(得分:0)

简而言之,您无法做自己要问的事情。至少不在Realm查询之内(因此,如果重要的话,可以从更新通知中受益)。毫无疑问,您可以通过非Realm过滤来构建某种包含所需内容的结构。

为了获得更好的答案,我们首先考虑一下您要作为查询结果生成的内容。正如您所说,您的上述尝试将无效。但是,您正在尝试通过让一些Restaurant与某些条件匹配来过滤Meal。这可能是可以实现的,但是您对Restaurant类型的查询将产生Restaurant的列表。每家餐厅仍将拥有其所有Meal的自然属性,并且需要再次对餐点应用相同的过滤器。

Restaurant类中添加一个函数(如果您需要动态的搜索条件,如果过滤器始终是相同的标签,则使用计算属性)是有意义的,该类生成其{符合您条件的{1}}。

例如

Meal

所以我认为有两种选择。

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

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

答案 1 :(得分:0)

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

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,但它取决于需要哪种查询以及饭店和餐饮之间的关系-在这种情况下,我假设为1-Many。

如果您想要所有餐厅,那么LinkingObjects是必经之路。

编辑:

另一个解决方案的想法。这将在不添加参考或逆关系的情况下起作用,并且将返回一系列带有选定标签的用餐餐厅。

let selectedTags = [1,2]
let results = realm.objects(Restaurant.self).filter( {
    for meal in $0.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
})