Menu Docs
Página inicial do Docs
/ /
Atlas Device SDKs
/ / /

Ler objetos de domínio - Kotlin SDK

Nesta página

  • Ler operações
  • Resultados congelados e ao vivo
  • Localizar objetos de banco de dados
  • Localizar a versão mais recente de um objeto
  • Fazer query de todos os objetos de um tipo
  • Fazer query de um único objeto
  • Filtrar por propriedade
  • Filtrar por chave primária
  • Filtrar por propriedade de objeto embarcado
  • Filtrar por propriedade RealmAny (misto)
  • Filtrar por propriedade remapeada
  • Filtrar por propriedade de Full-Text Search (FTS)
  • Filtrar por propriedade de collection (lista, conjunto, dicionário)
  • Filtrar por propriedade de relacionamento
  • Executar query de dados geoespaciais
  • Classificar e limitar resultados
  • Resultados agregados
  • Iterar resultados usando o Flow

Esta página descreve como fazer query e ler objetos persistentes em um banco de dados com o Atlas Device SDK para Kotlin. Essa avaliação lenta permite a eficiência e o desempenho de código ao gerenciar grandes conjuntos de dados e queries complexas.

Uma operação de leitura consiste em fazer query de objetos do banco de dados e, em seguida, executar a query quando estiver pronto para acessar os resultados. A sintaxe para operações de leitura é a mesma para bancos de dados sincronizados e não sincronizados.

Todas as queries são baseadas no tipo de objeto. Você pode fazer query de qualquer objeto, incluindo objetos embarcados, que persistam para o banco de dados e cujo tipo está incluído em seu esquema de banco de dados.

Construct queries using the SDK's query builder RealmQuery, passing the object type as the type parameter. You can query objects on a Realm or MutableRealm instance, a RealmResults collection, or a RealmList collection.

Um RealmQuery básico retorna todos os objetos do tipo especificado:

// Finds all objects of type <T>
.query<T>()

Você pode otimizar sua query com filtros e condições adicionais (por exemplo, classificar, agregar, limitar resultados). Você pode otimizar os resultados por uma ou mais propriedades usando o Realm Query Language (RQL), uma linguagem de query baseada em strings, junto com funções de extensão Kotlin incorporadas e métodos de ajuda fornecidos pelo SDK.

// Finds all objects of type <T> that meet the filter conditions
// and returns them in the specified sort order
.query<T>(filter).sort(sort)

Você também pode encadear queries usando métodos query() adicionais. Cada query() anexado atua como uma condição de query AND . E, devido à avaliação preguiçosa do SDK, queries sucessivas não exigem viagens separadas ao banco de dados.

// Finds all objects of type <T> that meet the first filter conditions
// AND the subsequent filter conditions
.query<T>(filter).query(filter)

Quando você estiver pronto para acessar os dados e trabalhar com os resultados retornados, execute a query:

  • Use find() para executar uma query síncrona. O SDK retorna de forma lenta uma coleção RealmResults, que representa todos os objetos do banco de dados que correspondem às condições da query. Em geral, você pode trabalhar com uma coleção de resultados como qualquer outra coleção Kotlin.

  • Use asFlow() para executar uma query assíncrona. O SDK se inscreve preguiçosamente em um fluxo de Kotlin Coroutine que você pode coletar e iterar ou ouvir as alterações. Você não pode ligar para asFlow() em um MutableRealm.query.

Observe que quaisquer resultados recuperados na verdade não contêm objetos de banco de dados correspondentes na memória. Em vez disso, o banco de dados utiliza referências diretas ou ponteiros. Os objetos de banco de dados em uma collection de resultados ou flow fazem referência aos objetos correspondentes, que mapeiam diretamente os dados no arquivo de banco de dados. Isso também significa que você pode percorrer o gráfico dos relacionamentos de um objeto diretamente por meio dos resultados de uma query.

Exemplo

Executar a query

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

Ao contrário de outros Atlas Device SDKs, que sempre retornam resultados ao vivo, os resultados com o Kotlin SDK podem ser congelados ou ao vivo. Para obter mais informações sobre a arquitetura congelada do Kotlin SDK, consulte Arquitetura congelada - Kotlin SDK.

Para acessar os resultados congelados, execute uma consulta em um Realm. Os resultados congelados não podem ser modificados e não refletem as alterações mais recentes no banco de dados. Um Realm.query() não exige uma transação de escrita.

