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

Query MongoDB - Swift SDK

Nesta página

  • Visão geral
  • Casos de uso
  • Pré-requisitos
  • Dados de exemplo
  • Assíncrono/Aguardar query MongoDB
  • Criar documentos
  • Inserir um único documento
  • Insira vários documentos
  • Ler documentos
  • Encontrar um único documento
  • Localizar vários documentos
  • Localizar e classificar documentos
  • Contagem de documentos na coleção
  • Atualize documentos
  • Atualizar um único documento
  • Atualizar vários documentos
  • Documentos do Upsert
  • Exclua documentos
  • Excluir um único documento
  • Excluir vários documentos
  • Fique atento às mudanças
  • Fique atento às mudanças em uma coleção
  • Fique atento às alterações em uma collection para ID específicos
  • Fique atento às alterações em uma coleção com um filtro
  • Assista a uma coleção como uma sequência assíncrona
  • Documentos agregados
  • Filtrar documentos
  • Documentos do grupo
  • Campos do documento do projeto
  • Adicionar campos aos documentos
  • Unwind Array Values

Você pode fazer query dos dados armazenados no MongoDB diretamente do código do seu aplicação cliente usando Realm Swift SDK oMongoClient do com a de Query .API O Atlas App Services fornece regras de acesso a dados em coleções para recuperar resultados com segurança com base no usuário conectado ou no conteúdo de cada documento.

Dica

Veja também:

Esta página aborda a query direta de uma fonte de dados MongoDB. Para filtrar os dados recuperados de um realm, consulte: Filtrar Dados.

Existem várias razões pelas quais você pode querer fazer uma query em uma fonte de dados MongoDB. Trabalhar com dados em seu cliente via Atlas Device Sync nem sempre é prático ou possível. Talvez você queira fazer uma query no MongoDB quando:

  • O conjunto de dados for grande ou o dispositivo cliente tiver restrições para carregar todo o conjunto de dados

  • Você estiver criando ou atualizando dados de usuário personalizados

  • Você estiver recuperando documentos que não sejam modelados no Realm

  • Sua aplicação precisa acessar collections que não têm esquemas rigorosos

  • Um serviço que não é do Realm gera coleções que você deseja acessar

Embora não exaustivos, esses são alguns casos de uso comuns para a query direta do MongoDB.

Antes de poder executar uma query do MongoDB a partir do seu aplicativo cliente, você deve configurar o MongoDB Data Access no seu App Services App. Para saber como configurar seu aplicativo de backend para permitir que o Realm SDK consulte o Atlas, consulte Configurar o acesso aos dados do MongoDB na documentação do Atlas App Services .

Esses exemplos operam em uma coleção MongoDB que descreve as bebidas de café em uma cadeia de lojas de café. Os documentos representam objetos com estas propriedades:

class CoffeeDrink: Object {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var name: String
@Persisted var beanRegion: String?
@Persisted var containsDairy: Bool
@Persisted var storeNumber: Int
}

O código completo para cada exemplo inclui o login e a instanciação de um identificador de collection do MongoDB antes de concluir cada operação. Por questões de brevidade, esses exemplos omitem o código de identificador de login e collection. No entanto, cada exemplo completo tem a seguinte aparência:

appClient.login(credentials: Credentials.anonymous) { (result) in
// Remember to dispatch back to the main thread in completion handlers
// if you want to do anything on the UI.
DispatchQueue.main.async {
switch result {
case .failure(let error):
print("Login failed: \(error)")
case .success(let user):
print("Login as \(user) succeeded!")
// mongodb-atlas is the cluster service name
let mongoClient = user.mongoClient("mongodb-atlas")
// Select the database
let database = mongoClient.database(named: "ios")
// Select the collection
let collection = database.collection(withName: "CoffeeDrinks")
// This document represents a CoffeeDrink object
let drink: Document = [ "name": "Colombian Blend", "beanRegion": "Timbio, Colombia", "containsDairy": false, "storeNumber": 43]
// Insert the document into the collection
collection.insertOne(drink) { result in
switch result {
case .failure(let error):
print("Call to MongoDB failed: \(error.localizedDescription)")
return
case .success(let objectId):
// Success returns the objectId for the inserted document
print("Successfully inserted a document with id: \(objectId)")
}
}
}
}
}

Novidade na versão 10.16.0.

O Realm Swift SDK fornece versões async/await dos métodos MongoCollection.

Todos os métodos nesta página são compatíveis com a sintaxe async/await. Este exemplo ilustra a sintaxe para o método collection.insertOne() . Você pode ver a versão do manipulador de conclusão em Inserir um documento único.

// This document represents a CoffeeDrink object
let drink: Document = [ "name": "Bean of the Day", "beanRegion": "Timbio, Colombia", "containsDairy": false, "storeNumber": 43]
do {
// Use the async collection method to insert the document
let objectId = try await collection.insertOne(drink)
print("Successfully inserted a document with id: \(objectId)")
} catch {
print("Call to MongoDB failed: \(error.localizedDescription)")
}

A partir das versões 10.15.0 e 10.16.0 do SDK do Realm Swift, muitas das APIs do Realm suportam a sintaxe async/await do Swift. Os projetos devem atender a estes requisitos:

Versão do Swift SDK
Requisito de versão do Swift
Sistema operacional compatível
10.25.0
Swift 5.6
iOS 13.x
10.15.0 ou 10.16.0
Swift 5.5
iOS 15.x

Se a sua aplicação acessar Realm em um contexto do async/await, marque o código com @MainActor para evitar falhas relacionadas a threading.

Esses trechos de código demonstram como inserir um ou mais documentos em uma coleção MongoDB de um aplicativo móvel. Esses métodos usam um ou mais documentos e retornam um resultado. O sucesso retorna o ObjectId do documento inserido ou uma array de objectIds em ordem ao inserir vários documentos.

Você pode inserir um único documento usando collection.insertOne().

