CRUD - 読み取り - Swift SDK
項目一覧
- Realm からの読み取り
- 読み取り特性
- クエリ結果の制限
- ページ分割
- Realm オブジェクトの読み取り
- このページの例について
- プライマリキーによる特定のオブジェクトの検索
- 特定の型のすべてのオブジェクトのクエリ
- オブジェクト プロパティに基づいてクエリをフィルタリングする
- オブジェクト ID プロパティでフィルタリング
- 関係のクエリ
- 逆関係のクエリ
- 埋め込みオブジェクト プロパティに対するコレクションのクエリ
- マップ プロパティのクエリ
- MutableSet プロパティのクエリ
- RealmValue プロパティの読み取りとクエリ
- 地理空間データのクエリ
- カスタム永続プロパティのクエリ
- オブジェクトの非同期な読み取り
- クエリ結果のソート
- セクション クエリ結果
- データを集計する
- チェーン クエリ
- クエリ クラスのプロジェクション
Realm からの読み取り
Realm からの読み取りは通常、次の手順で構成されます。
Realm から特定のタイプのすべてのオブジェクトを取得します。
必要に応じて、結果をフィルタリングします。
オプションで、 は結果を並べ替えます。
あるいは、特定のタイプのすべてのオブジェクトを取得し、セクション に分割します。 通常の結果と同様に、セクションされた結果をフィルタリングおよびソートすることができます。
クエリ、フィルタリング、ソート操作は、結果または SessionedResults コレクションのいずれかを返します。 これらのコレクションはライブです。つまり、関連付けられたクエリの最新の結果が常に含まれています。
読み取り特性
Realm での読み取りに関する 3 つの主要な特性に基づいてアプリのデータ アクセス パターンを設計すると、データをできるだけ効率的に読み取ることができます。
結果はコピーではない
クエリへの結果はデータのコピーではありません。クエリの結果を変更すると、ディスク上のデータが直接変更されます。 このメモリ マッピングは、結果がライブであることも意味します。つまり、ディスクの現在の状態を常に反映します。
結果は遅延
Realm は、そのクエリの結果を実際に要求した場合にのみクエリを実行します。 この遅延評価により、大規模なデータセットや複雑なクエリを処理するための洗練された高性能なコードを書込むことができます。 中間状態を処理するために余計な作業を必要とせずに、複数のフィルターやソート操作を連鎖させることができます。
参照は保持されます
Realm のオブジェクトモデルの利点の 1 つは、Realm がオブジェクトのすべての関係を直接参照として自動的に保持することであるため、クエリの結果から関係のグラフを直接走査できることです。
直接参照(ポインター)を使用すると、参照を介して関連オブジェクトのプロパティに直接アクセスできます。
他のデータベースでは通常、オブジェクトを直接操作する必要がある場合、データベース ストレージからアプリケーション メモリにオブジェクトがコピーされます。 Because application objects contain direct references, you are left with a choice: copy the object referred to by each direct reference out of the database in case it's needed, or just copy the foreign key for each object and query for the object with that key if it's accessed. 参照されたオブジェクトをアプリケーション メモリにコピーすることを選択すると、アクセスされていないオブジェクトの多くのリソースを使用することができますが、外部キーのみをコピーすることを選択した場合は、参照されたオブジェクトの検索によってアプリケーションが遅くなる可能性があります。
Realm は、ゼロコピーのライブ オブジェクトを使用して、これをすべてバイパスします。 Realm オブジェクト アアクセスはメモリ マッピングを使用してデータベース ストレージを直接指すため、Realm 内のオブジェクトとアプリケーション メモリ内のクエリの結果を区別しません。 このため、どのクエリ結果からでも、Realm 全体にわたる直接参照を走査できます。
クエリ結果の制限
遅延評価の結果、Realm でクエリ結果を制限するために特別なメカニズムは必要ありません。 たとえば、クエリが数千のオブジェクトと一致するが、最初の 10 個のオブジェクトのみをロードする場合は、結果コレクションの最初の 10 個の要素のみにアクセスします。
ページ分割
遅延評価の代わりに、ページ区切りの一般的なタスクが非常に簡単になります。 たとえば、Realm 内の数千のオブジェクトに一致するクエリに関連付けられた結果コレクションがあるとします。 ページごとに 100 個のオブジェクトを表示します。 任意のページに進むには、ターゲット ページに対応するインデックスから、結果コレクションの要素にアクセスします。
Realm オブジェクトの読み取り
このページの例について
このページの例では、次のモデルを使用します。
// DogToy.h @interface DogToy : RLMObject @property NSString *name; @end // Dog.h @interface Dog : RLMObject @property NSString *name; @property int age; @property NSString *color; // To-one relationship @property DogToy *favoriteToy; @end // Enable Dog for use in RLMArray RLM_COLLECTION_TYPE(Dog) // Person.h // A person has a primary key ID, a collection of dogs, and can be a member of multiple clubs. @interface Person : RLMObject @property int _id; @property NSString *name; // To-many relationship - a person can have many dogs @property RLMArray<Dog *><Dog> *dogs; // Inverse relationship - a person can be a member of many clubs @property (readonly) RLMLinkingObjects *clubs; @end RLM_COLLECTION_TYPE(Person) // DogClub.h @interface DogClub : RLMObject @property NSString *name; @property RLMArray<Person *><Person> *members; @end // Dog.m @implementation Dog @end // DogToy.m @implementation DogToy @end // Person.m @implementation Person // Define the primary key for the class + (NSString *)primaryKey { return @"_id"; } // Define the inverse relationship to dog clubs + (NSDictionary *)linkingObjectsProperties { return @{ @"clubs": [RLMPropertyDescriptor descriptorWithClass:DogClub.class propertyName:@"members"], }; } @end // DogClub.m @implementation DogClub @end
class DogToy: Object { var id: ObjectId var name = "" } class Dog: Object { var name = "" var age = 0 var color = "" var currentCity = "" var citiesVisited: MutableSet<String> var companion: AnyRealmValue // To-one relationship var favoriteToy: DogToy? // Map of city name -> favorite park in that city var favoriteParksByCity: Map<String, String> // Computed variable that is not persisted, but only // used to section query results. var firstLetter: String { return name.first.map(String.init(_:)) ?? "" } } class Person: Object { true) var id = 0 (primaryKey: var name = "" // To-many relationship - a person can have many dogs var dogs: List<Dog> // Inverse relationship - a person can be a member of many clubs "members") var clubs: LinkingObjects<DogClub> (originProperty: // Embed a single object. // Embedded object properties must be marked optional. var address: Address? convenience init(name: String, address: Address) { self.init() self.name = name self.address = address } } class DogClub: Object { var name = "" var members: List<Person> // DogClub has an array of regional office addresses. // These are embedded objects. var regionalOfficeAddresses: List<Address> convenience init(name: String, addresses: [Address]) { self.init() self.name = name self.regionalOfficeAddresses.append(objectsIn: addresses) } } class Address: EmbeddedObject { var street: String? var city: String? var country: String? var postalCode: String? }
プライマリキーによる特定のオブジェクトの検索
特定のオブジェクトのプライマリキーがわかっている場合は、 +[RLObjectForPrimaryKey:] を使用して直接検索できます。
// Get a specific person from the default realm Person *specificPerson = [Person objectForPrimaryKey:@12345];
特定のオブジェクトのプライマリキーがわかっている場合は、 Realm.object(OfType:forPrimaryKey:) を使用して直接検索できます。
let realm = try! Realm() let specificPerson = realm.object(ofType: Person.self, forPrimaryKey: 12345)
特定の型のすべてのオブジェクトのクエリ
Realm 内の特定のタイプのオブジェクトをクエリするには、Realm インスタンスを+[ YourRealmObjectClass allObjectsInRealm:]に渡します。 YourRealmObjectClass
を Realm オブジェクト クラス名に置き換えます。 これにより、Realm 内の指定された型のすべてのオブジェクトを表すRMResultsオブジェクトが返されます。
RLMRealm *realm = [RLMRealm defaultRealm]; RLMResults *dogs = [Dog allObjectsInRealm:realm]; RLMResults *people = [Person allObjectsInRealm:realm];
Realm 内の特定の型のオブジェクトをクエリするには、メタタイプ インスタンスYourClassName.self
をRealm.objects(_:)に渡します。 これにより、Realm 内の指定された型のすべてのオブジェクトを表す結果オブジェクトが返されます。
let realm = try! Realm() // Access all dogs in the realm let dogs = realm.objects(Dog.self)
オブジェクト プロパティに基づいてクエリをフィルタリングする
フィルターは、1 つ以上のオブジェクト プロパティの値に基づいて結果のサブセットを選択します。 Realm は、フィルターの定義に使用できるフル機能のクエリ エンジンを提供します。
バージョン 10.19.0 の新機能。
Realm Swift Query APIを使用するには、 .where クエリ式を引数として含む 閉じる 。
let realm = try! Realm() // Access all dogs in the realm let dogs = realm.objects(Dog.self) // Query by age let puppies = dogs.where { $0.age < 2 } // Query by person let dogsWithoutFavoriteToy = dogs.where { $0.favoriteToy == nil } // Query by person's name let dogsWhoLikeTennisBalls = dogs.where { $0.favoriteToy.name == "Tennis ball" }
フィルタリングするには、クエリ述語を指定してResult.filter(_:)を呼び出します。
let realm = try! Realm() // Access all dogs in the realm let dogs = realm.objects(Dog.self) // Filter by age let puppies = dogs.filter("age < 2") // Filter by person let dogsWithoutFavoriteToy = dogs.filter("favoriteToy == nil") // Filter by person's name let dogsWhoLikeTennisBalls = dogs.filter("favoriteToy.name == 'Tennis ball'")
フィルタリングするには、クエリ述語を使用して-[RMResults objectsWwhere:]を呼び出します。
RLMRealm *realm = [RLMRealm defaultRealm]; // Access all dogs in the realm RLMResults *dogs = [Dog allObjectsInRealm:realm]; // Filter by age RLMResults *puppies = [dogs objectsWhere:@"age < 2"]; // Filter by favorite toy RLMResults *dogsWithoutFavoriteToy = [dogs objectsWhere:@"favoriteToy == nil"]; // Filter by favorite toy's name RLMResults *dogsWhoLikeTennisBalls = [dogs objectsWhere:@"favoriteToy.name == %@", @"Tennis ball"];
Tip
関連プロパティと埋め込みオブジェクト プロパティでフィルタリング
埋め込みオブジェクトまたは関連オブジェクトのプロパティに基づいてクエリをフィルタリングするには、通常のネストされたオブジェクトと同様に ドット表記 を使用します。
オブジェクト ID プロパティでフィルタリング
述語内の型は、プロパティの型と一致する必要があります。 Realm は string を ObjectId に自動的に変換しないため、 ObjectIdプロパティを string と比較しないでください。
バージョン 10.19.0 の新機能。
Realm Swift Query API に組み込まれている型の安全性により、 ObjectId を使用したクエリの作成が簡素化されます。
let realm = try! Realm() let dogToys = realm.objects(DogToy.self) // Get specific user by ObjectId id let specificToy = dogToys.where { $0.id == ObjectId("11223344556677889900aabb") }
次の例えは、次の Realm オブジェクトで ObjectId を使用してクエリを作成する方法と正しくない方法を示しています。
let realm = try! Realm() let dogToys = realm.objects(DogToy.self) // Get specific toy by ObjectId id let specificToy = dogToys.filter("id = %@", ObjectId("11223344556677889900aabb")).first // WRONG: Realm will not convert the string to an object id // users.filter("id = '11223344556677889900aabb'") // not ok // users.filter("id = %@", "11223344556677889900aabb") // not ok
関係のクエリ
通常の Swift または Objective-C オブジェクトのメンバーにアクセスするのと同じ方法で、関係を介してクエリを実行できます。
RLMRealm *realm = [RLMRealm defaultRealm]; // Establish a relationship Dog *dog = [[Dog alloc] init]; dog.name = @"Rex"; dog.age = 10; Person *person = [[Person alloc] init]; person._id = 12345; [person.dogs addObject:dog]; [realm transactionWithBlock:^() { [realm addObject:person]; }]; // Later, query the specific person Person *specificPerson = [Person objectForPrimaryKey:@12345]; // Access directly through a relationship NSLog(@"# dogs: %lu", [specificPerson.dogs count]); NSLog(@"First dog's name: %@", specificPerson.dogs[0].name);
let realm = try! Realm() // Establish a relationship let dog = Dog() dog.name = "Rex" dog.age = 10 let person = Person() person.id = 12345 person.dogs.append(dog) try! realm.write { realm.add(person) } // Later, query the specific person let specificPerson = realm.object(ofType: Person.self, forPrimaryKey: 12345) // Access directly through a relationship let specificPersonDogs = specificPerson!.dogs let firstDog = specificPersonDogs[0] print("# dogs: \(specificPersonDogs.count)") print("First dog's name: \(firstDog.name)")
逆関係のクエリ
通常の Swift または Objective-C オブジェクトのメンバーにアクセスするのと同じ方法で、逆関係を介してクエリを実行できます。
RLMRealm *realm = [RLMRealm defaultRealm]; // Establish a relationship Person *person = [[Person alloc] init]; person._id = 12345; DogClub *club = [[DogClub alloc] init]; club.name = @"Pooch Pals"; [club.members addObject:person]; [realm transactionWithBlock:^() { [realm addObject:club]; }]; // Later, query the specific person Person *specificPerson = [Person objectForPrimaryKey:@12345]; // Access directly through an inverse relationship NSLog(@"# memberships: %lu", [specificPerson.clubs count]); NSLog(@"First club's name: %@", [specificPerson.clubs[0] name]);
let realm = try! Realm() // Establish an inverse relationship let person = Person() person.id = 12345 let club = DogClub() club.name = "Pooch Pals" club.members.append(person) try! realm.write { realm.add(club) } // Later, query the specific person let specificPerson = realm.object(ofType: Person.self, forPrimaryKey: 12345) // Access directly through an inverse relationship let clubs = specificPerson!.clubs let firstClub = clubs[0] print("# memberships: \(clubs.count)") print("First club's name: \(firstClub.name)")
埋め込みオブジェクト プロパティに対するコレクションのクエリ
ドット表記 を使用して、埋め込みオブジェクト プロパティ値に基づいてオブジェクトのコレクションをフィルタリングまたはソートする:
注意
埋め込みオブジェクトを直接クエリすることはできません。 代わりに、親オブジェクトタイプに対するクエリを介して埋め込みオブジェクトにアクセスします。
バージョン 10.19.0 の新機能。
// Open the default realm let realm = try! Realm() // Get all contacts in Los Angeles, sorted by street address let losAngelesPeople = realm.objects(Person.self) .where { $0.address.city == "Los Angeles" } .sorted(byKeyPath: "address.street") print("Los Angeles Person: \(losAngelesPeople)")
// Open the default realm let realm = try! Realm() // Get all people in Los Angeles, sorted by street address let losAngelesPeople = realm.objects(Person.self) .filter("address.city = %@", "Los Angeles") .sorted(byKeyPath: "address.street") print("Los Angeles Person: \(losAngelesPeople)")
RLMRealm *realm = [RLMRealm defaultRealm]; RLMResults<Contact *> *losAngelesContacts = [Contact objectsInRealm:realm where:@"address.city = %@", @"Los Angeles"]; losAngelesContacts = [losAngelesContacts sortedResultsUsingKeyPath:@"address.street" ascending:YES]; NSLog(@"Los Angeles Contacts: %@", losAngelesContacts);
マップ プロパティのクエリ
邦土 マップ の値を、標準の 辞書 と同様に反復処理して確認できます。
let realm = try! Realm() let dogs = realm.objects(Dog.self) // Find dogs who have favorite parks let dogsWithFavoriteParks = dogs.where { $0.favoriteParksByCity.count >= 1 } for dog in dogsWithFavoriteParks { // Check if an entry exists if dog.favoriteParksByCity.keys.contains("Chicago") { print("\(dog.name) has a favorite park in Chicago") } // Iterate over entries for element in dog.favoriteParksByCity { print("\(dog.name)'s favorite park in \(element.key) is \(element.value)") } }
MutableSet プロパティのクエリ
MutableSetをクエリして、要素が含まれているかどうかを確認できます。 複数のセットを操作している場合は、2 つのセットの共通部分を確認したり、一方のセットが他方のセットのサブセットであるかどうかを確認できます。
let realm = try! Realm() // Find dogs who have visited New York let newYorkDogs = realm.objects(Dog.self).where { $0.citiesVisited.contains("New York") } // Get some information about the cities they have visited for dog in newYorkDogs { print("Cities \(dog.name) has visited: \(dog.citiesVisited)") } // Check whether two dogs have visited some of the same cities. // Use "intersects" to find out whether the values of the two sets share common elements. let isInBothCitiesVisited = (dog.citiesVisited.intersects(dog2.citiesVisited)) print("The two dogs have visited some of the same cities: \(isInBothCitiesVisited)") // Prints "The two dogs have visited some of the same cities: true" // Or you can check whether a set is a subset of another set. In this example, // the first dog has visited "New York" and "Toronto", while dog2 has visited both of // those but also "Toronto" and "Boston". let isSubset = (dog.citiesVisited.isSubset(of: dog2.citiesVisited)) print("\(dog.name)'s set of cities visited is a subset of \(dog2.name)'s: \(isSubset)") // Prints "Maui's set of cities visited is a subset of Lita's: true"
RealmValue プロパティの読み取りとクエリ
AnyRealmValue プロパティを読み取る場合は、その値を操作する前にその値の型を確認します。 Realm Swift SDK は、AnyRealmValue が保存できるすべてのタイプを反復処理するAnyRealmValue 列挙型を提供します。
let realm = try! Realm() let dogs = realm.objects(Dog.self) for dog in dogs { // Verify the type of the ``AnyRealmProperty`` when attempting to get it. This // returns an object whose property contains the matched type. // If you only care about one type, check for that type. if case let .string(companion) = dog.companion { print("\(dog.name)'s companion is: \(companion)") // Prints "Wolfie's companion is: Fluffy the Cat" } // Or if you want to do something with multiple types of data // that could be in the value, switch on the type. switch dog.companion { case .string: print("\(dog.name)'s companion is: \(dog.companion)") // Prints "Wolfie's companion is: string("Fluffy the Cat") case .object: print("\(dog.name)'s companion is: \(dog.companion)") // Prints "Fido's companion is: object(Dog { name = Spot })" case .none: print("\(dog.name) has no companion") // Prints "Rex has no companion" and "Spot has no companion" default: print("\(dog.name)'s companion is another type.") } }
これらの混合値の型を比較できます。
数値: int、bool、float、double、decimal
バイトベース: string、binary
時間ベース: タイムスタンプ、 ObjectId
AnyRealmValue
混合データ型を使用する場合は、次の点に注意してください。
equals
値と型に一致するクエリnot equals
クエリが、異なる値または異なる型を持つオブジェクトと一致するRealm は、可能な場合は同等の数値プロパティを変換します。 たとえば、混合型フィールドでは、1 は 1.0、1、true のすべてと一致します。
string プロパティは数値クエリと一致しません。 たとえば、混合型フィールドでは、1 は "1" と一致しません。 "1" は 1、1.0、または true には一致しません。
地理空間データのクエリ
バージョン 10.47.0 の新機能。
Swift SDK は地理空間データのクエリを簡素化するためのいくつかのシェイプを提供します。 地理空間データ クエリの境界を設定するには、 GeoCircle
、 GeoBox
、 GeoPolygon
のシェイプを使用できます。
SDK は、形状を定義するための 2 つの専用非永続データ型を提供します。
GeoPoint
: これらの値で構成される double のペアによって作成される点の座標を表す構造体。緯度: -90 から 90 度の範囲。
経度: -180 から 180 度の範囲。
RLMDistance
: 距離を表現して変換するヘルパー構造体。
地理空間の形状を定義する
GeoCircle
は、その境界が中心GeoPoint
から始まり、ラジアンで測定された半径に対応するサイズを持つ円形の形状です。 SDK の便利なRLMDistance
データ型を使用して、さまざまな単位のラジアンを簡単に操作できます。
RLMDistance
を使用すると、地理的形状の半径距離を 4 つの単位のいずれかで指定できます。
.degrees
.kilometers
.miles
.radians
オプションで、提供されている便宜的メソッドを使用して、測定値を別の距離単位に変換できます。
// You can create a GeoCircle radius measured in radians. // This radian distance corresponds with 0.25 degrees. let smallCircle = GeoCircle(center: (47.3, -121.9), radiusInRadians: 0.004363323) // You can also create a GeoCircle radius measured with a Distance. // You can specify a Distance in .degrees, .kilometers, .miles, or .radians. let largeCircle = GeoCircle(center: GeoPoint(latitude: 47.8, longitude: -122.6)!, radius: Distance.kilometers(44.4)!)
GeoBox
は、左下と右上の隅の座標によって境界が決定される直列型です。
let largeBox = GeoBox(bottomLeft: (47.3, -122.7), topRight: (48.1, -122.1)) let smallBoxBottomLeft = GeoPoint(latitude: 47.5, longitude: -122.4)! let smallBoxTopRight = GeoPoint(latitude: 47.9, longitude: -121.8) let smallBox = GeoBox(bottomLeft: smallBoxBottomLeft, topRight: smallBoxTopRight!)
GeoPolygon
は、地理空間クエリから除外する外側の円と0以上の内側の穴で境界が構成される多角形の形状です。
多角形の外側の円には少なくとも 3 つのセグメントが含まれている必要があります。 最後の GeoPoint
と最初のは同じである必要があり、これは閉じた多角形であることを示します。 つまり、多角形を構築するには少なくとも 4 つのGeoPoint
値が必要です。
GeoPolygon
内の穴は完全に外側の円内に収まっている必要があります。
穴には次の制限があります。
穴は交差できません。 穴の境界は、他の穴の内側と外側の両方と交差することはできません。
穴はエッジを共有しない場合があります。 埋め込みにエッジ AB が含まれている場合、他の埋め込みにはそれが含まれることはありません。
穴はサイズを共有する場合があります。 ただし、どのバーテックスも 1 つの埋め込みに 2 回表示されることはありません。
空の穴はありません。
ネストは 1 つのみです。
// Create a basic polygon let basicPolygon = GeoPolygon(outerRing: [ (48.0, -122.8), (48.2, -121.8), (47.6, -121.6), (47.0, -122.0), (47.2, -122.6), (48.0, -122.8) ]) // Create a polygon with one hole let outerRing: [GeoPoint] = [ GeoPoint(latitude: 48.0, longitude: -122.8)!, GeoPoint(latitude: 48.2, longitude: -121.8)!, GeoPoint(latitude: 47.6, longitude: -121.6)!, GeoPoint(latitude: 47.0, longitude: -122.0)!, GeoPoint(latitude: 47.2, longitude: -122.6)!, GeoPoint(latitude: 48.0, longitude: -122.8)! ] let hole: [GeoPoint] = [ GeoPoint(latitude: 47.8, longitude: -122.6)!, GeoPoint(latitude: 47.7, longitude: -122.2)!, GeoPoint(latitude: 47.4, longitude: -122.6)!, GeoPoint(latitude: 47.6, longitude: -122.5)!, GeoPoint(latitude: 47.8, longitude: -122.6)! ] let polygonWithOneHole = GeoPolygon(outerRing: outerRing, holes: [hole]) // Add a second hole to the polygon let hole2: [GeoPoint] = [ GeoPoint(latitude: 47.55, longitude: -122.05)!, GeoPoint(latitude: 47.55, longitude: -121.9)!, GeoPoint(latitude: 47.3, longitude: -122.1)!, GeoPoint(latitude: 47.55, longitude: -122.05)! ] let polygonWithTwoHoles = GeoPolygon(outerRing: outerRing, holes: [hole, hole2])
地理空間形状を使用したクエリ
次に、これらのシェイプを地理空間クエリで使用できます。 地理空間データは、次の 3 つの方法でクエリできます。
型安全性のある Realm Swift Query API での
.geoWithin()
演算子の使用RQL で
.filter()
を使用するNPredate クエリと
.filter()
の使用
以下の例では、これら 2 つのCompany
オブジェクトを使用するクエリの結果を示しています。
let company1 = Geospatial_Company() company1.location = CustomGeoPoint(47.68, -122.35) let company2 = Geospatial_Company(CustomGeoPoint(47.9, -121.85))
let companiesInSmallCircle = realm.objects(Geospatial_Company.self).where { $0.location.geoWithin(smallCircle!) } print("Number of companies in small circle: \(companiesInSmallCircle.count)") let companiesInLargeCircle = realm.objects(Geospatial_Company.self) .filter("location IN %@", largeCircle) print("Number of companies in large circle: \(companiesInLargeCircle.count)")
let companiesInSmallBox = realm.objects(Geospatial_Company.self).where { $0.location.geoWithin(smallBox) } print("Number of companies in small box: \(companiesInSmallBox.count)") let filterArguments = NSMutableArray() filterArguments.add(largeBox) let companiesInLargeBox = realm.objects(Geospatial_Company.self) .filter(NSPredicate(format: "location IN %@", argumentArray: filterArguments as? [Any])) print("Number of companies in large box: \(companiesInLargeBox.count)")
let companiesInBasicPolygon = realm.objects(Geospatial_Company.self).where { $0.location.geoWithin(basicPolygon!) } print("Number of companies in basic polygon: \(companiesInBasicPolygon.count)") let companiesInPolygonWithTwoHoles = realm.objects(Geospatial_Company.self).where { $0.location.geoWithin(polygonWithTwoHoles!) } print("Number of companies in polygon with two holes: \(companiesInPolygonWithTwoHoles.count)")
カスタム永続プロパティのクエリ
タイププロジェクションを使用してサポートされていないタイプをサポートされているタイプにマッピングする場合、それらのプロパティへのアクセスは多くの場合、永続化されたタイプに基づきます。
Realm オブジェクトに対するクエリ
プロジェクションされた型を使用する場合、クエリは永続化された型で動作します。 ただし、ほとんどの場合、マップされた型は引数で永続化された型とどちらも使用できます。 例外は、埋め込みオブジェクトに対するクエリです。
Tip
プロジェクションされた型は、永続化された型がソートと集計をサポートする場合、ソートと集計をサポートします。
let akcClub = realm.objects(Club.self).where { $0.name == "American Kennel Club" }.first! // You can use type-safe expressions to check for equality XCTAssert(akcClub.url == URL(string: "https://akc.org")!) let clubs = realm.objects(Club.self) // You can use the persisted property type in NSPredicate query expressions let akcByUrl = clubs.filter("url == 'https://akc.org'").first! XCTAssert(akcByUrl.name == "American Kennel Club")
埋め込みオブジェクトに対するクエリ
ノードごとの等価性を使用して、 オブジェクト内でサポートされているプロパティ型の埋め込み型をクエリできます。
オブジェクト リンク プロパティは等価比較をサポートしていますが、メンバーごとの比較はサポートしていません。 すべてのプリミティブ タイプで、埋め込みオブジェクトのノードごとの等価性をクエリできます。 オブジェクトとコレクションに対してメンバーごとの比較を実行することはできません。
動的 API
スキーマにはカスタム型マッピングの概念がないため、動的 API のいずれかを経由してデータを読み取ると、基礎となる永続化された型が提供されます。 Realm は、動的 API 経由でマップされた型の書き込みをサポートしており、プロジェクションの型を永続的な型に変換します。
動的 API の最も一般的な用途は移行です。 移行中にプロジェクションされたタイプを書き込むことができ、Realm はプロジェクションされたタイプを永続的なタイプに変換します。 ただし、移行中にデータを読み取ると、基礎となる永続化されたタイプが提供されます。
オブジェクトの非同期な読み取り
アクター分離された Realm を使用する場合、Swift 同時実行機能を使用してオブジェクトを非同期にクエリできます。
let actor = try await RealmActor() // Read objects in functions isolated to the actor and pass primitive values to the caller func getObjectId(in actor: isolated RealmActor, forTodoNamed name: String) async -> ObjectId { let todo = actor.realm.objects(Todo.self).where { $0.name == name }.first! return todo._id } let objectId = await getObjectId(in: actor, forTodoNamed: "Keep it safe")
メイン スレッドまたはアクター分離された Realm で監視対象の Realm の状態を手動で高度化する必要がある場合は、 await realm.asyncRefresh()
を呼び出します。 これにより、Realm によって管理される Realm と未処理のオブジェクトが更新され、最新データを指し、該当する通知が送信されます。
Swift 同時実行機能を使用して Realm を操作する方法の詳細については、 「 アクターで Realm を使用する - Swift SDK 」を参照してください。
クエリ結果のソート
ソート操作を使用すると、Realm Database がクエリされたオブジェクトを返す順序を構成できます。 結果コレクション内のオブジェクトの 1 つ以上のプロパティに基づいて並べ替えることができます。 Realm では、明示的にソートした場合にのみ、結果の一貫した順序が保証されます。
ソートするには、ソートするキー パスを指定して-[RMResults sortResultsUsingKeyPath:ascending:]を呼び出します。
RLMRealm *realm = [RLMRealm defaultRealm]; RLMResults *dogs = [Dog allObjectsInRealm:realm]; // Sort dogs by name RLMResults *dogsSorted = [dogs sortedResultsUsingKeyPath:@"name" ascending:NO]; // You can also sort on the members of linked objects. In this example, // we sort the dogs by their favorite toys' names. RLMResults *dogsSortedByFavoriteToyName = [dogs sortedResultsUsingKeyPath:@"favoriteToy.name" ascending:YES];
バージョン10.11.0の新機能。
型セーフなキーパスを使用してソートするには、keyPath 名とオプションのソート順序を指定してResults.sorted(by: )を呼び出します。
let realm = try! Realm() // Access all dogs in the realm let dogs = realm.objects(Dog.self) // Sort by type-safe keyPath let dogsSorted = dogs.sorted(by: \.name)
古い API を使用して並べ替えるには、並べ替えるキー パスを指定してResult.sorted(byKeyPath:ascending:)を呼び出します。
let realm = try! Realm() // Access all dogs in the realm let dogs = realm.objects(Dog.self) let dogsSorted = dogs.sorted(byKeyPath: "name", ascending: false) // You can also sort on the members of linked objects. In this example, // we sort the dogs by their favorite toys' names. let dogsSortedByFavoriteToyName = dogs.sorted(byKeyPath: "favoriteToy.name")
Tip
関連性と埋め込みオブジェクトのプロパティでソート
埋め込みオブジェクトまたは関連オブジェクトのプロパティに基づいてクエリをソートするには、通常のネストされたオブジェクトと同様に ドット表記 を使用します。
注意
stringソートと大文字と小文字を区別しないクエリは、「ラテン語の基本」、「ラテン語の追加」、「ラテン語の拡張 A」、および「ラテン語の拡張 B」の文字セットでのみサポートされます(UTF-8 範囲 0 ~ 591)。
セクション クエリ結果
結果を個々のセクションに分割できます。 各セクションは、表すオブジェクトのプロパティから生成されたキーに対応します。
たとえば、 name
プロパティの最初の文字を取得するには、 オブジェクトに計算変数を追加します。
// Computed variable that is not persisted, but only // used to section query results. var firstLetter: String { return name.first.map(String.init(_:)) ?? "" }
次に、そのオブジェクトに対してSessionedResults型セーフなコレクションを作成し、それを使用して、その計算された変数によってセクション化されたオブジェクトを検索できます。
var dogsByFirstLetter: SectionedResults<String, Dog> dogsByFirstLetter = realm.objects(Dog.self).sectioned(by: \.firstLetter, ascending: true)
セクションの数を取得したり、キーのリストを取得したり、インデックスによって個々のResultSectionにアクセスしたりできます。
let realm = try! Realm() var dogsByFirstLetter: SectionedResults<String, Dog> dogsByFirstLetter = realm.objects(Dog.self).sectioned(by: \.firstLetter, ascending: true) // You can get a count of the sections in the SectionedResults let sectionCount = dogsByFirstLetter.count // Get an array containing all section keys for objects that match the query. let sectionKeys = dogsByFirstLetter.allKeys // This example realm contains 4 dogs, "Rex", "Wolfie", "Fido", "Spot". // Prints ["F", "R", "S", "W"] print(sectionKeys) // Get a specific key by index position let sectionKey = dogsByFirstLetter[0].key // Prints "Key for index 0: F" print("Key for index 0: \(sectionKey)") // You can access Results Sections by the index of the key you want in SectionedResults. // "F" is the key at index position 0. When we access this Results Section, we get dogs whose name begins with "F". let dogsByF = dogsByFirstLetter[0] // Prints "Fido" print(dogsByF.first?.name)
コールバックを使用してセクションを作成することもできます。 これにより、プリミティブのコレクションをセクション化したり、セクションキーの生成方法をより制御したりできます。
let realm = try! Realm() let results = realm.objects(Dog.self) let sectionedResults = results.sectioned(by: { String($0.name.first!) }, sortDescriptors: [SortDescriptor.init(keyPath: "name", ascending: true)]) let sectionKeys = sectionedResults.allKeys
SectionedResults
とResultsSection
インスタンスが確認できます。どちらもThreadConfinedに準拠しています。
データを集計する
Realm の集計演算子を使用して、リスト プロパティに対する高度なクエリを実行できます。
バージョン 10.19.0 の新機能。
let realm = try! Realm() let people = realm.objects(Person.self) // People whose dogs' average age is 5 people.where { $0.dogs.age.avg == 5 } // People with older dogs people.where { $0.dogs.age.min > 5 } // People with younger dogs people.where { $0.dogs.age.max < 2 } // People with many dogs people.where { $0.dogs.count > 2 } // People whose dogs' ages combined > 10 years people.where { $0.dogs.age.sum > 10 }
let realm = try! Realm() let people = realm.objects(Person.self) // People whose dogs' average age is 5 people.filter("dogs.@avg.age == 5") // People with older dogs people.filter("dogs.@min.age > 5") // People with younger dogs people.filter("dogs.@max.age < 2") // People with many dogs people.filter("dogs.@count > 2") // People whose dogs' ages combined > 10 years people.filter("dogs.@sum.age > 10")
RLMRealm *realm = [RLMRealm defaultRealm]; RLMResults *people = [Person allObjectsInRealm:realm]; // People whose dogs' average age is 5 [people objectsWhere:@"dogs.@avg.age == 5"]; // People with older dogs [people objectsWhere:@"dogs.@min.age > 5"]; // People with younger dogs [people objectsWhere:@"dogs.@max.age < 2"]; // People with many dogs [people objectsWhere:@"dogs.@count > 2"]; // People whose dogs' ages combined > 10 years [people objectsWhere:@"dogs.@sum.age > 10"];
チェーン クエリ
結果は 遅延評価 されるため、複数のクエリを連結できます。 従来のデータベースとは異なり、連続するクエリごとにデータベースを個別に使用する必要はありません。
例
タンジェントの結果セットと、名前が「B」で始まる名前のタームの結果セットを取得するには、次のように 2 つのクエリを連鎖させます。
バージョン 10.19.0 の新機能。
let realm = try! Realm() let tanDogs = realm.objects(Dog.self).where { $0.color == "tan" } let tanDogsWithBNames = tanDogs.where { $0.name.starts(with: "B") }
let realm = try! Realm() let tanDogs = realm.objects(Dog.self).filter("color = 'tan'") let tanDogsWithBNames = tanDogs.filter("name BEGINSWITH 'B'")
RLMRealm *realm = [RLMRealm defaultRealm]; RLMResults<Dog *> *tanDogs = [Dog objectsInRealm:realm where:@"color = 'tan'"]; RLMResults<Dog *> *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH 'B'"];
クエリ クラスのプロジェクション
Realm 内のクラス プロジェクションをクエリするには、メタタイプ インスタンスYourProjectionName.self
をRealm.objects(_:)に渡します。 これにより、Realm 内のすべてのクラス プロジェクション オブジェクトを表す結果オブジェクトが返されます。
// Retrieve all class projections of the given type `PersonProjection` let people = realm.objects(PersonProjection.self) // Use projection data in your view print(people.first?.firstName) print(people.first?.homeCity) print(people.first?.firstFriendsName)
Tip
クラスのプロジェクション結果に対して派生したクエリを実行しないでください。 代わりに、Realm オブジェクトに対して直接クエリを実行し、結果をプロジェクションします。 クラスのプロジェクション結果に対して派生クエリを実行しようとすると、元のオブジェクトと同じ名前とタイプを持つフィールドのクエリは機能しますが、元のオブジェクトにない名前やタイプのフィールドのクエリは失敗します。