Docs Menu
Docs Home
/ /
Atlas Device SDK
/ /

CRUD - 읽기 - Swift SDK

이 페이지의 내용

  • Realm에서 읽기
  • 특성 읽기
  • 쿼리 결과 제한
  • 페이지 매김
  • Realm 객체 읽기
  • 이 페이지의 예시 관련 정보
  • 기본 키로 특정 객체 찾기
  • 주어진 유형의 모든 객체 쿼리
  • 객체 속성을 기준으로 쿼리 필터링
  • 객체 ID 속성에서 필터링
  • 관계 쿼리
  • 역관계 쿼리
  • 임베디드 객체 속성에서 컬렉션 쿼리
  • 지도 속성 쿼리
  • 뮤터블 세트 속성 쿼리
  • AnyRealmValue 속성 읽기 및 쿼리
  • 지리 공간적 데이터 쿼리
  • 사용자 지정 지속 가능 속성 쿼리
  • 비동기적으로 객체 읽기
  • 쿼리 결과 정렬
  • 섹션 쿼리 결과
  • 데이터 집계
  • 체인 쿼리
  • 클래스 예측 쿼리

Realm에서의 읽기는 일반적으로 다음 단계로 구성됩니다.

  • Realm에서 특정 유형의 모든 객체를 가져옵니다.

  • 선택적으로 결과를 필터링합니다.

  • 선택적으로 결과를정렬 합니다.

  • 또는 특정 유형의 모든 객체를 섹션으로 나누어 가져옵니다. 일반적인 결과와 마찬가지로 섹션별 결과를 필터링하고 정렬할 수 있습니다.

쿼리, 필터 및 정렬 작업은 결과 또는 SectionedResults 컬렉션을 반환합니다. 이러한 컬렉션은 실시간이므로 항상 관련 쿼리의 최신 결과가 포함되어 있습니다.

Realm 읽기의 다음 세 가지 주요 특성을 중심으로 앱의 데이터 액세스 패턴을 설계하면 최대한 효율적으로 데이터를 읽을 수 있다고 확신할 수 있습니다.

쿼리 결과는 데이터의 복사본이 아닙니다. 쿼리 결과를 수정하면 디스크에 있는 데이터가 직접 수정됩니다. 또한 이 메모리 매핑은 결과가 라이브라는 의미이기도 합니다. 즉, 항상 디스크의 현재 상태를 반영합니다.

'컬렉션은 실시간입니다'를 참조하세요.

Realm은 사용자가 실제로 해당 쿼리의 결과를 요청할 때만 쿼리를 실행합니다. 이 지연 평가를 사용하면 대규모 데이터 세트와 복잡한 쿼리를 처리할 수 있는 우아하고 성능이 뛰어난 코드를 작성할 수 있습니다. 중간 상태를 처리하기 위해 추가 작업 없이 여러 필터 및 정렬 작업 을 연결할 수 있습니다.

Realm 객체 모델의 장점 중 하나는 Realm이 객체의 모든 관계를 자동으로 직접 참조로 유지하므로 쿼리 결과를 통해 관계 그래프를 직접 탐색할 수 있다는 것입니다.

직접 참조(포인터)를 사용하면 참조를 통해 관련 객체의 속성에 직접 액세스할 수 있습니다.

다른 데이터베이스는 직접 작업해야 할 때 일반적으로 데이터베이스 저장소의 객체를 애플리케이션 메모리로 복사합니다. 애플리케이션 객체에는 직접 참조가 포함되어 있으므로 각 직접 참조가 참조하는 객체가 필요할 경우 데이터베이스에서 객체를 복사하거나 각 객체에 대한 외래 키를 복사하고 해당 키에 액세스할 경우 해당 객체를 쿼리할 수 있습니다. 참조된 객체를 애플리케이션 메모리에 복사하면 액세스하지 않는 객체에 많은 리소스가 사용될 수 있는 반면 외래 키만 복사하도록 선택하면 참조된 객체 조회로 인해 애플리케이션 속도가 느려질 수 있습니다.

Realm은 제로 카피 라이브 객체를 사용하여 이 모든 과정을 우회합니다. Realm 객체 접근자는 메모리 매핑을 사용하여 데이터베이스 스토리지를 직접 지시하므로 Realm의 객체와 애플리케이션 메모리의 쿼리 결과를 구분하지 않습니다. 이로 인해 쿼리 결과에서 전체 영역에 걸쳐 직접 참조를 순회할 수 있습니다.