Para acessar os resultados em tempo real, execute uma query em uma instância do MutableRealm em uma transação de escrita. Um MutableRealm representa um estado gravável de um banco de dados e é acessível por meio de uma transação de escrita. Os resultados de um MutableRealm.query estão ativos, mas só são válidos no thread de chamada e são congelados quando a transação de escrita é concluída. Para obter mais informações sobre transações de gravação e acesso a um MutableRealm, consulte Transações de gravação.

Você também pode acessar objetos ativos a partir de resultados congelados chamando MutableRealm.findLatest() . Para obter mais informações, consulte a seção Localizar a versão mais recente de um objeto nesta página.

Exemplo

Acesse resultados ao vivo

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

Para localizar objetos armazenados em um banco de dados:

  1. Passe o tipo de objeto como um parâmetro de tipo para query(). O tipo de objeto já deve estar incluído no esquema do banco de dados.

  2. Você também pode passar todas as condições de query para otimizar ainda mais os resultados:

    • Especifique um filtro para retornar somente objetos que atendam à condição. Se você não especificar um filtro, o SDK retornará todos os objetos do tipo especificado.

      Você pode encadear filtros anexando métodos query() adicionais ao RealmQuery.

    • Especifique a ordem de classificação para os resultados. Como o banco de dados não está classificado, se você não incluir uma ordem de classificação, o SDK não poderá garantir que a query retorne objetos em uma ordem específica.

  3. Execute a query usando um dos seguintes métodos:

    • find() para queries síncronas. Retorna uma coleção de resultados.

    • asFlow() para queries assíncronas. Assina um Flow de alterações de resultados.

    Dica

    Use asFlow() para grandes conjuntos de dados

    find() executa uma query síncrona no thread do qual é chamado. Como resultado, evite usar find() para conjuntos de dados grandes no thread da UI ou em lógica que possa atrasar o thread da UI.

    Use asFlow() para evitar um impacto negativo no desempenho ou na UI.

  4. Trabalhe com os resultados. Os objetos podem estar congelados ou ao vivo, dependendo do tipo de query que você executou.

Exemplo

Ler operação

// 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

Devido à arquitetura congelada do SDK, você nem sempre trabalhará com a versão mais recente de um objeto ou collection.

Para obter uma versão de um objeto ou coleção que reflita as alterações mais recentes no banco de dados, você pode chamar findLatest() de uma instância do MutableRealm . Como um MutableRealm.query(), os resultados estão ativos, mas são válidos apenas no tópico de chamada e são congelados quando a transação de escrita é concluída.

No exemplo abaixo, passamos uma query frozenFrogs existente de RealmResults para findLatest() para obter a cópia ao vivo mais recente da collection. Em seguida, modificamos os objetos ao vivo ativos na transação de escrita:

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

Dica

Você pode verificar se um objeto está congelado usando o método isFrozen().

val isFrozen = frog.isFrozen()

Para fazer query de todos os objetos de um tipo específico, passe o tipo de objeto RealmObject ou EmbeddedRealmObject como um parâmetro de tipo para query() sem quaisquer argumentos de query. O SDK retorna todos os objetos do tipo especificado.

Observação

Não é possível ler objetos assimétricos

Não é possível ler objetos assimétricos porque eles são objetos especiais de somente gravação que não persistem no banco de dados. Para obter informações sobre como usar objetos assimétricos em seu aplicativo, consulte Fluxo de dados para o Atlas - Kotlin SDK.

No exemplo abaixo, fazemos query de todos os objetos RealmObject do tipo Frog:

// Query all Frog objects in the database
val queryAllFrogs = realm.query<Frog>()
val allFrogs = queryAllFrogs.find()

No exemplo abaixo, fazemos query de todos os objetos EmbeddedRealmObject do tipo EmbeddedAddress:

val queryAllEmbeddedAddresses = realm.query<EmbeddedAddress>()
val allEmbeddedAddresses = queryAllEmbeddedAddresses.find()

Você também pode fazer query de um objeto incorporado por meio de seu objeto principal. Para obter mais informações, consulte a seção Filtrar por propriedade de objeto incorporado nesta página.

Dica

Depois de localizar um objeto embarcado, você pode usar o método EmbeddedRealmObject.parent() para acessar o pai correspondente:

val getParent = embeddedObject.parent<Contact>()

Para encontrar um único objeto de um tipo de objeto específico, chame first() na query. Quando você executa a consulta, o SDK retorna o primeiro objeto que corresponde às condições ou null.

No exemplo abaixo, fazemos query em um tipo de objeto Frog e retornamos o primeiro objeto:

