Tony Macaren
Tony Macaren

Reputation: 457

Realm Swift select all values which match one field in one-to-many relationship entity

I have two entities in realm Product and ProductType. Product has one to many relation with Product Type.

Product entity one to many

import Foundation
import RealmSwift

class Product: Object{
    dynamic var productName = ""
    let productTypeList = List<ProductType>() 
}

ProductType entity

import Foundation
import RealmSwift

class ProductType: Object{   
  dynamic var typeName: String = ""
  dynamic var typeLogoUrl: String = ""
}

My goal is select all products, which contains ProductType typeName, for example "Electronics". I can do it easily, if there were one to one relationship like this

Product entity one to one

import Foundation
import RealmSwift

class Product:Object{
    dynamic var productName = ""
    dynamic var productType : ProductType? 
}

Query example

let realm = try! Realm()
let productTypeName = "Electronics"
let predicate = NSPredicate(format: "productType.typeName == %@", productTypeName)
let rmProducts = realm.objects(Product.self).filter(predicate)

Any ideas how to make query with one to many version of Product class?

Upvotes: 3

Views: 2517

Answers (2)

David Pasztor
David Pasztor

Reputation: 54805

You can use a subquery and check that the count of the productTypes in the list matching the name is bigger than 0.

let rmProducts = realm.objects(Product.self).filter("SUBQUERY(productTypeList, $type, $type.typeName == %@).@count>0",productTypeName)

I have run the query on the following test set and got the expected results:

class Product:Object{
    dynamic var productName = ""
    var productTypeList = List<ProductType>()
}

class ProductType  : Object{
    dynamic var typeName: String = ""
    dynamic var typeLogoUrl: String = ""
}

let types = [ProductType(value: ["typeName":"Electronics","typeLogoUrl":"url"]),ProductType(value: ["typeName":"a","typeLogoUrl":"url"]),ProductType(value: ["typeName":"Electronics","typeLogoUrl":"a"]),ProductType(value: ["typeName":"b","typeLogoUrl":"url"])]

let prod1 = Product()
prod1.productName = "a"
prod1.productTypeList = List([types[0],types[1]])
let prod2 = Product()
prod2.productName = "b"
prod2.productTypeList = List([types[3],types[1]])
let prod3 = Product()
prod3.productName = "c"
prod3.productTypeList = List([types[2],types[1]])
var prod4 = Product()
prod4.productName = "d"
prod4.productTypeList = List([types[1]])

try! realm.write {
    realm.add(types)
    realm.add([prod1,prod2,prod3,prod4])
}

let productTypeName = "Electronics"
let predicate = NSPredicate(format: "productType.typeName == %@", productTypeName)
let rmProducts = realm.objects(Product.self).filter("SUBQUERY(productTypeList, $type, $type.typeName == %@).@count>0",productTypeName)
print(rmProducts) //rmProducts contains prod1 and prod3 as expected

Upvotes: 3

bdash
bdash

Reputation: 18308

For a simple query like this, you want the ANY modifier:

realm.objects(Product.self).filter("ANY productTypeList.typeName == %@", productTypeName)

A SUBQUERY, as suggested in another answer, is only necessary if you need more than one criteria on each sub-object to match. For instance, if you wanted to find Products that have items of a given type whose URLs start with https:// you would use a SUBQUERY:

realm.objects(Product.self).filter("SUBQUERY(productTypeList, $type, $type.typeName == %@ AND $type.typeLogoUrl BEGINSWITH 'https://').@count > 0", productTypeName)

Upvotes: 3

Related Questions