Docs Menu
Docs Home
/ /
Atlas Device SDK
/ / /

Realm 객체 읽기 - Kotlin SDK

이 페이지의 내용

  • 읽기 작업
  • 고정 및 실시간 결과
  • 데이터베이스 객체 찾기
  • 객체의 최신 버전 찾기
  • 특정 유형의 모든 객체 쿼리
  • 단일 객체 쿼리
  • 속성별 필터링
  • 프라이머리 키별 필터링
  • 내장된 객체 속성별 필터링
  • RealmAny(혼합) 속성별 필터링
  • 다시 매핑된 속성별 필터링
  • Full Text Search(FTS) 속성별 필터링
  • 컬렉션(목록, 세트, 사전) 속성별 필터링
  • 관계 속성별 필터링
  • 지리 공간적 데이터 쿼리
  • 결과 정렬 및 제한
  • 결과 집계
  • 흐름을 사용하여 결과 반복

이 페이지에서는 코틀린(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)

데이터에 액세스하고 반환된 결과로 작업할 준비가 되면 쿼리를 실행합니다.

조회된 결과는 실제로 일치하는 데이터베이스 객체를 메모리에 보유하지 않습니다. 대신 데이터베이스는 직접 참조 또는 포인터를 사용합니다. 결과 컬렉션 또는 흐름의 데이터베이스 객체는 데이터베이스 파일의 데이터에 직접 매핑되는 일치하는 객체를 참조합니다. 이는 또한 쿼리 결과를 통해 객체의 관계 그래프를 직접 탐색할 수 있다는 의미이기도 합니다.

예시

쿼리 실행

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

데이터베이스 내에 저장된 객체를 찾으려면 다음을 수행합니다.

  1. 객체 유형을 유형 매개 변수로 query() 에 전달합니다. 객체 유형이 데이터베이스 스키마에 이미 포함되어 있어야 합니다.

  2. 원하는 경우 쿼리 조건을 전달하여 결과를 더욱 세분화합니다.

    • 조건을 충족하는 객체만 반환하도록 필터를 지정합니다. 필터를 지정하지 않으면 SDK는 지정된 유형의 모든 객체를 반환합니다.

      RealmQuery 에 추가 query() 메서드를 추가하여 필터를 연결할 수 있습니다.

    • 결과의 정렬 순서를 지정합니다. 데이터베이스는 정렬되지 않은 상태이므로 정렬 순서를 포함하지 않으면 SDK는 쿼리가 특정 순서로 객체를 반환한다는 것을 보장할 수 없습니다.

  3. 다음 중 하나를 사용하여 쿼리를 실행합니다.

    • 동기 쿼리의 경우 find() . 결과 컬렉션 을 반환합니다.

    • 비동기 쿼리의 경우 asFlow() 를 호출합니다. 결과 변경 사항의 Flow 을(를) 구독합니다.

    대규모 데이터 세트의 경우 asFlow() 사용 선호

    find() 호출된 스레드에서 동기 쿼리를 실행합니다. 따라서 UI 스레드 또는 UI 스레드를 지연시킬 수 있는 로직에서는 대규모 데이터 세트에 대해 find() 사용을 피해야 합니다.

    성능이나 UI에 부정적인 영향을 주지 않도록 asFlow()를 선호합니다.

  4. 결과로 작업합니다. 실행한 쿼리 유형에 따라 객체가 고정되거나 실시간 상태일 수 있습니다.

예시

읽기 작업

// 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 쿼리의 RealmResultsfindLatest()로 전달하여 컬렉션의 최신 실시간 사본을 가져옵니다. 그런 다음 쓰기 트랜잭션 내에서 현재 실행 중인 객체를 수정합니다.

// 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 속성을 쿼리할 수 있습니다.

다음 예제에서는 유형 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, LongShort 값은 내부적으로 int64_t 값으로 변환됩니다. 이러한 유형의 RealmAny 값을 비교, 정렬 또는 집계할 때는 이 점을 염두에 두어야 합니다.

데이터 모델에 다시 매핑된 속성 이름이 포함된 경우 코드에 사용된 코틀린 속성 이름과 데이터베이스에 유지된 다시 매핑된 속성 이름을 모두 기준으로 필터링할 수 있습니다.

다음 예에서 Frog 객체에는 데이터베이스의 latin_name에 다시 매핑되는 코드에 species라는 속성이 있습니다.

@PersistedName("latin_name")
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)

버전 1.11.0의 변경된 기능: 접두사 와일드카드 검색 지원

데이터 모델에 Full Text Search(FTS) 인덱스 속성이 포함된 경우 TEXT 조건자를 사용하여 속성을 기준으로 필터링할 수 있습니다. 쿼리의 단어는 다음 규칙을 사용하여 토크나이저에 의해 토큰으로 변환됩니다:

  • 토큰은 ASCII 및 Latin-1 Supplement(서양 언어)의 문자로만 구성될 수 있습니다. 그 외의 모든 문자는 공백으로 간주됩니다.

  • 하이픈(-)으로 나뉜 단어는 두 개의 토큰으로 나뉩니다. 예를 들어 full-textfulltext로 분할됩니다.

  • 토큰은 발음 부호 및 대소문자를 구분하지 않습니다.

전체 단어나 구문을 검색하거나 다음 문자로 결과를 제한할 수 있습니다.

  • 단어 앞에 - 문자를 붙여서 해당 단어에 대한 결과를 제외합니다. 예를 들어 fiction -science에는 fiction에 대한 모든 검색 결과가 포함되고 science 단어가 포함된 검색 결과는 제외됩니다.

  • Kotlin SDK 버전 1.11.0 이상에서는 단어 끝에 * 기호를 배치하여 접두사로 지정할 수 있습니다. 예를 들어 fict*에는 fictionfictitious에 대한 모든 검색 결과가 포함됩니다. (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을 사용하여 다른 속성과 동일한 방식으로 이러한 컬렉션 속성을 쿼리할 수 있습니다. 코틀린에 내장된 컬렉션 함수를 사용하여 결과를 필터링, 정렬, 반복할 수도 있습니다.

코틀린( 코틀린 (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")
}
}

코틀린( 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")
}

코틀린( 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 관계 속성은 다른 객체 유형의 단일 인스턴스에 매핑됩니다. 중첩된 객체와 같은 방식으로 점 표기법을 사용하여 관계 속성을 기준으로 필터링할 수 있습니다.

다음 예시에서 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 관계 속성은 다른 객체 유형의 컬렉션(RealmList 또는 RealmSet)입니다. 다른 컬렉션 속성과 동일한 방식으로 관계 속성을 기준으로 필터링하고 반복할 수 있습니다.

다음 예에서 Forest 객체 유형에는 Pond 유형의 RealmListnearbyPonds라는 속성이 있습니다.

// 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 쿼리에서 다시 매핑된 클래스 이름을 반드시 사용해야 합니다.

@PersistedName(name = "Blog_Author")
class User : RealmObject {
@PrimaryKey
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를 참조하세요.

지리 공간적 데이터를 쿼리하려면 다음을 수행합니다.

  1. 내장된 지리 공간적 데이터를 포함하는 객체를 만듭니다.

  2. 지리 공간적 도형을 정의하여 쿼리의 경계를 설정합니다.

  3. 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
GeoCircle 쿼리 예시
클릭하여 확대

결과가 예상대로 반환되도록 하려면 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를 참조하세요.

돌아가기

만들기