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.
Ler operações
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.
Construa queries usando o construtor de query do SDK RealmQuery, passando o tipo de objeto como o parâmetro de tipo. Você pode fazer query de objetos em uma instância Realm
ou MutableRealm
, em uma collection RealmResults
ou em uma collection RealmList
.
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 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 preguiçosamente uma coleção RealmResults , que representa todos os objetos do banco de dados 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 umMutableRealm.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()
Resultados congelados e ao vivo
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 últimas alterações no banco de banco de dados. Um Realm.query()
não exige uma transação de gravação.
Para acessar resultados em tempo real, execute uma query em uma instância de MutableRealm em uma transação de gravação. Um MutableRealm
representa um estado gravável de um banco de dados de dados e só é acessível por meio de uma transação de gravação. 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 gravação é 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()
Localizar objetos de banco de dados
Para localizar objetos armazenados em um banco de dados:
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.
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.
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 usarfind()
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.Trabalhe com os resultados. Os objetos podem estar congelados ou ao vivo, dependendo do tipo de query que você executou.
Exemplo
operação de leitura
// 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
Localizar a versão mais recente de um objeto
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()
Fazer query de todos os objetos de um tipo
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>()
Fazer query de um único objeto
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.") }
Filtrar por propriedade
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 filtros e operadores do Realm Query Language (RQL), usar os métodos de extensão incorporados do Kotlin ou os métodos de conveniência do SDK; ou usar uma combinação destes. Para obter informações sobre todos os operadores e sintaxe RQL atualmente aceitos, consulte a documentação de referência do Realm Query Language.
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()
Filtrar por chave primária
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.
Filtrar por propriedade de objeto embarcado
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")
Filtrar por propriedade RealmAny (misto)
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.
Filtrar por propriedade remapeada
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:
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)
Filtrar por propriedade de Full-Text Search (FTS)
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 emfull
etext
.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 parafiction
e excluiria aqueles que incluem a palavrascience
.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 parafiction
efictitious
. (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()
Filtrar por propriedade de collection (lista, conjunto, dicionário)
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.
Lista de domínios
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") } }
RealmSet
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") }
Dicionário Realm
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") }
Filtrar por propriedade de relacionamento
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.
Relacionamentos para-um
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}") }
Relacionamento para-muitos
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") } }
Relações inversas
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.
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()
Executar query de dados geoespaciais
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 centroGeoPoint
e um raioDistance
GeoBox
: definida por duas coordenadasGeoPoint
que representam os cantos sudoeste e nordeste da caixaGeoPolygon
: definida por um conjunto de pelo menos quatro coordenadas doGeoPoint
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:
Criar um objeto contendo os dados geoespaciais incorporados.
Definir a forma geoespacial para configurar o limite da query.
Fazer query usando o operador
GEOWITHIN
RQL. Este método utiliza a propriedadecoordinates
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
Classificar e limitar resultados
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).
Resultados agregados
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()
Iterar resultados usando o Flow
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.