평가가 지연되므로 Realm의 쿼리 결과를 제한하기 위한 특별한 메커니즘이 필요하지 않습니다. 예를 들어 쿼리가 수천 개의 객체와 일치하지만 처음 10개만 로드하려는 경우 결과 컬렉션의 처음 10개 요소에만 액세스하면 됩니다.

지연 평가 덕분에 일반적인 페이지 매김 작업이 아주 간단해집니다. 예를 들어 영역에 있는 수천 개의 객체와 일치하는 쿼리와 연결된 결과 컬렉션이 있다고 가정해 보겠습니다. 페이지당 100개의 개체를 표시합니다. 원하는 페이지로 이동하려면 대상 페이지에 해당하는 인덱스부터 결과 컬렉션의 요소에 액세스하기만 하면 됩니다.

이 페이지의 예시에서는 다음 모델을 사용합니다.

// 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.object(ofType:forPrimaryKey:)를 사용하여 직접 조회할 수 있습니다.

let realm = try! Realm()
let specificPerson = realm.object(ofType: Person.self, forPrimaryKey: 12345)

Realm에서 지정된 유형의 객체를 쿼리하려면 Realm 인스턴스를 +[YourRealmObjectClass allObjectsInRealm:] 에 전달합니다. YourRealmObjectClass 를 Realm 객체 클래스 이름으로 바꿉니다. Realm에 있는 지정된 유형의 모든 객체를 나타내는 RLMResults 객체를 반환합니다.

RLMRealm *realm = [RLMRealm defaultRealm];
RLMResults *dogs = [Dog allObjectsInRealm:realm];
RLMResults *people = [Person allObjectsInRealm:realm];

영역에서 지정된 유형의 객체를 쿼리하려면 YourClassName.self 메타 유형 인스턴스를 Realm.objects(_:)에 전달합니다. 영역에 있는 지정된 유형의 모든 객체를 나타내는 결과 객체를 반환합니다.

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'")

필터링하려면 쿼리 조건자와 함께 -[RLMResults 객체 위치:] 를 호출합니다.

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"];

관련 객체 및 포함된 객체 속성 필터링

포함된 객체 또는 관련 객체의 속성을 기준으로 쿼리를 필터링하려면 일반 중첩 객체에서와 같이 점 표기법을 사용합니다.

조건자의 유형은 속성의 유형과 일치해야 합니다. Realm은 문자열을 ObjectId로 자동으로 변환하지 않으므로 ObjectId 속성을 문자열과 비교하지 마세요.

버전 10.19.0의 새로운 기능

Realm Swift Query API에 내장된 유형 안정 기능은 객체 ID로 쿼리를 작성하는 작업을 간소화합니다.

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 또는 오브젝티브-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 또는 오브젝티브-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);

표준 사전 과 마찬가지로 Realm 맵 의 값을 반복하고 확인할 수 있습니다.

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

  • 바이트 기반: 문자열, 바이너리

  • 시간 기반: 타임스탬프, objectId

AnyRealmValue 혼합 데이터 유형을 사용할 때 다음 사항에 유의하세요.

  • equals 값 및 유형에 대한 쿼리 일치

  • not equals 쿼리는 값이 다르거나 유형이 다른 객체와 일치합니다.

  • Realm은 가능한 경우 비교 가능한 숫자 속성을 변환합니다. 예를 들어 혼합 형식 필드에서 1은 1.0, 1 및 true를 모두 일치시킵니다.

  • 문자열 속성이 숫자 쿼리와 일치하지 않습니다. 예를 들어 혼합 유형 필드에서 1은 '1'과 일치하지 않습니다. '1'은 1, 1.0 또는 true와 일치하지 않습니다.

버전 10.47.0에 새로 추가되었습니다.

Swift SDK는 지리 공간적 데이터 쿼리를 간소화하기 위해 여러 가지 도형을 제공합니다. GeoCircle, GeoBoxGeoPolygon 도형을 사용하여 지리 공간적 데이터 쿼리에 대한 경계를 설정할 수 있습니다.

SDK는 도형을 정의하기 위해 두 가지 특수한 비지속성 데이터 유형을 제공합니다.

  • GeoPoint: 다음 값으로 구성된 double 쌍으로 형성된 점의 좌표를 나타내는 구조체입니다.

    • 위도: -90도에서 90도 사이의 범위입니다.

    • 경도: -180도에서 180도 사이의 범위입니다.

  • RLMDistance: 거리를 표현하고 변환하는 헬퍼 구조체입니다.

