CRUD - 读取 - Swift SDK
在此页面上
从 Realm 读取
Realm 读取一般包括以下步骤:
查询、过滤和排序操作会返回结果或 SectionedResults 集合。这些集合是实时的,这意味着它们始终包含关联查询的最新结果。
读取特征
当您围绕 Realm 中读取的以下三个关键特征设计应用的数据访问模式时,您可以确信自己正在尽可能高效地读取数据。
结果不是副本
查询的结果不是数据副本:修改查询结果将直接修改磁盘上的数据。这种内存映射还意味着结果是实时的:也就是说,它们始终反映磁盘上的当前状态。
另请参阅:集合的实时性。
结果出现延迟
Realm 仅在您实际请求该查询的结果时才运行查询。这种惰性求值机制让您能够编写优雅、高性能的代码来处理大型数据集和复杂查询。您可以链接多个过滤和排序操作,而无需额外的工作来处理中间状态。
引用被保留
Realm 对象模型的一项优势是,Realm 会自动将对象的所有关系保留为直接引用,因此您可以通过查询结果直接遍历关系图。
直接引用或指针允许您直接通过该引用访问相关对象的属性。
当您需要直接使用对象时,其他数据库通常会将对象从数据库存储复制到应用程序内存中。由于应用程序对象包含直接引用,因此您只有一个选项:将每个直接引用所引用的对象从数据库中复制出来(如果需要),或仅复制每个对象的外键并在该对象被访问的情况下使用该键来查询此对象。如果选择将引用的对象复制到应用程序内存中,则可能会为从未访问过的对象占用大量资源;但是,如果选择仅复制外键,引用的对象查找则可能会导致应用程序变慢。
Realm 使用零拷贝活动对象绕过所有这些操作。Realm 对象访问器使用内存映射直接指向数据库存储,因此 Realm 中的对象与应用程序内存中的查询结果没有区别。因此,您可从任一查询结果遍历整个 Realm 的直接引用。
限制查询结果
由于出现延迟求值,因此无需任何特殊机制来限制 Realm 的查询结果。例如,如果您的查询匹配到数千个对象,但您只想加载前十个对象,则只需访问结果集合的前十个元素即可。
分页
借助延迟求值,分页的常见任务变得非常简单。例如,假设您有一个与某一查询关联的结果集合,而该查询可与您 Realm 中的数千个对象匹配。您选择每页显示一百个对象。要前进到任一页面,只需从与目标页面相对应的索引开始访问结果集合中的元素即可。
读取 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? }
按主键查找特定对象
如果知道给定对象的主键,则可以直接使用+[RLMObject objectForPrimaryKey:] 进行查找。
// Get a specific person from the default realm Person *specificPerson = [Person objectForPrimaryKey:@12345];
如果您知道给定对象的主键,则可以直接使用Realm进行查找。对象(ofType:forPrimaryKey:)。
let realm = try! Realm() let specificPerson = realm.object(ofType: Person.self, forPrimaryKey: 12345)
查询给定类型的所有对象
要在 Realm 中查询给定类型的对象,请将域实例传递给 +[ 域 allObjectsInRealm:] 。 将 YourRealmObjectClass
替换为您的Realm 对象类名称。 这将返回一个RLMResults对象,表示域中给定类型的所有对象。
RLMRealm *realm = [RLMRealm defaultRealm]; RLMResults *dogs = [Dog allObjectsInRealm:realm]; RLMResults *people = [Person allObjectsInRealm:realm];
要查询域中给定类型的对象,请将元类型实例 YourClassName.self
传递给 Realm.objects(_:)。这将返回一个 Results 对象,代表域中给定类型的所有对象。
let realm = try! Realm() // Access all dogs in the realm let dogs = realm.objects(Dog.self)
根据对象属性筛选查询
过滤器根据一个或多个对象属性的值选择结果子集。Realm 提供了一个功能完备的查询引擎,您可以用它来定义过滤器。
版本 10.19.0 的新增功能。
要使用 Realm Swift 查询 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" }
要进行筛选,请使用查询谓词调用Results.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'")
要进行筛选,请使用查询谓词调用-[RLMResultsobjectswhere:] 。
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"];
根据对象 ID 属性进行筛选
谓词中的类型必须与属性的类型匹配。避免将 ObjectId 属性与字符串进行比较,因为 Realm 不会自动将字符串转换为 ObjectId。
版本 10.19.0 的新增功能。
Realm Swift 查询 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);
查询 Map 属性
您可以像检查标准字典一样遍历和检查域 映射 的值 :
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 以检查它是否包含某个元素。如果要处理多个集,您可以检查两个集的交集,或者检查一个集是否是另一个集的子集。
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"
读取和查询 AnyRealmValue 属性
当您读取 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
基于字节:字符串、二进制
基于时间:时间戳、对象标识符
使用 AnyRealmValue
混合数据类型时,请记住以下事项:
equals
查询会匹配值和类型not equals
查询会匹配具有不同值或不同类型的对象Realm 会尽可能转换可比较的数值属性。例如,在混合类型字段中,1 匹配所有 1.0、1 和 true。
字符串属性不匹配数值查询。例如,在混合类型字段中,1 不匹配 "1"。“1” 不匹配 1、1.0 或 true。
查询地理空间数据
10.47.0 版本的新增功能。
Swift SDK 提供几种形状以简化地理空间数据查询。您可以使用 GeoCircle
、GeoBox
和 GeoPolygon
形状设置地理空间数据查询的边界。
SDK 提供两种专用的非持久保存数据类型以定义形状:
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个或更多要从地理空间查询中排除的内孔组成。
多边形的外环必须至少包含三个线段。 最后一个和第一个GeoPoint
必须相同,表示闭合多边形。 这意味着构造一个多边形至少需要四个GeoPoint
值。
GeoPolygon
中的内孔必须完全包含在外环内。
洞有以下限制:
孔不得交叉。 一个孔的边界不得与任何其他孔的内部和外部相交。
孔不得股票边。 如果一个孔包含边 AB,则其他孔不得包含该边。
孔可以股票顶点。 但是,任何顶点都不能在单个孔中出现两次。
没有一个洞是空的。
只允许嵌套一次。
// 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])
使用地理空间形状进行查询
然后,您可以在地理空间查询中使用这些形状。您可以使用三种方法查询地理空间数据:
在类型安全的 Realm Swift 查询 API 中使用
.geoWithin()
操作符在 RQL 中使用
.filter()
在 NSPredicate 查询中使用
.filter()
以下示例显示使用两个 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 对象的查询
使用投影类型时,查询将对持久化类型进行操作。不过,在大多数情况下,您可以在参数中互换使用映射类型和持久化类型。但对嵌入式对象的查询是一种例外情况。
提示
投影类型支持排序和聚合,而持久化类型支持这些排序和聚合。
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 会将投影类型转换为持久化类型。但是,在迁移期间读取数据会得到底层持久化类型。
异步读取对象
当您使用 actor 隔离的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 或 actor 隔离 Realm 上被观察到的 Realm 状态,请调用 await realm.asyncRefresh()
。这会更新 Realm 管理的 Realm 对象和未完成的对象,使其指向最新的数据并提供所有适用的通知。
有关使用 Swift 并发功能处理 Realm 的更多信息,请参阅结合使用 Realm 与 Actor - Swift SDK。
对查询结果进行排序
排序操作允许您配置 Realm 数据库返回查询对象的顺序。您可以根据结果集合中对象的一个或多个属性进行排序。仅当您对结果进行显式排序时,Realm 才能保证结果顺序一致。
要排序,请使用所需的排序依据键路径调用-[RLMResultssortedResultsUsingKeyPath: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 中的新增内容。
可以通过键路径名称和可选的排序顺序调用 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 进行排序,请调用 Results.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")
注意
字符串排序和不区分大小写的查询仅支持“基本拉丁语”、“拉丁语补充”、“拉丁扩展 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(_:)) ?? "" }
然后,您可以为该对象创建一个 SectionedResults 类型安全的集合,并使用它来检索由该计算变量分割的对象:
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
实例,并且都符合 ThreadConsided。
聚合数据
您可以使用 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”开头的棕褐色狗的结果集,请按以下方式链接两个查询:
版本 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'"];
查询类投影
要查询域中的类投影,请将元类型实例 YourProjectionName.self
传递给 Realm.objects(_:)。这将返回一个代表域中所有类投影对象的 Results 对象。
// 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)
提示
请勿在类投影结果上执行派生查询。取而代之的是,直接对 Realm 对象运行查询,然后投影结果。如果您尝试在类投影结果上执行派生查询,则查询与原始对象具有相同名称和类型的字段可以正常运行,但如果所查询字段的名称或类型不在原始对象中,则查询将失败。