Realm 객체 읽기 - Kotlin SDK
이 페이지의 내용
이 페이지에서는 코틀린(Kotlin)용 Atlas Device SDK를 사용하여 데이터베이스에 지속되는 객체를 쿼리하고 지연 읽기하는 방법을 설명합니다. 이 지연 평가는 대규모 데이터 세트와 복잡한 쿼리를 처리할 때 코드 효율성과 성능을 가능하게 합니다.
읽기 작업
읽기 작업은 데이터베이스 객체를 쿼리한 후 결과에 액세스할 준비가 되면 쿼리를 실행하는 것으로 구성됩니다. 읽기 작업의 구문은 동기화된 데이터베이스와 동기화되지 않은 데이터베이스에서 동일합니다.
모든 쿼리는 객체 유형을 기반으로 합니다. 데이터베이스에 지속되고 데이터베이스 스키마에 유형이 포함된 내장된 객체를 포함한 모든 객체를 쿼리할 수 있습니다.
SDK의 쿼리 빌더 RealmQuery 를 사용하여 쿼리를 구성하고 객체 유형을 유형 매개 변수로 전달합니다. Realm
또는 MutableRealm
인스턴스, RealmResults
컬렉션 또는 RealmList
컬렉션 에서 객체를 쿼리 할 수 있습니다.
기본 RealmQuery
는 지정된 유형의 모든 객체를 반환합니다.
// Finds all objects of type <T> .query<T>()
추가 필터 및 조건(예: 결과 정렬, 집계, 제한)을 사용하여 쿼리를 세분화할 수 있습니다. SDK에서 제공하는 기본 제공 코틀린(Kotlin) 확장 함수 및 헬퍼 메서드와 함께 문자열 기반 쿼리 언어인 RQL(Realm 쿼리 언어)을 사용하여 하나 이상의 속성을 기준으로 결과를 세분화할 수 있습니다.
// Finds all objects of type <T> that meet the filter conditions // and returns them in the specified sort order .query<T>(filter).sort(sort)
추가 쿼리() 메서드를 사용하여 쿼리 를 함께 연결할 수도 있습니다. 추가된 각 query()
는 AND
쿼리 조건으로 작동합니다. 그리고 SDK의 지연 평가 덕분에 연속적인 쿼리에는 데이터베이스 를 별도로 방문할 필요가 없습니다.
// Finds all objects of type <T> that meet the first filter conditions // AND the subsequent filter conditions .query<T>(filter).query(filter)
데이터에 액세스하고 반환된 결과로 작업할 준비가 되면 쿼리를 실행합니다.
find() 를 사용하여 동기 쿼리 를 수행합니다. SDK는 쿼리 조건과 일치하는 모든 데이터베이스 객체를 나타내는 RealmResults 컬렉션 을 느리게 반환합니다. 일반적으로 다른 코틀린 (Kotlin) 컬렉션 과 마찬가지로 결과 컬렉션 으로 작업할 수 있습니다.
비동기 쿼리 를 수행하려면 asFlow() 를 사용합니다. Kotlin SDK가 루틴 흐름 을 느리게 구독합니다. 수집하고 반복하거나 변경 사항을 수신할 수 있습니다.
MutableRealm.query
에서는asFlow()
을(를) 호출 할 수 없습니다 .
조회된 결과는 실제로 일치하는 데이터베이스 객체를 메모리에 보유하지 않습니다. 대신 데이터베이스는 직접 참조 또는 포인터를 사용합니다. 결과 컬렉션 또는 흐름의 데이터베이스 객체는 데이터베이스 파일의 데이터에 직접 매핑되는 일치하는 객체를 참조합니다. 이는 또한 쿼리 결과를 통해 객체의 관계 그래프를 직접 탐색할 수 있다는 의미이기도 합니다.
예시
쿼리 실행
val queryAllFrogs = realm.query<Frog>() val queryAllLiveFrogs = this.query<Frog>() // this: MutableRealm // Calling 'find()' on the query returns a RealmResults collection // Can be called on a `Realm.query()` or `MutableRealm.query()` val allFrogs: RealmResults<Frog> = queryAllFrogs.find() val allLiveFrogs: RealmResults<Frog> = queryAllLiveFrogs.find() // Calling 'asFlow()' on the query returns a ResultsChange Flow // Can ONLY be called on a `Realm.query()` val allFrogsFlow: Flow<ResultsChange<Frog>> = queryAllFrogs.asFlow()
고정 및 실시간 결과
항상 실시간 결과를 반환하는 다른 Atlas Device SDK와 달리 코틀린 SDK를 사용하면 결과를 고정하거나 실시간 결과를 반환할 수 있습니다. 코틀린 SDK의 고정 아키텍처에 대한 자세한 내용은 고정 아키텍처 - 코틀린 SDK를 참조하세요.
고정된 결과 에 액세스 하려면 Realm 에서 쿼리 를 실행 합니다. 고정된 결과는 수정할 수 없으며 데이터베이스 의 최신 변경 사항을 반영하지 않습니다. Realm.query()
에는 쓰기 트랜잭션( 쓰기 트랜잭션 (write transaction))이 필요하지 않습니다.
실시간 결과 에 액세스 하려면 쓰기 트랜잭션( 쓰기 트랜잭션 (write transaction) )에서 MutableRealm 인스턴스 에 대한 쿼리 를 실행 합니다. MutableRealm
는 데이터베이스 의 쓰기 가능한 상태 를 나타내며 쓰기 트랜잭션( 쓰기 트랜잭션 (write transaction))을 통해서만 액세스할 수 있습니다. MutableRealm의 결과입니다. 쿼리 는 라이브이지만 호출 스레드에서만 유효하며 쓰기 트랜잭션( 쓰기 트랜잭션 (write transaction) )이 완료되면 동결됩니다. 쓰기 트랜잭션(write transaction) 및 MutableRealm
액세스에 대한 자세한 내용은 쓰기 (write) 트랜잭션(write transaction)을 참조하세요 .
MutableRealm.findLatest() 를 호출하여 동결된 결과에서 라이브 객체 에 액세스 할 수도 있습니다. 자세한 내용은 이 페이지 의 객체의 최신 버전 찾기 섹션을 참조하세요.
예시
실시간 결과 액세스
// 'Realm.query()' results are always frozen val frozenResults = realm.query<Frog>("age > $0", 50).find() // 'MutableRealm.query()' results are live within the current write transaction realm.write { // this: MutableRealm val liveResults = this.query<Frog>("age > $0", 50).find()
데이터베이스 객체 찾기
데이터베이스 내에 저장된 객체를 찾으려면 다음을 수행합니다.
객체 유형을 유형 매개 변수로 query() 에 전달합니다. 객체 유형이 데이터베이스 스키마에 이미 포함되어 있어야 합니다.
원하는 경우 쿼리 조건을 전달하여 결과를 더욱 세분화합니다.
조건을 충족하는 객체만 반환하도록 필터를 지정합니다. 필터를 지정하지 않으면 SDK는 지정된 유형의 모든 객체를 반환합니다.
RealmQuery
에 추가 query() 메서드를 추가하여 필터를 연결할 수 있습니다.결과의 정렬 순서를 지정합니다. 데이터베이스는 정렬되지 않은 상태이므로 정렬 순서를 포함하지 않으면 SDK는 쿼리가 특정 순서로 객체를 반환한다는 것을 보장할 수 없습니다.
다음 중 하나를 사용하여 쿼리를 실행합니다.
팁
대규모 데이터 세트의 경우 asFlow() 사용 선호
find()
호출된 스레드에서 동기 쿼리를 실행합니다. 따라서 UI 스레드 또는 UI 스레드를 지연시킬 수 있는 로직에서는 대규모 데이터 세트에 대해find()
사용을 피해야 합니다.성능이나 UI에 부정적인 영향을 주지 않도록
asFlow()
를 선호합니다.결과로 작업합니다. 실행한 쿼리 유형에 따라 객체가 고정되거나 실시간 상태일 수 있습니다.
예시
읽기 작업
// Pass the object type as <T> parameter and filter by property val findFrogs = realm.query<Frog>("age > 1") // Chain another query filter .query("owner == $0 AND name CONTAINS $1", "Jim Henson", "K") // Sort results by property .sort("age", Sort.ASCENDING) // Run the query .find() // ... work with the results
객체의 최신 버전 찾기
SDK의 고정 아키텍처로 인해 항상 최신 버전의 객체 또는 컬렉션으로 작업하는 것은 아닙니다.
데이터베이스의 최신 변경 사항을 반영하는 객체 또는 컬렉션의 버전을 가져오려면 MutableRealm
인스턴스에서 findLatest() 를 호출하면 됩니다. MutableRealm.query()
와 같이 결과는 실시간 이지만 호출 스레드에서만 유효하며 쓰기 트랜잭션(write transaction)이 완료되면 동결됩니다.
다음 예에서는 기존 frozenFrogs
쿼리의 RealmResults
를 findLatest()
로 전달하여 컬렉션의 최신 실시간 사본을 가져옵니다. 그런 다음 쓰기 트랜잭션 내에서 현재 실행 중인 객체를 수정합니다.
// Open a write transaction to access the MutableRealm realm.write { // this: MutableRealm for (frog in frozenFrogs) { // Call 'findLatest()' on an earlier query's frozen results // to return the latest live objects that you can modify // within the current write transaction findLatest(frog)?.also { liveFrog -> copyToRealm(liveFrog.apply { age += 1 }) println(liveFrog.name + " is now " + liveFrog.age + " years old") } } }
팁
객체가 고정되었는지 여부는 isFrozen() 메서드를 사용하여 확인할 수 있습니다.
val isFrozen = frog.isFrozen()
특정 유형의 모든 객체 쿼리
특정 유형의 모든 객체를 쿼리하려면 쿼리 인수 없이 RealmObject
또는 EmbeddedRealmObject
객체 유형을 query()에 유형 매개변수로 전달합니다. SDK는 지정된 유형의 모든 객체를 반환합니다.
참고
읽을 수 없는 비대칭 객체
비대칭 객체는 데이터베이스에 유지되지 않는 특수한 쓰기 전용 객체이므로 읽을 수 없습니다 . 애플리케이션에서 비대칭 객체를 사용하는 방법에 대한 자세한 내용은 Atlas로 데이터 스트리밍 - 코틀린 SDK를 참조하세요.
다음 예에서는 Frog
유형의 모든 RealmObject
객체를 쿼리합니다.
// Query all Frog objects in the database val queryAllFrogs = realm.query<Frog>() val allFrogs = queryAllFrogs.find()
다음 예에서는 EmbeddedAddress
유형의 모든 EmbeddedRealmObject
객체를 쿼리합니다.
val queryAllEmbeddedAddresses = realm.query<EmbeddedAddress>() val allEmbeddedAddresses = queryAllEmbeddedAddresses.find()
상위 객체를 통해 포함된 객체를 쿼리할 수도 있습니다. 자세한 내용은 이 페이지의 내장된 객체 속성으로 필터링 섹션을 참조하세요.
팁
내장된 객체를 찾으면 EmbeddedRealmObject.parent() 메서드를 사용하여 해당 상위 객체에 액세스할 수 있습니다.
val getParent = embeddedObject.parent<Contact>()
단일 객체 쿼리
특정 객체 유형의 단일 객체를 찾으려면 쿼리에서 first() 를 호출합니다. 쿼리를 실행하면 SDK는 조건 또는 null
와 일치하는 첫 번째 객체를 반환합니다.
다음 예에서는 Frog
객체 유형을 쿼리하여 첫 번째 객체를 반환합니다.
val querySingleFrog = realm.query<Frog>().first() val singleFrog = querySingleFrog.find() if (singleFrog != null) { println("${singleFrog.name} is a frog.") } else { println("No frogs found.") }
속성별 필터링
데이터베이스에 유지되는 객체 유형의 속성을 기준으로 쿼리를 필터링할 수 있습니다. 여기에는 점 표기법을 사용하여 참조할 수 있는 하위 속성이 포함됩니다.
속성으로 필터링하려면 RQL(Realm 쿼리 언어) 필터와 연산자를 전달하거나, 코틀린의 내장 확장 메서드 또는 SDK의 편의 메서드를 사용하거나, 조합하여 사용할 수 있습니다. 현재 지원되는 모든 RQL 연산자와 구문에 대한 정보는 Realm 쿼리 언어 참고 문서를 참조하세요.
다음 예시에서는 Frog
객체 유형을 쿼리하고 name
속성을 기준으로 필터링합니다.
val filterByProperty = realm.query<Frog>("name == $0", "Kermit") val frogsNamedKermit = filterByProperty.find()
프라이머리 키별 필터링
프라이머리 키는 데이터베이스에 있는 객체의 고유 식별자로, 특정 객체를 쿼리하는 데 유용합니다.
특정 프라이머리 키로 필터링하려면 객체 유형을 유형 매개변수로 전달하고 프라이머리 키 필드에서 원하는 값을 쿼리합니다.
다음 예에서는 Frog
객체를 쿼리하고 프라이머리 키 속성 _id
기준으로 필터링합니다.
val filterByPrimaryKey = realm.query<Frog>("_id == $0", PRIMARY_KEY_VALUE) val findPrimaryKey = filterByPrimaryKey.find().first()
팁
Device Sync는 항상 _id를 프라이머리 키로 사용합니다.
Atlas Device Sync를 사용하면 항상 프라이머리 키 필드 _id
로 쿼리할 수 있습니다. 이는 Atlas Device Sync 데이터 모델에 _id
라는 프라이머리 키가 있는 객체가 필요하기 때문입니다. 자세한 내용은 Device Sync를 통한 데이터 모델 - Kotlin SDK를 참조하세요.
내장된 객체 속성별 필터링
포함된 객체 는 단일 특정 상위 객체 내에서 중첩된 데이터 역할을 합니다. 포함된 객체를 직접 쿼리하거나 상위 객체의 속성으로 쿼리할 수 있습니다. 내장된 객체를 직접 쿼리하는 방법에 대한 자세한 내용은 이 페이지 의 모든 객체 유형 쿼리 섹션을 참조하세요.
내장된 객체를 상위 객체를 통해 찾으려면 상위 객체 유형을 유형 매개변수로 전달하고 점 표기법을 사용하여 내장된 객체 속성으로 필터링합니다.
다음 예에는 address
라는 내장된 객체 속성이 포함된 Contact
상위 객체가 있습니다. 내장된 객체의 address.street
속성에 대해 Contact
객체 유형을 쿼리합니다.
// Use dot notation to access the embedded object properties as if it // were in a regular nested object val filterEmbeddedObjectProperty = realm.query<Contact>("address.street == '123 Pond St'") // You can also access properties nested within the embedded object val queryNestedProperty = realm.query<Contact>() .query("address.propertyOwner.name == $0", "Mr. Frog")
RealmAny(혼합) 속성별 필터링
RealMany(혼합) 속성은 특정 시점에 지원되는 데이터 유형 중 하나를 포함할 수 있는 다형성 값을 나타냅니다. 다른 속성과 동일한 방식으로 RealmAny
속성을 쿼리할 수 있습니다.
다음 예제에서는 유형 Int
를 가장 좋아하는 개구리에 대해 favoriteThing
이라는 RealmAny
속성을 쿼리합니다.
val filterByRealmAnyInt = realm.query<Frog>("favoriteThing.@type == 'int'") val findFrog = filterByRealmAnyInt.find().first()
다른 속성과 달리 RealmAny
속성을 사용하려면 먼저 저장된 값을 추출해야 합니다. 값을 추출하려면 저장된 유형에 대한 SDK의 게터 메서드를 사용합니다. 유형에 잘못된 게터를 사용하면 SDK에서 예외가 발생합니다.
가장 좋은 방법은 표현식 을 사용하여 RealmAny.type() 으로 현재 저장된 유형을 가져오는 것입니다. 그런 다음 유형에 따라 값을 추출합니다. 게터 메서드의 전체 목록은 RealmAny API 참조를 참조하세요.
다음 예제에서는 RealmAny.asInt() 반환된 개구리가 가장 좋아하는 값이 Int
유형 값이라는 것을 알고 있기 때문입니다.
val frogsFavoriteThing = findFrog.favoriteThing // Int // Using the correct getter method returns the value val frogsFavoriteNumber = frogsFavoriteThing?.asInt() println("${findFrog.name} likes the number $frogsFavoriteNumber")
팁
조건 표현식으로 다형성 처리하기
조건 when
표현식을 사용하여 지정된 RealmAny
속성의 가능한 내부 값 클래스를 처리합니다.
// Handle possible types with a 'when' statement frogsFavoriteThings.forEach { realmAny -> if (realmAny != null) { when (realmAny.type) { RealmAny.Type.INT -> { val intValue = realmAny.asInt() // Do something with intValue ... } RealmAny.Type.STRING -> { val stringValue = realmAny.asString() // Do something with stringValue ... } RealmAny.Type.OBJECT -> { val objectValue = realmAny.asRealmObject(Frog::class) // Do something with objectValue ... } // Handle other possible types... else -> { // Debug or perform a default action for unhandled types Log.d("Unhandled type: ${realmAny.type}") } } } }
현재 저장된 값이 있으면 해당 유형의 다른 값과 동일한 방식으로 작업할 수 있습니다.
참고
Byte
, Char
, Int
, Long
및 Short
값은 내부적으로 int64_t
값으로 변환됩니다. 이러한 유형의 RealmAny
값을 비교, 정렬 또는 집계할 때는 이 점을 염두에 두어야 합니다.
다시 매핑된 속성별 필터링
데이터 모델에 다시 매핑된 속성 이름이 포함된 경우 코드에 사용된 코틀린 속성 이름과 데이터베이스에 유지된 다시 매핑된 속성 이름을 모두 기준으로 필터링할 수 있습니다.
다음 예에서 Frog
객체에는 데이터베이스의 latin_name
에 다시 매핑되는 코드에 species
라는 속성이 있습니다.
var species: String? = null // Remapped property
데이터베이스에서 속성 이름을 기준으로 필터링하여 동일한 결과를 반환할 수 있습니다.
val filterByKotlinName = realm.query<Frog>("species == $0", "Muppetarium Amphibius") val findSpecies = filterByKotlinName.find().first() val filterByRemappedName = realm.query<Frog>("latin_name == $0", "Muppetarium Amphibius") val find_latin_name = filterByRemappedName.find().first() // Both queries return the same object assertEquals(findSpecies, find_latin_name)
Full Text Search(FTS) 속성별 필터링
버전 1.11.0의 변경된 기능: 접두사 와일드카드 검색 지원
데이터 모델에 Full Text Search(FTS) 인덱스 속성이 포함된 경우 TEXT
조건자를 사용하여 속성을 기준으로 필터링할 수 있습니다. 쿼리의 단어는 다음 규칙을 사용하여 토크나이저에 의해 토큰으로 변환됩니다:
토큰은 ASCII 및 Latin-1 Supplement(서양 언어)의 문자로만 구성될 수 있습니다. 그 외의 모든 문자는 공백으로 간주됩니다.
하이픈(
-
)으로 나뉜 단어는 두 개의 토큰으로 나뉩니다. 예를 들어full-text
는full
과text
로 분할됩니다.토큰은 발음 부호 및 대소문자를 구분하지 않습니다.
전체 단어나 구문을 검색하거나 다음 문자로 결과를 제한할 수 있습니다.
단어 앞에
-
문자를 붙여서 해당 단어에 대한 결과를 제외합니다. 예를 들어fiction -science
에는fiction
에 대한 모든 검색 결과가 포함되고science
단어가 포함된 검색 결과는 제외됩니다.Kotlin SDK 버전 1.11.0 이상에서는 단어 끝에
*
기호를 배치하여 접두사로 지정할 수 있습니다. 예를 들어fict*
에는fiction
및fictitious
에 대한 모든 검색 결과가 포함됩니다. (Kotlin SDK는 현재 접미사 검색을 지원하지 않습니다.)
SDK는 관련성 기반 일치 대신 지정된 쿼리에 대한 부울 일치를 반환합니다.
다음 예시에서 Frog
객체 유형에는 physicalDescription
이라는 FTS 인덱스 속성이 있으며, 이를 기준으로 필터링하여 다양한 유형의 개구리를 찾을 수 있습니다.
// Filter by FTS property value using 'TEXT' // Find all frogs with "green" in the physical description val onlyGreenFrogs = realm.query<Frog>("physicalDescription TEXT $0", "green").find() // Find all frogs with "green" but not "small" in the physical description val onlyBigGreenFrogs = realm.query<Frog>("physicalDescription TEXT $0", "green -small").find() // Find all frogs with "muppet-" and "rain-" in the physical description val muppetsInTheRain = realm.query<Frog>("physicalDescription TEXT $0", "muppet* rain*").find()
컬렉션(목록, 세트, 사전) 속성별 필터링
객체 유형을 정의하는 방식에 따라 다음의 지원되는 컬렉션 유형 중 하나로 정의된 속성이 있을 수 있습니다.
RealmList
RealmSet
RealmDictionary
RQL을 사용하여 다른 속성과 동일한 방식으로 이러한 컬렉션 속성을 쿼리할 수 있습니다. 코틀린에 내장된 컬렉션 함수를 사용하여 결과를 필터링, 정렬, 반복할 수도 있습니다.
RealmList
코틀린( 코틀린 (Kotlin) ) 목록 과 마찬가지로 RealmList속성 을 쿼리 하고 반복할 수 있습니다.
다음 예에서는 favoritePonds
라는 RealmList
속성을 쿼리합니다.
// Find frogs with a favorite pond val allFrogs = query<Frog>().find() val frogsWithFavoritePond = allFrogs.query("favoritePonds.@size > $0", 0).find() // Check if the list contains a value for (frog in frogsWithFavoritePond) { val likesBigPond = frog.favoritePonds.any { pond -> pond.name == "Big Pond" } if (likesBigPond) { Log.v("${frog.name} likes Big Pond") } else { Log.v("${frog.name} does not like Big Pond") } }
RealmSet
코틀린( Kotlin) 세트 와 마찬가지로 RealmSet속성을 쿼리하고 반복할 수 있습니다.
다음 예에서는 favoriteSnacks
라는 RealmSet
속성을 쿼리합니다.
// Find frogs with flies and crickets as a favorite snack val filterBySnackSet = query<RealmSet_Frog>("favoriteSnacks.name CONTAINS $0 AND favoriteSnacks.name CONTAINS $1", "Flies", "Crickets") val potentialFrogs = filterBySnackSet.find() // Check if the set contains a value val frogsThatLikeWorms = potentialFrogs.filter { frog -> val requiredSnacks = query<RealmSet_Snack>("name == $0", "Worms") frog.favoriteSnacks.contains(requiredSnacks.find().first()) } for (frog in frogsThatLikeWorms) { Log.v("${frog.name} likes both Flies, Worms, and Crickets") }
RealmDictionary
코틀린( Kotlin) 지도 와 마찬가지로 RealmDictionary속성을 쿼리하고 반복할 수 있습니다.
다음 예시에서는 String
키(숲)를 String
값(연못)에 매핑하는 favoritePondsByForest
라는 RealmDictionary
속성을 쿼리합니다.
// Find frogs who have forests with favorite ponds val frogs = realm.query<Frog>().find() val frogsWithFavoritePonds = frogs.query("favoritePondsByForest.@count > $0", 1).find() val thisFrog = frogsWithFavoritePonds.first() // Iterate through the map and log each key-value pair for (forestName in thisFrog.favoritePondsByForest.keys) { val pondName = thisFrog.favoritePondsByForest[forestName] Log.v("Forest: $forestName, Pond: $pondName") } // Check if the dictionary contains a key if (thisFrog.favoritePondsByForest.containsKey("Hundred Acre Wood")) { Log.v("${thisFrog.name}'s favorite pond in Hundred Acre Wood is ${thisFrog.favoritePondsByForest["Hundred Acre Wood"]}") } // Check if the dictionary contains a value if (thisFrog.favoritePondsByForest.containsValue("Picnic Pond")) { Log.v("${thisFrog.name} lists Picnic Pond as a favorite pond") }
관계 속성별 필터링
객체 유형을 정의하는 방법에 따라 다른 데이터베이스 객체를 참조하는 속성이 있을 수 있습니다. 이는 to-one, to-many 또는 역관계일 수 있습니다.
내장된 객체 를 참조하는 관계 속성을 기준으로 필터링하는 방법에 대한 자세한 내용은 이 페이지의 내장된 객체 속성 으로 필터링 섹션을 참조하세요.
To-One 관계
To-One 관계 속성은 다른 객체 유형의 단일 인스턴스에 매핑됩니다. 중첩된 객체와 같은 방식으로 점 표기법을 사용하여 관계 속성을 기준으로 필터링할 수 있습니다.
다음 예시에서 Frog
객체 유형에는 Pond
유형의 favoritePond
라는 속성이 있습니다.
// Find frogs who have a favorite pond val allFrogs = query<Frog>().find() val frogsWithFavoritePond = allFrogs.query("favoritePond.@count == $0", 1).find() // Iterate through the results for (frog in frogsWithFavoritePond) { Log.v("${frog.name} likes ${frog.favoritePond?.name}") }
To-Many 관계
To-many 관계 속성은 다른 객체 유형의 컬렉션(RealmList 또는 RealmSet)입니다. 다른 컬렉션 속성과 동일한 방식으로 관계 속성을 기준으로 필터링하고 반복할 수 있습니다.
다음 예에서 Forest
객체 유형에는 Pond
유형의 RealmList
인 nearbyPonds
라는 속성이 있습니다.
// Find all forests with at least one nearby pond val allForests = query<Forest>().find() val forestsWithPonds = allForests.query("nearbyPonds.@count > $0", 0).find() // Iterate through the results for (forest in forestsWithPonds) { val bigPond = query<Pond>("name == $0", "Big Pond").find().first() if (forest.nearbyPonds.contains(bigPond)) { Log.v("${forest.name} has a nearby pond named ${bigPond.name}") } else { Log.v("${forest.name} does not have a big pond nearby") } }
역관계
to-one 및 to-many 관계와 달리 역관계는 상위 객체와 하위 객체 간에 자동으로 백링크를 만듭니다. 즉, 상위와 하위 모두에 대해 언제든지 쿼리할 수 있습니다. RQL 전용 구문을 사용하여 백링크를 쿼리할 수도 있습니다(자세한 내용은 백링크 쿼리 참조).
다음 예에서 User
유형의 상위 객체는 Post
유형의 하위 객체와 역관계를 갖습니다. 상위 객체의 User.posts
관계("사용자가 많은 게시물을 가지고 있음") 및 역 Post.user
관계("게시물은 사용자에 속함")를 쿼리할 수 있습니다.
// Query the parent object val filterByUserName = query<User>("name == $0", "Kermit") val kermit = filterByUserName.find().first() // Use dot notation to access child objects val myFirstPost = kermit.posts[0] // Iterate through the backlink collection property kermit.posts.forEach { post -> Log.v("${kermit.name}'s Post: ${post.date} - ${post.title}") } // Filter posts through the parent's backlink property // using `@links.<ObjectType>.<PropertyName>` syntax val oldPostsByKermit = realm.query<Post>("date < $0", today) .query("@links.User.posts.name == $0", "Kermit") .find() // Query the child object to access the parent val post1 = query<Post>("title == $0", "Forest Life").find().first() val post2 = query<Post>("title == $0", "Top Ponds of the Year!").find().first() val parent = post1.user.first()
중요
다시 매핑된 클래스 이름으로 역관계 쿼리
역관계 속성이 다시 매핑된(지속된) 클래스 이름을 가진 객체 유형인 경우, 원시 RQL 쿼리에서 다시 매핑된 클래스 이름을 반드시 사용해야 합니다.
class User : RealmObject { var _id: ObjectId = ObjectId() var name: String = "" var posts: RealmList<Post> = realmListOf() }
// Filter by the remapped object type name // using `@links.<RemappedObjectType>.<PropertyName>` syntax val postsByKermit = realm.query<Post>() .query("@links.Blog_Author.posts.name == $0", "Kermit") .find()
지리 공간적 데이터 쿼리
버전 1.11.0의 새로운 기능.
코틀린 SDK 버전 1.11.0 이상에는 지리 공간적 데이터 쿼리를 지원하는 실험적인 지리 공간적 API가 추가되었습니다.
참고
지리 공간적 데이터를 유지하려면 데이터 모델에 사용자 지정 GeoJSON 호환 포함 클래스를 정의해야 합니다. 요구 사항에 대한 자세한 내용은 지리 공간적 데이터 유지를 참조하세요.
지리 공간적 데이터는 내장된 사용자 지정 객체의 coordinates
속성에서 위도/경도 쌍으로 유지됩니다. 지리 공간적 쿼리는 coordinates
속성으로 정의된 점이 정의된 지리 공간적 도형의 경계 내에 포함되어 있는지 여부를 확인합니다.
SDK는 다음과 같은 지리 공간적 도형을 지원합니다.
GeoCircle
: 중심GeoPoint
와 반지름Distance
로 정의됩니다.GeoBox
: 상자의 남서쪽과 북동쪽 모서리를 나타내는 두 개의GeoPoint
좌표로 정의됩니다.GeoPolygon
: 닫힌 도형을 나타내는 최소 4개의GeoPoint
좌표 세트로 정의됩니다. 이 도형에는 정의된 다각형 범위 내의 배타적 경계를 나타내는 구멍이 포함될 수 있습니다.
지리 공간적 도형 및 도형을 정의하는 방법에 대한 자세한 내용은 지리 공간적 데이터 - 코틀린 SDK를 참조하세요.
지리 공간적 데이터를 쿼리하려면 다음을 수행합니다.
내장된 지리 공간적 데이터를 포함하는 객체를 만듭니다.
지리 공간적 도형을 정의하여 쿼리의 경계를 설정합니다.
GEOWITHIN
RQL 연산자를 사용하여 쿼리합니다. 이 메서드는 GeoJSON 호환 내장된 객체의coordinates
속성을 가져와 해당 점이 정의된 도형의 경계 내에 포함되어 있는지 확인합니다. 구문은 지리 공간적 도형에 관계없이 동일합니다.
다음 예에서는 내장된 location
속성을 통해 Company
객체에 유지되는 지리 공간적 데이터를 쿼리하려고 합니다. 쿼리 경계를 설정하기 위해 두 개의 GeoCircle
객체를 생성합니다.
val circle1 = GeoCircle.create( center = GeoPoint.create(47.8, -122.6), radius = Distance.fromKilometers(44.4) ) val circle2 = GeoCircle.create( center = GeoPoint.create(47.3, -121.9), radius = Distance.fromDegrees(0.25) )
그런 다음 정의된 GeoCircle
경계 내에 포함된 location
이 있는 모든 Company
객체를 쿼리합니다.
val companiesInLargeCircle = realm.query<Company>("location GEOWITHIN $circle1").find() println("Companies in large circle: ${companiesInLargeCircle.size}") val companiesInSmallCircle = realm.query<Company>("location GEOWITHIN $circle2").find() println("Companies in small circle: ${companiesInSmallCircle.size}")
Companies in large circle: 1 Companies in small circle: 0
결과 정렬 및 제한
결과가 예상대로 반환되도록 하려면 RQL 정렬, 제한 및 고유 연산자, 다음 SDK 편의 메서드 중 하나 또는 이 모두를 조합하여 정렬 순서 및 제한 조건을 지정할 수 있습니다.
중요
알림 순서의 중요성
RQL을 사용하든 편의 메서드를 사용하든 관계없이 SDK는 쿼리에 추가된 순서대로 각 요청을 실행합니다. 이는 반환되는 결과에 영향을 미칠 수 있습니다. 예를 들어 쿼리를 제한하기 전에 정렬하면 제한 후 정렬하는 것과 매우 다른 결과가 반환될 수 있습니다.
다음 예에서는 편의 메서드만 사용하여 정렬하고 제한하는 경우, RQL만 사용하여 정렬하고 제한하는 경우, 그리고 두 가지를 조합하여 동일한 결과를 반환하는 경우를 살펴봅니다.
// Query for all frogs owned by Jim Henson, then: // 1. Sort results by age in descending order // 2. Limit results to only distinct names // 3. Limit results to only the first 2 objects val organizedWithMethods = realm.query<Frog>("owner == $0", "Jim Henson") .sort("age", Sort.DESCENDING) .distinct("name") .limit(2) .find() organizedWithMethods.forEach { frog -> Log.v("Method sort: ${frog.name} is ${frog.age}") } val organizedWithRql = realm.query<Frog>() .query("owner == $0 SORT(age DESC) DISTINCT(name) LIMIT(2)", "Jim Henson") .find() organizedWithRql.forEach { frog -> Log.v("RQL sort: ${frog.name} is ${frog.age}") } val organizedWithBoth = realm.query<Frog>() .query("owner == $0 SORT(age DESC)", "Jim Henson") .distinct("name") .limit(2) .find() organizedWithBoth.forEach { frog -> Log.v("Combined sort: ${frog.name} is ${frog.age}") }
Method sort: Kermit, Sr. is 100 Method sort: Kermit is 42 RQL sort: Kermit, Sr. is 100 RQL sort: Kermit is 42 Combined sort: Kermit, Sr. is 100 Combined sort: Kermit is 42
참고
문자열 정렬 및 대소문자를 구분하지 않는 퀴리는 'Latin Basic', 'Latin Supplement', 'Latin Extended A', 'Latin Extended B'(UTF-8 범위 0-591)의 문자에 대해서만 지원합니다.
결과 집계
또한 결과를 집계하여 지정된 수치 속성이나 컬렉션을 기반으로 결과를 단일 값으로 줄일 수도 있습니다. RQL 집계 연산자, 다음 편의 메서드 중 하나를 사용하거나 두 가지를 조합하여 사용할 수 있습니다.
다음 예에서는 Frog
객체 유형의 age
속성을 집계합니다.
val jimHensonsFrogs = realm.query<Frog>("owner == $0", "Jim Henson") // Find the total number of frogs owned by Jim Henson val numberOfJimsFrogs = jimHensonsFrogs.count().find() // Find the oldest frog owned by Jim Henson val maxAge = jimHensonsFrogs.max<Int>("age").find() val oldestFrog = jimHensonsFrogs.query("age == $0", maxAge).find().first()
흐름을 사용하여 결과 반복
코틀린 (Kotlin) 루틴 흐름을사용하여 결과를 반복할 수 있습니다.
쿼리 결과를 비동기 Flow
로 변환하려면 쿼리에서 asFlow() 를 호출합니다. SDK는 flow.collect() 를 사용하여 반복할 수 있는 ResultsChange 를 반환합니다.Flow
다음 예시에서는 Frog
객체의 Flow
를 반복합니다.
// Get a Flow of all frogs in the database val allFrogsQuery = realm.query<Frog>() val frogsFlow: Flow<ResultsChange<Frog>> = allFrogsQuery.asFlow() // Iterate through the Flow with 'collect()' val frogsObserver: Deferred<Unit> = async { frogsFlow.collect { results -> when (results) { is InitialResults<Frog> -> { for (frog in results.list) { Log.v("Frog: $frog") } } else -> { /* no-op */ } } } } // ... Later, cancel the Flow, so you can safely close the database frogsObserver.cancel() realm.close()
팁
플로우를 구독하여 변경 사항 수신
쿼리에서 Flow
를 생성한 후 알림 핸들러를 등록하여 ResultsChanges
에 대한 변경 사항을 수신할 수 있습니다. 자세한 내용은 변경 사항에 대응 - 코틀린 SDK를 참조하세요.