문서 메뉴

문서 홈애플리케이션 개발Atlas Device SDK

CRUD - 읽기 - Swift SDK

이 페이지의 내용

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

필터는 하나 이상의 객체 속성 값을 기반으로 결과의 하위 집합을 선택합니다. Realm은 필터를 정의하는 데 사용할 수 있는 모든 기능을 갖춘 쿼리 엔진을 제공합니다.

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

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

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

일반 Swift 또는 오브젝티브-C 객체 멤버에 액세스하는 것과 같은 방식으로 관계를 쿼리할 수 있습니다.

일반 Swift 또는 오브젝티브-C 객체 멤버에 액세스하는 것과 동일한 방식으로 역 관계를 통해 쿼리할 수 있습니다.

점 표기법을 사용하여 포함된 객체 속성 값을 기준으로 객체 컬렉션을 필터링하거나 정렬할 수 있습니다.

참고

포함된 객체를 직접 쿼리할 수는 없습니다. 대신 상위 객체 유형에 대한 쿼리를 통해 포함된 객체에 액세스하세요.

표준 사전 과 마찬가지로 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: 다음 값으로 구성된 더블 쌍으로 형성된 점의 좌표를 나타내는 구조체입니다.

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

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

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

그런 다음 이러한 도형을 지리 공간적 쿼리에 사용할 수 있습니다. 다음 세 가지 방법으로 지리 공간적 데이터를 쿼리할 수 있습니다.

  • 유형 안전 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 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은 명시적으로 정렬하는 경우에만 결과의 일관된 순서를 보장합니다.

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

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

참고

문자열 정렬 및 대소문자를 구분하지 않는 퀴리는 '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의 집계 연산자를 사용할 수 있습니다.

결과가 느리게 평가 되므로 여러 쿼리를 함께 연결할 수 있습니다. 기존 데이터베이스와 달리 각 연속 쿼리에 대해 데이터베이스를 별도로 방문할 필요가 없습니다.

예제

갈색 개와 이름이 '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)

클래스 프로젝션 결과 위에 파생 쿼리를 수행하지 마세요. 대신 Realm 객체에 직접 쿼리를 실행한 다음 결과를 프로젝트합니다. 클래스 프로젝션 결과를 기반으로 파생 쿼리를 수행하려는 경우 원본 객체와 동일한 이름 및 형식을 가진 필드를 쿼리할 수 있지만 원본 객체에 없는 이름 또는 유형을 가진 필드를 쿼리하면 실패합니다.

← CRUD - 만들기 - Swift SDK