val querySingleFrog = realm.query<Frog>().first()
val singleFrog = querySingleFrog.find()
if (singleFrog != null) {
println("${singleFrog.name} is a frog.")
} else {
println("No frogs found.")
}

Você pode filtrar uma query por qualquer propriedade no tipo de objeto que persiste no banco de dados. Isso inclui propriedades secundárias, às quais você pode se referir usando a notação de ponto.

Para filtrar por propriedade, você pode passar por filtros e operadores da Realm Query Language (RQL), usar os métodos de extensão integrados do Kotlin ou os métodos de conveniência do SDK, ou usar uma combinação. Para obter informações sobre todos os operadores e sintaxe de RQL atualmente suportados, consulte a documentação de referência.

No exemplo abaixo, fazemos query em um tipo de objeto Frog e filtramos pela propriedade name:

val filterByProperty = realm.query<Frog>("name == $0", "Kermit")
val frogsNamedKermit = filterByProperty.find()

As chaves primárias são identificadores exclusivos de objetos em um banco de dados, o que as torna úteis para fazer query de objetos específicos.

Para filtrar por uma chave primária específica, passe o tipo de objeto como um parâmetro de tipo e faz query no campo de chave primária para o valor desejado.

No exemplo abaixo, fazemos query de um objeto Frog e filtramos pela propriedade de chave primária _id:

val filterByPrimaryKey = realm.query<Frog>("_id == $0", PRIMARY_KEY_VALUE)
val findPrimaryKey = filterByPrimaryKey.find().first()

Dica

O Device Sync sempre usa _id como chave primária

Se você usar o Atlas Device Sync, poderá sempre fazer query pelo campo de chave primária _id. Isso ocorre porque o modelo de dados do Device Sync requer que os objetos tenham uma chave primária chamada _id. Para obter mais informações, consulte Modelar dados com Device Sync - Kotlin SDK.

Objetos embarcados atuam como dados aninhados dentro de um único objeto pai específico. Você pode executar queries de um objeto embarcado diretamente ou como uma propriedade em seu objeto pai. Para obter informações sobre a query de um objeto incorporado diretamente, consulte a seção Consultar todos os objetos de um tipo nesta página.

Para localizar um objeto embarcado por meio de seu objeto principal, passe o tipo de objeto principal como um parâmetro de tipo e filtre pela propriedade de objeto embarcado usando a notação de ponto.

No exemplo abaixo, temos um objeto principal do Contact que contém uma propriedade de objeto embarcado chamada address. Fazemos query do tipo de objeto Contact em relação à propriedade address.street do objeto embarcado:

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

Uma propriedade RealmAny (Misto) representa um valor polimórfico que pode conter qualquer um dos tipos de dados aceitos em um determinado momento. Você pode fazer query de uma propriedade RealmAny da mesma forma que faria com qualquer outra propriedade.

No exemplo abaixo, fazemos query de uma propriedade RealmAny chamada favoriteThing para um sapo com uma coisa favorita do tipo Int:

val filterByRealmAnyInt = realm.query<Frog>("favoriteThing.@type == 'int'")
val findFrog = filterByRealmAnyInt.find().first()

Diferente de outras propriedades, você deve extrair o valor armazenado de uma propriedade RealmAny antes de poder trabalhar com ela. Para extrair o valor, use o método getter do SDK para o tipo armazenado. Se você usar o getter errado para o tipo, o SDK emitirá uma exceção.

Uma prática recomendada é usar uma expressão condicional para obter o tipo armazenado atual com RealmAny.type(), em seguida, extraia o valor com base no tipo. Para obter uma lista completa dos métodos getter, consulte a referência da API da RealmAny.

No exemplo a seguir, extraímos o valor usando RealmAny.asInt() já que sabemos que a coisa favorita do árbitro retornado é um valor do tipo 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")

Dica

Lidar com polimorfismo com expressões condicionais

Use uma expressão condicional when para gerenciar a possível classe de valor interno de uma determinada propriedade 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}")
}
}
}
}

Uma vez com o valor armazenado atual, você poderá usá-lo da mesma maneira que faria com outro valor desse tipo.

Observação

Byte, valores de Char, Int, Long e Short são convertidos internamente para valores de int64_t. Lembre-se disso ao comparar, classificar ou agregar valores de RealmAny destes tipos.

Se seu modelo de dados incluir nomes de propriedades remapeados, você poderá filtrar pelo nome da propriedade Kotlin usado em seu código e pelo nome da propriedade remapeada que persiste no banco de dados.

