Realm Filter nested Array

201 views Asked by At

I have three object

class YearDatabaseModel: Object {
    @Persisted var year: Int
    @Persisted var months = List<MonthsDatabaseModel>()

    override static func primaryKey() -> String? {
        return "year"
    }
}
final class MonthsDatabaseModel: Object {
    @Persisted var id: String
    @Persisted var media = List<Model>()

    override static func primaryKey() -> String? {
        return "id"
    }
}
public class Model: Object {
    @Persisted public var id: String
    @Persisted public var type: String
}

I need to return all the Year models with filter nested Month. Model items with their type for example just return models items in month with type == "audio"

I did use this SUBQUERY but it just filter the year list not filter the model list

 let predicate = NSPredicate(
                format: "SUBQUERY(months.media, $media, $media.type = %@).@count > 0",
 "audio")

And I don't want to get the realm result then filter models by flat mapping the list of models I want to use realm query power to filter neseted objects from big model

Here's a diagram

enter image description here

1

There are 1 answers

7
Jay On

This is a very challenging query due to the depth and dealing with Lists, but the subquery is likely the best solution unless you want to leverage high-level Swift functions.

I think this will get your result but it's a two part query. As is, the current SwiftSDK doesn't have a (good) implementation for subqueries per se so I'm using an old school NSPredicate combined with the newer type safe query to get the result

let predicate = NSPredicate(format: "SUBQUERY(media, $m, $m.type == 'm0 type 0').@count > 0")
let months = realm.objects(MonthsDatabaseModel.self).filter(predicate)

let years = realm.objects(YearDatabaseModel.self).where { $0.months.containsAny(in: months) }
print(years)

We use the subquery to get all of the months MonthsDatabaseModel that have a Model with type = "audio"

Then we use those results to get the YearDatabaseModel that have months in their list that match.

Edit:

Here's a sample set of data (that matches the diagram in the question, noting there were two Year 2's so I used 2022 and 2023)

year 2021
 month:  year 2021 month 1
  media: video
  media: image
  media: video
 month:  year 2021 month 2
  media: image
  media: image
  media: image
 month:  year 2021 month 3
  media: video
  media: image
  media: video

year 2022  (note, no video media values in the child list)
 month:  year 2022 month 1
  media: image
  media: image
  media: image

year 2023
 month:  year 2023 month 1
  media: video
  media: image
  media: image

and when the code above is run against that data this it returns year 2021 and year 2023, omitting year 2022 because it does not have any video child values in the media List

year 2021
year 2023