Este trecho insere um único documento que descreve uma bebida de café "Colombian baixa" em uma coleção de documentos que descrevem bebidas de café para venda em um grupo de lojas:

// This document represents a CoffeeDrink object
let drink: Document = [ "name": "Colombian Blend", "beanRegion": "Timbio, Colombia", "containsDairy": false, "storeNumber": 43]
// Insert the document into the collection
collection.insertOne(drink) { result in
switch result {
case .failure(let error):
print("Call to MongoDB failed: \(error.localizedDescription)")
return
case .success(let objectId):
// Success returns the objectId for the inserted document
print("Successfully inserted a document with id: \(objectId)")
}
}
Successfully inserted a document with id: objectId(64e50cab7765243942cd04ce)

Você pode inserir vários documentos usando collection.insertMany().

Esse trecho insere três documentos descrevendo bebidas à base de café em uma coleção de documentos que descrevem as bebidas à base de café para venda em um grupo de lojas:

let drink: Document = [ "name": "Bean of the Day", "beanRegion": "Timbio, Colombia", "containsDairy": false, "storeNumber": 42]
let drink2: Document = [ "name": "Maple Latte", "beanRegion": "Yirgacheffe, Ethiopia", "containsDairy": true, "storeNumber": 42]
let drink3: Document = [ "name": "Bean of the Day", "beanRegion": "San Marcos, Guatemala", "containsDairy": false, "storeNumber": 47]
// Insert the documents into the collection
collection.insertMany([drink, drink2, drink3]) { result in
switch result {
case .failure(let error):
print("Call to MongoDB failed: \(error.localizedDescription)")
return
case .success(let objectIds):
print("Successfully inserted \(objectIds.count) new documents.")
}
}
Successfully inserted 3 new documents.

Esses trechos de código demonstram como ler dados armazenados em uma coleção MongoDB de um aplicativo móvel. As operações de leitura usam uma sintaxe de query padrão para especificar quais documentos devem ser retornados do banco de dados. As operações de leitura retornam um resultado que resulta em um único documento correspondente (no caso de findOneDocument()), um valor numérico long (no caso de count()) ou uma array de documentos correspondentes (no caso de find()).

Você pode encontrar um único documento usando collection.findOneDocument().

Esse trecho encontra um único documento de uma coleção de documentos que descrevem bebidas de café à venda em um grupo de lojas, onde o campo name do documento contém o valor da string "Colombian baixa":

let queryFilter: Document = ["name": "Colombian Blend"]
collection.findOneDocument(filter: queryFilter) { result in
switch result {
case .failure(let error):
print("Did not find matching documents: \(error.localizedDescription)")
return
case .success(let document):
print("Found a matching document: \(String(describing: document))")
}
}
Found a matching document: Optional([
"name": Optional(RealmSwift.AnyBSON.string("Colombian Blend")),
"_id": Optional(RealmSwift.AnyBSON.objectId(64e5014f65796c813bc68274)),
"beanRegion": Optional(RealmSwift.AnyBSON.string("Timbio, Colombia")),
"containsDairy": Optional(RealmSwift.AnyBSON.bool(false)),
"storeNumber": Optional(RealmSwift.AnyBSON.int64(43))
])

Você pode encontrar vários documentos usando collection.find().

Este trecho encontra todos os documentos em uma coleção de documentos que descrevem bebidas à base de café à venda em um grupo de lojas, onde o campo name do documento contém o valor " Americano ":

let queryFilter: Document = ["name": "Americano"]
collection.find(filter: queryFilter) { result in
switch result {
case .failure(let error):
print("Call to MongoDB failed: \(error.localizedDescription)")
return
case .success(let bsonDocumentArray):
print("Results: ")
for bsonDocument in bsonDocumentArray {
print("Coffee drink named: \(String(describing: bsonDocument["name"]))")
}
}
}
Results:
Coffee drink named: Optional(Optional(RealmSwift.AnyBSON.string("Americano")))
... more matching documents ...

Você pode classificar documentos em uma coleção inicializando uma instância de FindOptions com suas opções de classificação preferidas. FindOptions tem três parâmetros: limite, projeção e classificação. O argumento sorting pode ser uma array de pares de valores-chave onde cada chave representa um campo. Para cada chave, o valor 1 é classificado em ordem decrescente ou -1 é classificado em ordem crescente.

Em seguida, você passa a instância FindOptions para o método collection.find() ao executar a query.

Este trecho encontra todos os documentos em uma coleção de documentos que descrevem bebidas de café à venda em um grupo de lojas, onde o campo name do documento contém o valor " Americano ", classificado em ordem decrescente pelo campo beanRegion :

let queryFilter: Document = ["name": "Americano"]
let findOptions = FindOptions(0, nil, [["beanRegion": 1]])
collection.find(filter: queryFilter, options: findOptions) { result in
switch result {
case .failure(let error):
print("Call to MongoDB failed: \(error.localizedDescription)")
return
case .success(let documents):
print("Results: ")
for document in documents {
print("Coffee drink: \(document)")
}
}
}
Results:
Coffee drink: [
"_id": Optional(RealmSwift.AnyBSON.objectId(64e521ed6b124fd047534345)),
"beanRegion": Optional(RealmSwift.AnyBSON.string("San Marcos, Guatemala")),
"name": Optional(RealmSwift.AnyBSON.string("Americano")),
"containsDairy": Optional(RealmSwift.AnyBSON.bool(true)),
"storeNumber": Optional(RealmSwift.AnyBSON.int64(42))
]
Coffee drink: [
"_id": Optional(RealmSwift.AnyBSON.objectId(64e521ed6b124fd047534344)),
"storeNumber": Optional(RealmSwift.AnyBSON.int64(42)),
"containsDairy": Optional(RealmSwift.AnyBSON.bool(false)),
"name": Optional(RealmSwift.AnyBSON.string("Americano")),
"beanRegion": Optional(RealmSwift.AnyBSON.string("Timbio, Colombia"))
]
... more matching documents, sorted by `beanRegion` ...