GeoCircle은(는) 경계가 중앙 GeoPoint에서 시작되는 원형 모양이며 크기는 라디안으로 측정된 반경에 해당합니다. SDK의 편의 RLMDistance 데이터 유형을 사용하여 다양한 단위의 반경을 쉽게 작업할 수 있습니다.

RLMDistance 다음의 네 가지 단위 중 하나로 geo 도형의 반경 거리를 지정할 수 있습니다.

  • .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)!)
GeoCircles 2개
클릭하여 확대

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 개 이상의 내부 구멍으로 구성된 다각형 모양입니다.

다각형의 외부 링에는 최소 3개의 세그먼트가 포함되어야 합니다. 마지막 GeoPoint 와 첫 번째 은 같아야 하며, 이는 닫힌 다각형을 나타냅니다. 즉, 다각형을 구성하는 데 최소 네 개의 GeoPoint 값이 필요합니다.

GeoPolygon 의 내부 구멍은 외부 링 내에 완전히 포함되어야 합니다.

구멍에는 다음과 같은 제한 사항이 있습니다.

  • 구멍이 교차할 수 없습니다. 구멍의 경계는 다른 구멍의 내부와 외부 모두와 교차할 수 없습니다.

  • 구멍은 가장자리를 공유할 수 없습니다. 구멍에 모서리 AB가 포함되어 있으면 다른 구멍에는 모서리 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은 프로젝션된 유형을 지속형 유형으로 변환합니다. 그러나 마이그레이션 중에 데이터를 읽으면 기본 지속 유형이 제공됩니다.

행위자 격리 영역을 사용할 때 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")

기본 스레드에서 관찰된 영역이나 행위자 격리 영역의 상태를 수동으로 진행해야 하는 경우 await realm.asyncRefresh()을(를) 호출하세요. 이렇게 하면 Realm이 관리하는 영역과 처리되지 않은 객체가 최신 데이터를 가리키고 적용 가능한 알림을 전달하도록 업데이트됩니다.

Swift 동시성 기능을 사용하여 Realm으로 작업하는 방법에 대한 자세한 내용은 행위자와 Realm 사용 - Swift SDK를 참조하세요.

정렬 작업을 통해 Realm 데이터베이스가 쿼리된 객체를 반환하는 순서를 구성할 수 있습니다. 컬렉션에 있는 객체의 속성을 하나 이상의 기준으로 정렬할 수 있습니다. Realm은 명시적으로 정렬하는 경우에만 결과의 일관된 순서를 보장합니다.

정렬하려면 정렬 기준으로 사용할 키 경로를 사용하여 -[RLMResults sortedResultsUsingKeyPath: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: )를 호출하면 형식 안전 keyPath를 사용하여 정렬할 수 있습니다:

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")

관련 객체 및 포함된 객체 속성에서 정렬

포함된 객체 또는 관련 객체의 속성을 기준으로 쿼리를 정렬하려면 일반 중첩된 객체에서처럼 점 표기법을 사용합니다.

참고

문자열 정렬 및 대소문자를 구분하지 않는 퀴리는 'Latin Basic', 'Latin Supplement', 'Latin Extended A', 'Latin Extended B'(UTF-8 범위 0-591)의 문자에 대해서만 지원합니다.

문자열 정렬 및 대소문자를 구분하지 않는 퀴리는 'Latin Basic', 'Latin Supplement', 'Latin Extended A', 'Latin Extended 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 인스턴스를 관찰 할 수 있으며 둘 다 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'로 시작하는 갈색 개에 대한 결과 세트를 얻으려면 다음과 같이 두 개의 쿼리를 연결합니다.

버전 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(_:)로 전달합니다. 영역의 모든 클래스 프로젝션 객체를 나타내는 결과 객체를 반환합니다.

// 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 객체에 직접 쿼리를 실행한 다음 결과를 프로젝트합니다. 클래스 프로젝션 결과를 기반으로 파생 쿼리를 수행하려는 경우 원본 객체와 동일한 이름 및 형식을 가진 필드를 쿼리할 수 있지만 원본 객체에 없는 이름 또는 유형을 가진 필드를 쿼리하면 실패합니다.

돌아가기

만들기