No exemplo abaixo, o objeto Frog tem uma propriedade chamada species no código que é remapeado para latin_name no banco de dados:

@PersistedName("latin_name")
var species: String? = null // Remapped property

No banco de dados, podemos filtrar por nome de propriedade e retornar os mesmos resultados:

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)

Alterado na versão 1.11.0: Suporte para pesquisas curinga de prefixo

Se o seu modelo de dados incluir uma propriedade de índice Full-Text Search (FTS), você poderá filtrar pela propriedade com o predicado do TEXT. As palavras na query são convertidas em tokens por um tokenizador usando as seguintes regras:

  • Os tokens só podem conter caracteres ASCII e o suplemento Latin-1 (idiomas ocidentais). Todos os outros caracteres são considerados espaços em branco.

  • As palavras divididas por um hífen (-) são divididas em dois tokens. Por exemplo, full-text divide em full e text.

  • Os tokens são diacríticos e não fazem distinção entre maiúsculas e minúsculas.

Você pode pesquisar uma palavra ou frase inteira ou limitar seus resultados com os seguintes caracteres:

  • Exclua os resultados de uma palavra colocando o caractere - na frente da palavra. Por exemplo, fiction -science incluiria todos os resultados de pesquisa para fiction e excluiria aqueles que incluem a palavra science.

  • No Kotlin SDK versão 1.11.0 e posterior, você pode especificar prefixos colocando o caractere * no final de uma palavra. Por exemplo, fict* incluiria todos os resultados de pesquisa para fiction e fictitious. (Atualmente, o Kotlin SDK não oferece suporte a pesquisas de sufixo.)

O SDK retorna uma correspondência boolean para a query especificada, em vez de uma correspondência baseada em relevância.

No exemplo abaixo, o tipo de objeto Frog tem uma propriedade de índice FTS chamada physicalDescription pela qual podemos filtrar para encontrar diferentes tipos de sapos:

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

Dependendo de como você define seu tipo de objeto, você pode ter propriedades definidas como um dos seguintes tipos de coleção compatíveis:

  • RealmList

  • RealmSet

  • RealmDictionary

Você pode fazer query nessas propriedades de collections da mesma forma que faria com qualquer outra propriedade usando RQL. Você também pode usar as funções de Kotlin incorporadas da collection para filtrar, classificar e iterar sobre os resultados.

Você pode fazer query e iterar por meio de uma propriedade RealmList como faria com uma Lista de Kotlin.

No exemplo abaixo, fazemos query de uma propriedade RealmList denominada favoritePonds:

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

Você pode fazer query e iterar por meio de uma propriedade RealmSet como faria com um Conjunto de Kotlin.

No exemplo abaixo, fazemos query de uma propriedade RealmSet denominada favoriteSnacks:

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

Você pode fazer query e iterar por meio de uma propriedade RealmDictionary como faria com um Mapa de Kotlin.

No exemplo abaixo, fazemos query com uma propriedade RealmDictionary denominada favoritePondsByForest, que mapeia uma chave String (floresta) para um valor String (lagoa):

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

Dependendo de como você define seu tipo de objeto, você pode ter propriedades que referenciam outro objeto do banco de dados. Esse pode ser um relacionamento para-um, para-muitos ou inverso.

Para obter informações sobre a filtragem por propriedades de relacionamento que fazem referência a um objeto incorporado, consulte a seção Filtrar por propriedade de objeto incorporado nesta página.

Uma propriedade de relacionamento para-um é mapeada para uma única instância de outro tipo de objeto. Você pode filtrar pela propriedade de relacionamento usando a notação de ponto, da mesma forma que faria com um objeto aninhado.

No exemplo abaixo, o tipo de objeto Frog tem uma propriedade denominada favoritePond do tipo Pond:

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

As propriedades de relacionamentos para-muitos são collections (um RealmList ou RealmSet) de outro tipo de objeto. Você pode filtrar e iterar pela propriedade de relacionamento da mesma forma que faria com qualquer outra propriedade de collection.

No exemplo abaixo, o tipo de objeto Forest tem uma propriedade denominada nearbyPonds que é um RealmList do tipo Pond:

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

Ao contrário dos relacionamentos para-um e para-muitos, um relacionamento inverso cria automaticamente um backlink entre objetos principais e secundários. Isso significa que você sempre pode fazer query tanto do principal quanto do secundário. Você também pode usar a sintaxe específica do RQL para fazer query de backlink (para obter mais informações, consulte Queries de backlink).