Você pode contar documentos em uma coleção usando collection.count(). Você pode especificar uma query opcional e um limite para determinar quais documentos contar. Se você não especificar uma query, a ação contará todos os documentos da collection.

Esse trecho conta o número de documentos em uma coleção de documentos que descrevem bebidas à base de café à venda em um grupo de lojas, onde o campo name do documento contém o valor "Bean of the day":

let queryFilter: Document = ["name": "Bean of the Day"]
collection.count(filter: queryFilter) { result in
switch result {
case .failure(let error):
print("Call to MongoDB failed: \(error.localizedDescription)")
return
case .success(let count):
print("Found this many documents in the collection matching the filter: \(count)")
}
}
Found this many documents in the collection matching the filter: 24

Esses trechos de código demonstram como atualizar os dados armazenados em uma coleção MongoDB de um aplicativo móvel. As operações de atualização usam queries para especificar quais documentos atualizar e atualizar os operadores para descrever como mutar documentos que correspondem à query. As operações de atualização retornam um resultado que se resolve para um UpdateResult ou Error.

Você pode atualizar um único documento usando collection.updateOneDocument().

Este trecho atualiza um único documento em uma coleção de documentos que descrevem bebidas de café para venda em um grupo de lojas. Esta operação de atualização consulta um documento cujo campo name contém o valor "Bean of the day" e define o campo containsDairy como true:

