Docs 菜单
Docs 主页
/ /
Atlas Device SDKs
/ /

CRUD - 读取 - Swift SDK

在此页面上

  • 从 Realm 读取
  • 读取特征
  • 限制查询结果
  • 分页
  • 读取 Realm 对象
  • 关于本页中的示例
  • 按主键查找特定对象
  • 查询给定类型的所有对象
  • 根据对象属性筛选查询
  • 根据对象 ID 属性进行筛选
  • 查询关系
  • 查询反向关系
  • 查询嵌入式对象属性的集合
  • 查询 Map 属性
  • 查询 MutableSet 属性
  • 读取和查询 AnyRealmValue 属性
  • 查询地理空间数据
  • 查询自定义持久化属性
  • 异步读取对象
  • 对查询结果进行排序
  • 分段查询结果
  • 聚合数据
  • 链接查询
  • 查询类投影

Realm 读取一般包括以下步骤:

  • 从 Realm 中获取特定类型的所有对象

  • (可选)过滤结果。

  • (可选) 对结果进行排序

  • 或者,获取特定类型的所有对象,并划分为多个部分。与常规结果一样,您可以对分段结果进行过滤和排序。

查询、过滤和排序操作会返回结果或 SectionedResults 集合。这些集合是实时的,这意味着它们始终包含关联查询的最新结果。

当您围绕 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 {
@Persisted var id: ObjectId
@Persisted var name = ""
}
class Dog: Object {
@Persisted var name = ""
@Persisted var age = 0
@Persisted var color = ""
@Persisted var currentCity = ""
@Persisted var citiesVisited: MutableSet<String>
@Persisted var companion: AnyRealmValue
// To-one relationship
@Persisted var favoriteToy: DogToy?
// Map of city name -> favorite park in that city
@Persisted 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 {
@Persisted(primaryKey: true) var id = 0
@Persisted var name = ""
// To-many relationship - a person can have many dogs
@Persisted var dogs: List<Dog>
// Inverse relationship - a person can be a member of many clubs
@Persisted(originProperty: "members") var clubs: LinkingObjects<DogClub>
// Embed a single object.
// Embedded object properties must be marked optional.
@Persisted var address: Address?
convenience init(name: String, address: Address) {
self.init()
self.name = name
self.address = address
}
}
class DogClub: Object {
@Persisted var name = ""
@Persisted var members: List<Person>
// DogClub has an array of regional office addresses.
// These are embedded objects.
@Persisted var regionalOfficeAddresses: List<Address>
convenience init(name: String, addresses: [Address]) {
self.init()
self.name = name
self.regionalOfficeAddresses.append(objectsIn: addresses)
}
}
class Address: EmbeddedObject {
@Persisted var street: String?
@Persisted var city: String?
@Persisted var country: String?
@Persisted 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"];

提示

过滤相关和嵌入式对象属性

要根据嵌入式对象相关对象的属性来过滤查询,请使用点符号,就像在常规嵌套对象中一样。

谓词中的类型必须与属性的类型匹配。避免将 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);

您可以像检查标准字典一样遍历和检查域 映射 的值

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 以检查它是否包含某个元素。如果要处理多个集,您可以检查两个集的交集,或者检查一个集是否是另一个集的子集。

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 属性时,请先检查该值的类型,然后再执行任何操作。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 提供几种形状以简化地理空间数据查询。您可以使用 GeoCircleGeoBoxGeoPolygon 形状设置地理空间数据查询的边界。

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)!)
两个 GeoCircle
点击放大

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!)
2 GeoBox
点击放大

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])
3 GeoPolygons
点击放大

然后,您可以在地理空间查询中使用这些形状。您可以使用三种方法查询地理空间数据:

  • 在类型安全的 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))
2 个 GeoPoint
点击放大
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)")
查询 GeoCircle 示例。
点击放大
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)")
查询 GeoBox 示例。
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)")
查询 GeoPolygon 示例。
点击放大

当您使用类型投影将不支持的类型映射到支持的类型上时,访问这些属性通常基于持久化类型。

使用投影类型时,查询将对持久化类型进行操作。不过,在大多数情况下,您可以在参数中互换使用映射类型和持久化类型。但对嵌入式对象的查询是一种例外情况。

提示

投影类型支持排序和聚合,而持久化类型支持这些排序和聚合。

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 读取数据都会给出底层持久化类型。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

您可以观察 SectionedResultsResultsSection 实例,并且都符合 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 对象运行查询,然后投影结果。如果您尝试在类投影结果上执行派生查询,则查询与原始对象具有相同名称和类型的字段可以正常运行,但如果所查询字段的名称或类型不在原始对象中,则查询将失败。

后退

创建