No exemplo abaixo, um objeto principal do tipo User tem um relacionamento inverso com um objeto secundário do tipo Post. Podemos fazer query do relacionamento de User.posts do objeto principal ("O usuário tem muitas postagens"), bem como o relacionamento Post.user inverso ("A postagem pertence ao usuário"):

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

Importante

Fazer query de relacionamento inverso por meio de nomes de classes remapeados

Se a propriedade de relacionamento inverso for um tipo de objeto com um nome de classe remapeado (persistente), você deverá usar o nome da classe remapeado na consulta RQL bruta.

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

Novidades na versão 1.11.0.

O Kotlin SDK versão 1.11.0 e posterior adiciona APIs geoespaciais experimentais que aceitam queries com dados geoespaciais.

Observação

Para persistir dados geoespaciais, você deve definir uma classe embutida personalizada compatível com GeoJSONS em seu modelo de dados. Para obter mais informações sobre os requisitos, consulte Persistir dados geoespaciais.

Os dados geoespaciais são persistentes como par latitude/longitude em uma propriedade coordinates de objeto embarcado personalizado. Uma query geoespacial verifica se o ponto definido pela propriedade coordinates está contido dentro do limite de uma forma geoespacial definida.

O SDK aceita as seguintes formas geoespaciais:

  • GeoCircle: definida por um centro GeoPoint e um raio Distance

  • GeoBox: definida por duas coordenadas GeoPoint que representam os cantos sudoeste e nordeste da caixa

  • GeoPolygon: definida por um conjunto de pelo menos quatro coordenadas do GeoPoint que representam uma forma fechada. Esta forma pode conter buracos que representam limites exclusivos dentro dos limites do polígono definido.

Para obter mais informações sobre formas geoespaciais e como defini-las, consulte Dados geoespaciais Kotlin SDK.

Para fazer query de dados geoespaciais:

  1. Criar um objeto contendo os dados geoespaciais incorporados.

  2. Definir a forma geoespacial para configurar o limite da query.

  3. Fazer query usando o operador GEOWITHIN RQL. Este método utiliza a propriedade coordinates do seu objeto embarcado compatível com GeoJSONV e verifica se este ponto está contido dentro do limite de uma forma definida. A sintaxe é a mesma, independente da forma geoespacial.

No exemplo abaixo, queremos fazer query de dados geoespaciais persistentes em um objeto Company por meio de sua propriedade location incorporada. Criamos dois objetos GeoCircle para definir nosso limite de query:

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

Em seguida, fazemos query de quaisquer objetos Company com um location contido nos limites definidos de GeoCircle:

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
Exemplo de query de um GeoCircle
clique para ampliar

Para garantir que os resultados sejam retornados conforme o esperado, você pode especificar uma ordem de classificação e condições de limite usando os operadores RQL Sort, Limit e Distinct, um dos métodos de conveniência do SDK abaixo ou uma combinação de ambos:

Importante

Questões importantes

Independente de você usar RQL ou métodos de conveniência, o SDK executa cada solicitação na ordem em que ela é adicionada à query. Isso pode afetar os resultados retornados. Por exemplo, a classificação de uma query antes de limitá-la pode retornar resultados muito diferentes da classificação após a limitação.

No exemplo abaixo, classificamos e limitamos usando somente métodos de conveniência, somente RQL e, em seguida, uma combinação de ambos para retornar os mesmos resultados:

// 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

Observação

Somente é permitido classificar strings e consultas sem diferenciação de maiúsculas e minúsculas para conjuntos de caracteres em 'Latin Basic', 'Latin Supplement', 'Latin Extended A' e 'Latin Extended B' (UTF-8 faixa 0-591).

Você também pode agregar resultados, o que reduz os resultados para um único valor baseado em uma propriedade numérica ou collection específica. Você pode usar operadores agregados RQL, um dos seguintes métodos de conveniência ou uma combinação de ambos:

No exemplo abaixo, agregamos a propriedade age de um tipo de objeto Frog:

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

Você pode iterar por meio dos resultados usando um Fluxo de corrotina de Kotlin.

Para converter os resultados da query em um Flow assíncrono, chame asFlow() na query. O SDK retorna um ResultsChange Flow que você pode iterar com flow.collect().

No exemplo a seguir, iteramos através de um Flow de Frog objetos:

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

Dica

Assine os fluxos para ouvir as alterações

Depois de gerar um Flow a partir de uma query, você pode registrar um manipulador de notificação para ouvir as alterações no ResultsChanges. Para obter mais informações, consulte Reagir às alterações - Kotlin SDK.

Voltar

criar

Próximo

Update