let queryFilter: Document = ["name": "Bean of the Day", "storeNumber": 42]
let documentUpdate: Document = ["$set": ["containsDairy": true]]
collection.updateOneDocument(filter: queryFilter, update: documentUpdate) { result in
switch result {
case .failure(let error):
print("Failed to update document: \(error.localizedDescription)")
return
case .success(let updateResult):
if updateResult.matchedCount == 1 && updateResult.modifiedCount == 1 {
print("Successfully updated a matching document.")
} else {
print("Did not update a document")
}
}
Successfully updated a matching document.

Você pode atualizar vários documentos usando collection.updateManyDocuments().

Este trecho atualiza vários documentos em uma coleção de documentos que descrevem bebidas de café para venda em um grupo de lojas. Esta operação de atualização faz queries em documentos onde o campo name contém o valor "Bean of the day" e altera o campo containsDairy para true:

let queryFilter: Document = ["name": "Bean of the Day"]
let documentUpdate: Document = ["$set": ["containsDairy": true]]
collection.updateManyDocuments(filter: queryFilter, update: documentUpdate) { result in
switch result {
case .failure(let error):
print("Failed to update document: \(error.localizedDescription)")
return
case .success(let updateResult):
print("Successfully updated \(updateResult.modifiedCount) documents.")
}
}
Successfully updated 24 documents.

Se uma operação de atualização não corresponder a nenhum documento na coleção, você poderá inserir automaticamente um único novo documento na coleção que corresponda à query de atualização definindo a opção upsert como true.

O trecho a seguir atualiza um documento em uma coleção de documentos que descrevem bebidas de café para venda em um grupo de lojas. Se nenhum documento corresponder à query, ele inserirá um novo documento se nenhum documento. Esta operação faz uam query em documentos onde o campo name tem um valor de "Bean of the day" e o campo storeNumber tem um valor de 55.

Como esse trecho define a opção upsert como true, se nenhum documento corresponder à query, o MongoDB cria um novo documento que inclui a query e as atualizações especificadas:

let queryFilter: Document = ["name": "Bean of the Day", "storeNumber": 55]
let documentUpdate: Document = ["name": "Bean of the Day", "beanRegion": "Yirgacheffe, Ethiopia", "containsDairy": false, "storeNumber": 55]
collection.updateOneDocument(filter: queryFilter, update: documentUpdate, upsert: true) { result in
switch result {
case .failure(let error):
print("Failed to update document: \(error.localizedDescription)")
return
case .success(let updateResult):
if let unwrappedDocumentId = updateResult.documentId {
print("Successfully upserted a document with id: \(unwrappedDocumentId)")
} else {
print("Did not upsert a document")
}
}
}
Successfully upserted a document with id: 64e523e37765243942eba44a

Esses trechos de código demonstram como excluir documentos armazenados em uma coleção MongoDB de um aplicativo móvel. As operações de exclusão usam uma query para especificar quais documentos excluir e retornar resultados que se resolvem em uma contagem de documentos excluídos ou Int Error.

Você pode excluir um único documento de uma coleção usando collection.deleteOneDocument().

Este trecho exclui um documento em uma coleção de documentos que descrevem bebidas à base de café para venda em um grupo de lojas. Esta operação consulta um documento onde o campo name tem um valor de "Mocha" e o campo storeNumber tem um valor de 17 e o exclui.

let queryFilter: Document = ["name": "Mocha", "storeNumber": 17]
collection.deleteOneDocument(filter: queryFilter) { deletedResult in
switch deletedResult {
case .failure(let error):
print("Failed to delete a document: \(error.localizedDescription)")
return
case .success(let deletedResult):
print("Successfully deleted a document.")
}
}
Successfully deleted a document.

Você pode excluir vários itens de uma coleção usando collection.deleteManyDocuments().

Este snippet exclui todos os documentos em uma coleção de documentos que descrevem bebidas à base de café à venda em um grupo de lojas que correspondem à query de documentos cujo campo name contém o valor "Caramel Latter":

let filter: Document = ["name": "Caramel Latte"]
collection.deleteManyDocuments(filter: filter) { deletedResult in
switch deletedResult {
case .failure(let error):
print("Failed to delete a document: \(error.localizedDescription)")
return
case .success(let deletedResult):
print("Successfully deleted \(deletedResult) documents.")
}
}
Successfully deleted 3 documents.

Você pode assistir a uma coleção para notificações de alteração que o MongoDB emite sempre que um documento da coleção for criado, modificado ou excluído. Cada notificação especifica um documento que foi alterado, como ele foi alterado e o documento completo após a operação que causou o evento.

Importante

Limitações sem servidor

Você não poderá observar alterações se a fonte de dados for uma instância sem servidor do Atlas. Atualmente, o MongoDB serverless não oferece suporte a fluxos de alterações, que são usados em coleções monitoradas para escutar alterações.

Você pode abrir um fluxo de alterações feitas em uma coleção chamando collection.watch(). Esta função cria um editor que emite um evento de alteração AnyBSON quando a coleção MongoDB é alterada.

Opcionalmente, você pode assistir a uma lista filtrada de _ids na coleção com collection.watch(filterIds:) ou aplicar um filtro de $match aos eventos de alteração recebidos com coleção.watch(matchFilter:).

O método .watch() pode usar um ChangeEventDelegate para assinar alterações em um fluxo. Por exemplo, com este ChangeEventDelegate:

class MyChangeStreamDelegate: ChangeEventDelegate {
func changeStreamDidOpen(_ changeStream: RealmSwift.ChangeStream) {
print("Change stream opened: \(changeStream)")
}
func changeStreamDidClose(with error: Error?) {
if let anError = error {
print("Change stream closed with error: \(anError.localizedDescription)")
} else {
print("Change stream closed")
}
}
func changeStreamDidReceive(error: Error) {
print("Received error: \(error.localizedDescription)")
}
func changeStreamDidReceive(changeEvent: RealmSwift.AnyBSON?) {
guard let changeEvent = changeEvent else { return }
guard let document = changeEvent.documentValue else { return }
print("Change event document received: \(document)")
}
}

Este código observa alterações em documentos na collection CoffeeDrinks :

appClient.login(credentials: Credentials.anonymous) { (result) in
DispatchQueue.main.async {
switch result {
case .failure(let error):
print("Login failed: \(error)")
case .success(let user):
print("Login as \(user) succeeded!")
// Continue below
}
// Set up the client, database, and collection.
let client = self.appClient.currentUser!.mongoClient("mongodb-atlas")
let database = client.database(named: "ios")
let collection = database.collection(withName: "CoffeeDrinks")
// Watch the collection. In this example, we use a queue and delegate,
// both of which are optional arguments.
let queue = DispatchQueue(label: "io.realm.watchQueue")
let delegate = MyChangeStreamDelegate()
let changeStream = collection.watch(delegate: delegate, queue: queue)
// Adding a document triggers a change event.
let drink: Document = [ "name": "Bean of the Day", "beanRegion": "Timbio, Colombia", "containsDairy": false, "storeNumber": 42]
collection.insertOne(drink) { result in
switch result {
case .failure(let error):
print("Call to MongoDB failed: \(error.localizedDescription)")
return
case .success(let objectId):
print("Successfully inserted a document with id: \(objectId)")
}
}
// After you're done watching for events, close the change stream.
changeStream.close()
}
}
Login as <RLMUser: 0x600002dfd1a0> succeeded!
Change stream opened: <RLMChangeStream: 0x60000182ab80>
Successfully inserted a document with id: objectId(64e525665fef1743dedb5aa6)
Change event document received: [
"clusterTime": Optional(RealmSwift.AnyBSON.datetime(2023-08-22 21:15:18 +0000)),
"_id": Optional(RealmSwift.AnyBSON.document([
"_data": Optional(RealmSwift.AnyBSON.string(
"8264E525660000000B2B022C0100296E5A100464816C3449884434A07AC19F4AAFCB8046645F6964006464E525665FEF1743DEDB5AA60004"
))
])),
"documentKey": Optional(RealmSwift.AnyBSON.document([
"_id": Optional(RealmSwift.AnyBSON.objectId(64e525665fef1743dedb5aa6))
])),
"ns": Optional(RealmSwift.AnyBSON.document([
"coll": Optional(RealmSwift.AnyBSON.string("CoffeeDrinks")),
"db": Optional(RealmSwift.AnyBSON.string("ios"))
])),
"operationType": Optional(RealmSwift.AnyBSON.string("insert")),
"fullDocument": Optional(RealmSwift.AnyBSON.document([
"name": Optional(RealmSwift.AnyBSON.string("Bean of the Day")),
"storeNumber": Optional(RealmSwift.AnyBSON.int64(42)),
"beanRegion": Optional(RealmSwift.AnyBSON.string("Timbio, Colombia")),
"_id": Optional(RealmSwift.AnyBSON.objectId(64e525665fef1743dedb5aa6)),
"containsDairy": Optional(RealmSwift.AnyBSON.bool(false))
]))]
Change stream closed

Você pode observar alterações em uma coleção em uma lista específica de objetos passando sua _id. Chame collection.watch(filterIds: ) com uma array de ObjectIds para receber somente eventos de alteração que se apliquem a esses documentos.

Considere um exemplo que usa este ChangeEventDelegate:

class MyChangeStreamDelegate: ChangeEventDelegate {
func changeStreamDidOpen(_ changeStream: RealmSwift.ChangeStream) {
print("Change stream opened: \(changeStream)")
}
func changeStreamDidClose(with error: Error?) {
if let anError = error {
print("Change stream closed with error: \(anError.localizedDescription)")
} else {
print("Change stream closed")
}
}
func changeStreamDidReceive(error: Error) {
print("Received error: \(error.localizedDescription)")
}
func changeStreamDidReceive(changeEvent: RealmSwift.AnyBSON?) {
guard let changeEvent = changeEvent else { return }
guard let document = changeEvent.documentValue else { return }
print("Change event document received: \(document)")
}
}

O código abaixo usa esse delegado para observar alterações em documento específicos na collection CoffeeDrinks :

appClient.login(credentials: Credentials.anonymous) { (result) in
DispatchQueue.main.async {
switch result {
case .failure(let error):
print("Login failed: \(error)")
case .success(let user):
print("Login as \(user) succeeded!")
// Continue below
}
// Set up the client, database, and collection.
let client = self.appClient.currentUser!.mongoClient("mongodb-atlas")
let database = client.database(named: "ios")
let collection = database.collection(withName: "CoffeeDrinks")
// Watch the collection. In this example, we use a queue and delegate,
// both of which are optional arguments.
// `filterIds` is an array of specific document ObjectIds you want to watch.
let queue = DispatchQueue(label: "io.realm.watchQueue")
let delegate = MyChangeStreamDelegate()
let changeStream = collection.watch(filterIds: [drinkObjectId], delegate: delegate, queue: queue)
// An update to a relevant document triggers a change event.
let queryFilter: Document = ["_id": AnyBSON(drinkObjectId) ]
let documentUpdate: Document = ["$set": ["containsDairy": true]]
collection.updateOneDocument(filter: queryFilter, update: documentUpdate) { result in
switch result {
case .failure(let error):
print("Call to MongoDB failed: \(error.localizedDescription)")
return
case .success(let updateResult):
print("Successfully updated the document")
}
}
// After you're done watching for events, close the change stream.
changeStream.close()
}
}
Login as <RLMUser: 0x60000010eb00> succeeded!
Successfully inserted a document with id: objectId(64e525ce7765243942ef0a58)
Change stream opened: <RLMChangeStream: 0x6000034946c0>
Change event document received: [
"fullDocument": Optional(RealmSwift.AnyBSON.document([
"containsDairy": Optional(RealmSwift.AnyBSON.bool(true)),
"beanRegion": Optional(RealmSwift.AnyBSON.string("Timbio, Colombia")),
"_id": Optional(RealmSwift.AnyBSON.objectId(64e525ce7765243942ef0a58)),
"name": Optional(RealmSwift.AnyBSON.string("Bean of the Day")),
"storeNumber": Optional(RealmSwift.AnyBSON.int64(42))
])),
"clusterTime": Optional(RealmSwift.AnyBSON.datetime(2023-08-22 21:17:09 +0000)),
"operationType": Optional(RealmSwift.AnyBSON.string("update")),
"documentKey": Optional(RealmSwift.AnyBSON.document([
"_id": Optional(RealmSwift.AnyBSON.objectId(64e525ce7765243942ef0a58))
])),
"ns": Optional(RealmSwift.AnyBSON.document([
"db": Optional(RealmSwift.AnyBSON.string("ios")),
"coll": Optional(RealmSwift.AnyBSON.string("CoffeeDrinks"))
])),
"updateDescription": Optional(RealmSwift.AnyBSON.document([
"removedFields": Optional(RealmSwift.AnyBSON.array([])),
"updatedFields": Optional(RealmSwift.AnyBSON.document([
"containsDairy": Optional(RealmSwift.AnyBSON.bool(true))]))
])),
"_id": Optional(RealmSwift.AnyBSON.document([
"_data": Optional(RealmSwift.AnyBSON.string(
"8264E525D5000000082B022C0100296E5A100464816C3449884434A07AC19F4AAFCB8046645F6964006464E525CE7765243942EF0A580004"
))
]))]
Change stream closed

Você pode abrir um fluxo de alterações feitas em documentos em uma coleção que atendam a determinados critérios chamando collection.watch(matchFilter: ). Este método aceita um parâmetro Document que é utilizado como query de um operador $match para processar cada evento do banco de dados que ocorre enquanto assiste à collection.

Considere um exemplo que usa este ChangeEventDelegate:

class MyChangeStreamDelegate: ChangeEventDelegate {
func changeStreamDidOpen(_ changeStream: RealmSwift.ChangeStream) {
print("Change stream opened: \(changeStream)")
}
func changeStreamDidClose(with error: Error?) {
if let anError = error {
print("Change stream closed with error: \(anError.localizedDescription)")
} else {
print("Change stream closed")
}
}
func changeStreamDidReceive(error: Error) {
print("Received error: \(error.localizedDescription)")
}
func changeStreamDidReceive(changeEvent: RealmSwift.AnyBSON?) {
guard let changeEvent = changeEvent else { return }
guard let document = changeEvent.documentValue else { return }
print("Change event document received: \(document)")
}
}

O código abaixo usa esse delegado para observar alterações em documento na collection CoffeeDrink . Ele só Atlas Triggers a chamada de resposta fornecida para evento cujo documento tenham um valor storeNumber de 42:

appClient.login(credentials: Credentials.anonymous) { (result) in
DispatchQueue.main.async {
switch result {
case .failure(let error):
print("Login failed: \(error)")
case .success(let user):
print("Login as \(user) succeeded!")
// Continue below
}
// Set up the client, database, and collection.
let client = self.appClient.currentUser!.mongoClient("mongodb-atlas")
let database = client.database(named: "ios")
let collection = database.collection(withName: "CoffeeDrinks")
// Watch the collection. In this example, we use a queue and delegate,
// both of which are optional arguments.
let queue = DispatchQueue(label: "io.realm.watchQueue")
let delegate = MyChangeStreamDelegate()
let matchFilter = [ "fullDocument.storeNumber": AnyBSON(42) ]
let changeStream = collection.watch(matchFilter: matchFilter, delegate: delegate, queue: queue)
// An update to a relevant document triggers a change event.
let queryFilter: Document = ["_id": AnyBSON(drinkObjectId) ]
let documentUpdate: Document = ["$set": ["containsDairy": true]]
collection.updateOneDocument(filter: queryFilter, update: documentUpdate) { result in
switch result {
case .failure(let error):
print("Call to MongoDB failed: \(error.localizedDescription)")
return
case .success(let updateResult):
print("Successfully updated the document")
}
}
// After you're done watching for events, close the change stream.
changeStream.close()
}
}
Login as <RLMUser: 0x6000026ee640> succeeded!
Successfully inserted a document with id: objectId(64e5266731323150716faf13)
Change stream opened: <RLMChangeStream: 0x6000013283c0>
Change event document received: [
"operationType": Optional(RealmSwift.AnyBSON.string("update")),
"updateDescription": Optional(RealmSwift.AnyBSON.document([
"removedFields": Optional(RealmSwift.AnyBSON.array([])),
"updatedFields": Optional(RealmSwift.AnyBSON.document([
"containsDairy": Optional(RealmSwift.AnyBSON.bool(true))
]))
])),
"clusterTime": Optional(RealmSwift.AnyBSON.datetime(2023-08-22 21:19:44 +0000)),
"_id": Optional(RealmSwift.AnyBSON.document([
"_data": Optional(RealmSwift.AnyBSON.string(
"8264E526700000000E2B022C0100296E5A100464816C3449884434A07AC19F4AAFCB8046645F6964006464E5266731323150716FAF130004"
))
])),
"ns": Optional(RealmSwift.AnyBSON.document([
"db": Optional(RealmSwift.AnyBSON.string("ios")),
"coll": Optional(RealmSwift.AnyBSON.string("CoffeeDrinks"))
])),
"fullDocument": Optional(RealmSwift.AnyBSON.document([
"_id": Optional(RealmSwift.AnyBSON.objectId(64e5266731323150716faf13)),
"name": Optional(RealmSwift.AnyBSON.string("Bean of the Day")),
"containsDairy": Optional(RealmSwift.AnyBSON.bool(true)),
"beanRegion": Optional(RealmSwift.AnyBSON.string("Timbio, Colombia")),
"storeNumber": Optional(RealmSwift.AnyBSON.int64(42))
])),
"documentKey": Optional(RealmSwift.AnyBSON.document([
"_id": Optional(RealmSwift.AnyBSON.objectId(64e5266731323150716faf13))
]))]
Successfully updated the document
Change stream closed

Novidade na versão 10.37.0 .

Você pode abrir uma sequência assíncrona para observar as alterações em uma coleção. Em um contexto assíncrono, chame changeEvents() em uma coleção para abrir um fluxo de alteração. Isso fornece uma sequência assíncrona de valores AnyBSON contendo informações sobre cada alteração na coleção MongoDB.

Opcionalmente, você pode fornecer uma chamada de resposta changeEvents(onOpen: ) que é invocada quando o fluxo de observação é inicializado no servidor.

A API changeEvents() pode levar filterIds ou um matchFilter para monitorar um subconjunto de documentos em uma coleção, semelhante aos exemplos acima.

O seguinte trecho observa alterações em quaisquer documentos da collection CoffeeDrinks como uma sequência assíncrona:

let user = try await appClient.login(credentials: Credentials.anonymous)
// Set up the client, database, and collection.
let mongoClient = user.mongoClient("mongodb-atlas")
let database = mongoClient.database(named: "ios")
let collection = database.collection(withName: "CoffeeDrinks")
// Set up a task you'll later await to keep the change stream open,
// and you can cancel it when you're done watching for events.
let task = Task {
// Open the change stream.
let changeEvents = collection.changeEvents(onOpen: {
print("Successfully opened change stream")
})
// Await events in the change stream.
for try await event in changeEvents {
let doc = event.documentValue!
print("Received event: \(event.documentValue!)")
}
}
// Updating a document in the collection triggers a change event.
let queryFilter: Document = ["_id": AnyBSON(objectId) ]
let documentUpdate: Document = ["$set": ["containsDairy": true]]
let updateResult = try await collection.updateOneDocument(filter: queryFilter, update: documentUpdate)
// Cancel the task when you're done watching the stream.
task.cancel()
_ = await task.result
Successfully opened change stream
Received event: [
"operationType": Optional(RealmSwift.AnyBSON.string("update")),
"documentKey": Optional(RealmSwift.AnyBSON.document([
"_id": Optional(RealmSwift.AnyBSON.objectId(64e526d9850b15debe83ff46))
])),
"ns": Optional(RealmSwift.AnyBSON.document([
"coll": Optional(RealmSwift.AnyBSON.string("CoffeeDrinks")),
"db": Optional(RealmSwift.AnyBSON.string("ios"))
])),
"clusterTime": Optional(RealmSwift.AnyBSON.datetime(2023-08-22 21:21:30 +0000)),
"fullDocument": Optional(RealmSwift.AnyBSON.document([
"name": Optional(RealmSwift.AnyBSON.string("Bean of the Day")),
"containsDairy": Optional(RealmSwift.AnyBSON.bool(true)),
"storeNumber": Optional(RealmSwift.AnyBSON.int64(43)),
"_id": Optional(RealmSwift.AnyBSON.objectId(64e526d9850b15debe83ff46)),
"beanRegion": Optional(RealmSwift.AnyBSON.string("Timbio, Colombia"))
])),
"_id": Optional(RealmSwift.AnyBSON.document([
"_data": Optional(RealmSwift.AnyBSON.string(
"8264E526DA000000092B022C0100296E5A100464816C3449884434A07AC19F4AAFCB8046645F6964006464E526D9850B15DEBE83FF460004"
)
)
])),
"updateDescription": Optional(RealmSwift.AnyBSON.document([
"updatedFields": Optional(RealmSwift.AnyBSON.document([
"containsDairy": Optional(RealmSwift.AnyBSON.bool(true))
])),
"removedFields": Optional(RealmSwift.AnyBSON.array([]))
]))]

As operações de agregação executam todos os documentos em uma coleção por meio de uma série de estágios de agregação de dados chamados de pipeline de agregação. A agregação permite filtrar e transformar documentos, coletar dados resumidos sobre grupos de documentos relacionados e outras operações de dados complexas.

Você pode configurar e executar operações de agregação em uma coleção usando collection.aggregate().

Uma operação de aggregation aceita uma lista de estágios de aggregation como entrada e retorna um resultado que se resolve em uma collection de documentos processados pelo pipeline, ou um Error .

Você pode utilizar o estágio $match para filtrar documentos utilizando a sintaxe de query padrão do MongoDB:

Esse estágio $match filtra os documentos para incluir apenas aqueles em que o campo storeNumber tem um valor igual a 42:

let pipeline: [Document] = [["$match": ["storeNumber": ["$eq": 42]]]]
collection.aggregate(pipeline: pipeline) { result in
switch result {
case .failure(let error):
print("Failed to aggregate: \(error.localizedDescription)")
return
case .success(let documents):
print("Successfully ran the aggregation:")
for document in documents {
print("Coffee drink: \(document)")
}
}
}
Successfully ran the aggregation:
Coffee drink: [
"_id": Optional(RealmSwift.AnyBSON.objectId(64e53171313231507183e7c2)),
"name": Optional(RealmSwift.AnyBSON.string("Bean of the Day")),
"containsDairy": Optional(RealmSwift.AnyBSON.bool(false)),
"beanRegion": Optional(RealmSwift.AnyBSON.string("Timbio, Colombia")),
"storeNumber": Optional(RealmSwift.AnyBSON.int64(42))]
Coffee drink: [
"containsDairy": Optional(RealmSwift.AnyBSON.bool(true)),
"name": Optional(RealmSwift.AnyBSON.string("Maple Latte")),
"beanRegion": Optional(RealmSwift.AnyBSON.string("Yirgacheffe, Ethiopia")),
"_id": Optional(RealmSwift.AnyBSON.objectId(64e53171313231507183e7c3)),
"storeNumber": Optional(RealmSwift.AnyBSON.int64(42))]
(...more results...)

Você pode usar o estágio $group para agregar dados resumidos de um ou mais documentos. O MongoDB agrupa documentos com base na expressão definida no campo _id da etapa $group . Você pode referenciar um campo de documento específico prefixando o nome do campo com um $.

Este estágio $group organiza documentos pelo valor de seu campo storeNumber . Em seguida, ele calcula o número de documentos de bebida de café que contêm esse número de armazenamento no valor do campo. Em outras palavras, estamos calculando o número de bebidas de café para cada número de loja.

let pipeline: [Document] = [["$group": ["_id": "$storeNumber", "numItems": ["$sum": 1]]]]
collection.aggregate(pipeline: pipeline) { result in
switch result {
case .failure(let error):
print("Failed to aggregate: \(error.localizedDescription)")
return
case .success(let results):
print("Successfully ran the aggregation.")
for result in results {
print(result)
}
}
}
Successfully ran the aggregation.
["numItems": Optional(RealmSwift.AnyBSON.int64(27)), "_id": Optional(RealmSwift.AnyBSON.int64(42))]
["numItems": Optional(RealmSwift.AnyBSON.int64(44)), "_id": Optional(RealmSwift.AnyBSON.int64(47))]
(...more results...)

Você pode utilizar o estágio $project para incluir ou omitir campos específicos de documentos ou calcular novos campos utilizando operadores de agregação. As projeções funcionam de duas formas:

  • Especifique que você deseja incluir campos usando um 1. Isso tem o efeito colateral de excluir implicitamente todos os campos não especificados.

  • Especifique que você deseja excluir campos usando um 0. Isso tem o efeito colateral de incluir implicitamente todos os campos não especificados.

Estes dois métodos de projeção são mutuamente exclusivos. Se você especificar campos para incluir, não poderá também especificar campos para excluir e vice-versa.

Observação

O campo _id é um caso especial: ele é sempre incluído em todas as consultas, a menos que seja explicitamente especificado de outra forma. Por esse motivo, você pode excluir o campo _id com um valor 0 e, ao mesmo tempo, incluir outros campos, como storeNumber, com um 1. Somente o caso especial de exclusão do campo _id permite tanto a exclusão quanto a inclusão em um estágio $project .

Para este exemplo, suponha que o documento CoffeeDrink tenha um campo store que é um valor de string contendo a palavra "Loja" com um número, como "Loja 42", semelhante a estes documentos:

let drink: Document = [ "name": "Bean of the Day", "beanRegion": "Timbio, Colombia", "containsDairy": false, "store": "Store 42"]
let drink2: Document = [ "name": "Bean of the Day", "beanRegion": "Yirgacheffe, Ethiopia", "containsDairy": true, "store": "Store 47"]

O estágio $project a seguir omite o campo _id, inclui o campo name e cria um novo campo chamado storeNumber. O storeNumber é gerado usando dois operadores de agregação:

  1. $split separa a representação de string store em dois segmentos de string ao redor do caractere de espaço. Por exemplo, o valor "Store 42" dividido dessa forma retorna uma matriz com dois elementos: "Store" e "42".

  2. $arrayElemAt seleciona um elemento específico de uma matriz com base no segundo argumento. Nesse caso, o valor 1 seleciona o segundo elemento da matriz gerada pelo operador $split, já que as matrizes são indexadas a partir de 0. Por exemplo, o valor ["Loja", "42"] passado para esta operação retornaria um valor de "42".

let pipeline: [Document] = [["$project": ["_id": 0, "name": 1, "storeNumber": ["$arrayElemAt": [["$split": ["$store", " "]], 1]]]]]
collection.aggregate(pipeline: pipeline) { result in
switch result {
case .failure(let error):
print("Failed to aggregate: \(error.localizedDescription)")
return
case .success(let results):
print("Successfully ran the aggregation.")
for result in results {
print(result)
}
}
}
Successfully ran the aggregation.
["name": Optional(RealmSwift.AnyBSON.string("Bean of the Day")), "storeNumber": Optional(RealmSwift.AnyBSON.string("42"))]
["storeNumber": Optional(RealmSwift.AnyBSON.string("47")), "name": Optional(RealmSwift.AnyBSON.string("Bean of the Day"))]
(...more results...)

Você pode usar o estágio $addFields para adicionar novos campos com valores calculados usando operadores de agregação.

Observação

$addFields é semelhante ao $project, mas não permite que você inclua ou omita campos.

Para este exemplo, suponha que o documento CoffeeDrink tenha um campo store que é um valor de string contendo a palavra "Loja" com um número, como "Loja 42", semelhante a estes documentos:

let drink: Document = [ "name": "Bean of the Day", "beanRegion": "Timbio, Colombia", "containsDairy": false, "store": "Store 42"]
let drink2: Document = [ "name": "Bean of the Day", "beanRegion": "Yirgacheffe, Ethiopia", "containsDairy": true, "store": "Store 47"]

O estágio $addFields a seguir cria um novo campo chamado storeNumber em que o valor é a saída de dois operadores agregados que transformam o valor do campo store.

let pipeline: [Document] = [["$addFields": ["storeNumber": ["$arrayElemAt": [["$split": ["$store", " "]], 1]]]]]
collection.aggregate(pipeline: pipeline) { result in
switch result {
case .failure(let error):
print("Failed to aggregate: \(error.localizedDescription)")
return
case .success(let results):
print("Successfully ran the aggregation.")
for result in results {
print(result)
}
}
}
Successfully ran the aggregation.
[
"storeNumber": Optional(RealmSwift.AnyBSON.string("42")),
"name": Optional(RealmSwift.AnyBSON.string("Bean of the Day")),
"_id": Optional(RealmSwift.AnyBSON.objectId(64e588ff5fef1743de3559aa)),
"store": Optional(RealmSwift.AnyBSON.string("Store 42")),
"containsDairy": Optional(RealmSwift.AnyBSON.bool(false)),
"beanRegion": Optional(RealmSwift.AnyBSON.string("Timbio, Colombia"))]
[
"containsDairy": Optional(RealmSwift.AnyBSON.bool(true)),
"storeNumber": Optional(RealmSwift.AnyBSON.string("47")),
"store": Optional(RealmSwift.AnyBSON.string("Store 47")),
"beanRegion": Optional(RealmSwift.AnyBSON.string("Yirgacheffe, Ethiopia")),
"name": Optional(RealmSwift.AnyBSON.string("Bean of the Day")),
"_id": Optional(RealmSwift.AnyBSON.objectId(64e588ff5fef1743de3559ab))]

Você pode usar o estágio $unwind para transformar um único documento contendo uma matriz em vários documentos contendo valores individuais dessa matriz. Quando você desenrola um campo de matriz, o MongoDB copia cada documento uma vez para cada elemento do campo de matriz, mas substitui o valor da matriz pelo elemento da matriz em cada cópia.

Considere este documento que inclui um array featuredInPromotions :

let drink: Document = [
"name": "Maple Latte",
"beanRegion": "Yirgacheffe, Ethiopia",
"containsDairy": true,
"storeNumber": 42,
"featuredInPromotions": [
"Spring into Spring",
"Tastes of Fall",
"Winter Delights"
]
]

A etapa $unwind a seguir cria um novo documento para cada elemento da array items em cada documento. Ele também adiciona um campo chamado itemIndex a cada novo documento que especifica o índice de posição do elemento na array original:

let pipeline: [Document] = [["$unwind": ["path": "$featuredInPromotions", "includeArrayIndex": "itemIndex"]]]
collection.aggregate(pipeline: pipeline) { result in
switch result {
case .failure(let error):
print("Failed to aggregate: \(error.localizedDescription)")
return
case .success(let results):
print("Successfully ran the aggregation.")
for result in results {
print("Coffee drink: \(result)")
}
}
}
Successfully ran the aggregation.
Coffee drink: [
"_id": Optional(RealmSwift.AnyBSON.objectId(64e58bb4fc901d40e03fde64)),
"storeNumber": Optional(RealmSwift.AnyBSON.int64(42)),
"beanRegion": Optional(RealmSwift.AnyBSON.string("Yirgacheffe, Ethiopia")),
"featuredInPromotions": Optional(RealmSwift.AnyBSON.string("Spring into Spring")),
"itemIndex": Optional(RealmSwift.AnyBSON.int64(0)),
"name": Optional(RealmSwift.AnyBSON.string("Maple Latte")),
"containsDairy": Optional(RealmSwift.AnyBSON.bool(true))]
Coffee drink: [
"itemIndex": Optional(RealmSwift.AnyBSON.int64(1)),
"name": Optional(RealmSwift.AnyBSON.string("Maple Latte")),
"_id": Optional(RealmSwift.AnyBSON.objectId(64e58bb4fc901d40e03fde64)),
"featuredInPromotions": Optional(RealmSwift.AnyBSON.string("Tastes of Fall")),
"storeNumber": Optional(RealmSwift.AnyBSON.int64(42)),
"containsDairy": Optional(RealmSwift.AnyBSON.bool(true)),
"beanRegion": Optional(RealmSwift.AnyBSON.string("Yirgacheffe, Ethiopia"))]
Coffee drink: [
"name": Optional(RealmSwift.AnyBSON.string("Maple Latte")),
"_id": Optional(RealmSwift.AnyBSON.objectId(64e58bb4fc901d40e03fde64)),
"beanRegion": Optional(RealmSwift.AnyBSON.string("Yirgacheffe, Ethiopia")),
"itemIndex": Optional(RealmSwift.AnyBSON.int64(2)),
"containsDairy": Optional(RealmSwift.AnyBSON.bool(true)),
"storeNumber": Optional(RealmSwift.AnyBSON.int64(42)),
"featuredInPromotions": Optional(RealmSwift.AnyBSON.string("Winter Delights"))]

Você pode então $group pelo valor de featuredInPromotions e $sum o número de bebidas de café em cada promoção, como no exemplo de documentos do grupo, ou realizar outros cálculos ou transformações com base em seus dados.

Voltar

Chamar uma função