Reputation: 3
This is how my Realm objects look:
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>()
}
I'm trying to fetch all meals that have some tags (I know I can filter all Realm objects of type Meal
for specific tags), but the goal is to fetch all Restaurant
objects and filter it's Meal
child objects based on tag values.
I tried filtering like this:
restaurants = realm.objects(Restaurant.self).filter("meals.@tags IN %@", selectedTags)
but this won't work. Is there a way to filter results based on values in child object list?
To clarify the question, this is an example how filtering should work
for selectedTags = [1, 2, 3]
This is the whole Restaurant
model that is saved in Realm.
[Restaurant {
name = "Foo"
meals = [
Meal {
mealName = "Meal 1"
tag = 1
},
Meal {
mealName = "Meal 2"
tag = 2
},
Meal {
mealName = "Meal 7"
tag = 7
}
]
}]
Filtering should return this:
[Restaurant {
name = "Foo"
meals = [
Meal {
mealName = "Meal 1"
tag = 1
},
Meal {
mealName = "Meal 2"
tag = 2
}
]
}]
Upvotes: 0
Views: 1250
Reputation: 35648
Here's one possible solution - add a reverse refererence to the restaurant for each meal object
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
}
then query the meals for that restaurant with the tags you want.
let results = realm.objects(Meal.self).filter("restaurant.name == %@ AND tag IN %@", "Foo", [1,2])
LinkingObjects could also be leveraged but it depends on what kind of queries will be needed and what the relationships are between Restaurants and Meals - I am assuming 1-Many in this case.
if you want ALL restaurants, then LinkingObjects is the way to go.
Edit:
Thought of another solution. This will work without adding a reference or an inverse relationship and will return an array of restaurants that have meals with the selected tags.
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
})
Upvotes: 0
Reputation: 1610
In short, you cannot do what you are asking. Not within a Realm query (and therefore benefit from update notifications if that is important) at least. No doubt you can make some kind of structure containing what you want though via non-Realm filtering.
To better answer, let's first consider what you're trying to produce as a query result. As you say, your attempt above won't work. But you're trying to filter Restaurant
s by having some Meal
s matching some criteria; this is probably achievable, but your resulting query on Restaurant
type would then produce a list of Restaurant
s. Each restaurant would still have a natural property of all its Meal
s, and would require the same filter applied again to the meals.
It makes sense though to add a function (if you need the search criteria to be dynamic, use a computed property if the filter is always the same tags) to the Restaurant
class that produces a view of its Meal
s matching your criteria.
e.g.
extension Restaurant
{
var importantMeals : Results<Meal>
{
return meals.filter(...)
}
}
So I think there are two options.
Restaurant
objects, and add it to a data structure of your own (Set or array) if its importantMeals
property is not empty. Then use the same property to produce the meal list when needed. Or you could use a non-Realm filter to produce that query for you. E.g. realm.objects(Restaurant.self).compactMap {$0}.filter { !$0.importantMeals.isEmpty }
Meal
s according to your criteria (realm.objects(Meal.self).filter(...)
). You could then add a LinkingObjects
property to your Meal
class to make the Set of Restaurant
s with relevant Meal
s.The correct approach will depend on how you want to use the results, but I'd suggest approach 1 will see you right. Note that you might want to sort the results produced by queries before using if order is of any importance to you (e.g. for displaying in UITableView
) as there is no guarantee that the order of objects will be the same for each query performed.
Upvotes: 0