MongoDB 쿼리 - Swift SDK
이 페이지의 내용
- 개요
- 사용 사례
- 전제 조건
- 데이터 예시
- 비동기/대기 쿼리 MongoDB
- 문서 만들기
- 단일 문서 삽입
- 여러 문서를 삽입합니다.
- 문서 읽기
- 단일 문서 찾기
- 여러 문서 찾기
- 문서 찾기 및 정렬
- collection의 문서 수 계산
- 문서 업데이트
- 단일 문서 업데이트
- 여러 문서 업데이트하기
- 문서 업서트
- 문서 삭제
- 단일 문서 삭제
- 여러 문서 삭제
- 변화를 주시하세요
- 컬렉션의 모든 변경 사항 보기
- collection에서 특정 ID에 대한 변경 사항 확인
- 필터로 컬렉션의 변경 사항 보기
- collection을 비동기 시퀀스로 보기
- 문서 애그리게이션
- 문서 필터링
- 문서 그룹화
- 문서 필드 프로젝트
- 문서에 필드 추가
- 배열 값 풀기
개요
쿼리 API 와 함께 Realm Swift SDK의 MongoClient 를 사용하여 클라이언트 애플리케이션 코드에서 직접 MongoDB에 저장된 데이터를 쿼리할 수 있습니다. Atlas App Services는 로그인한 사용자 또는 각 문서의 콘텐츠를 기반으로 결과를 안전하게 검색할 수 있도록 컬렉션에 대한 데이터 액세스 규칙 을 제공합니다.
사용 사례
MongoDB 데이터 소스를 쿼리해야 하는 이유는 여러 가지가 있습니다. Atlas Device Sync를 통해 클라이언트의 데이터로 작업하는 것이 항상 실용적이거나 가능한 것은 아닙니다. 다음과 같은 경우에는 MongoDB를 쿼리해야 할 수 있습니다.
데이터 세트의 용량이 크거나, 클라이언트 디바이스로 데이터 세트 전체를 로드하는 데 제약이 있을 경우
사용자 지정 사용자 데이터 생성 또는 업데이트를 실행하려는 경우
Realm에서 모델링되지 않은 문서를 검색하려는 경우
앱으로 엄격한 스키마가 없는 컬렉션에 액세스해야 하는 경우
비Realm 서비스를 통해 액세스하고자 하는 컬렉션을 생성할 경우
다음 내용은 MongoDB를 직접 쿼리할 수 있는 일반적인 사용 사례입니다. 단, 해당 사례들이 전부는 아닙니다.
전제 조건
MongoDB 클라이언트 애플리케이션에서 MongoDB 를 쿼리하려면 먼저 에서 데이터 액세스를 설정해야 App Services App 합니다. Realm SDK가 Atlas 를 쿼리할 수 있도록 백엔드 앱을 설정하는 방법을 알아보려면 Atlas 앱 서비스 문서에서 MongoDB 데이터 액세스 설정 을 참조하세요.
데이터 예시
이 예제는 커피숍 체인에서 커피 음료를 설명하는 MongoDB collection에서 작동합니다. 문서는 다음과 같은 속성을 가진 객체를 나타냅니다.
class CoffeeDrink: Object { true) var _id: ObjectId (primaryKey: var name: String var beanRegion: String? var containsDairy: Bool var storeNumber: Int }
각 예제의 전체 코드에는 각 작업을 완료하기 전에 로그인하고 MongoDB collection 핸들을 인스턴스화하는 작업이 포함됩니다. 간결성을 위해 이 예제에서는 로그인 및 collection 핸들 코드를 생략합니다. 그러나 각 전체 예시는 다음과 같습니다.
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)") } } } } }
비동기/대기 쿼리 MongoDB
버전 10.16.0의 새로운 기능.
Realm Swift SDK는 MongoCollection 메서드의 비동기/대기 버전을 제공합니다.
이 페이지의 모든 메서드는 비동기/대기 구문과 호환됩니다. 이 예에서는 collection.insertOne()
메서드에 대한 구문을 보여 줍니다. 단일 문서 삽입에서 완료 핸들러 버전을 확인할 수 있습니다.
// 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)") }
Realm Swift SDK 버전 10.15.0 및 10.16.0부터 많은 Realm API가 Swift async/await 구문을 지원합니다. 프로젝트는 다음 요구 사항을 충족해야 합니다.
Swift SDK 버전 | Swift 버전 요구 사항 | 지원되는 OS |
---|---|---|
10.25.0 | Swift 5.6 | iOS 13.x |
10.15.0 또는 10.16.0 | Swift 5.5 | iOS 15.x |
앱이 async/await
컨텍스트에서 Realm에 액세스하는 경우 코드를 @MainActor
(으)로 표시하여 스레드 관련 충돌을 방지합니다.
문서 만들기
이 코드 스니펫은 모바일 애플리케이션에서 하나 이상의 문서를 MongoDB 컬렉션에 삽입하는 방법을 보여줍니다. 이러한 메서드는 하나 이상의 문서 를 가져와서 결과를 반환합니다. 성공은 삽입된 문서의 ObjectId 를 반환하거나, 여러 문서를 삽입할 때 순서대로 objectId의 배열을 반환합니다.
단일 문서 삽입
collection.insertOne()을 사용하여 단일 문서를 삽입할 수 있습니다.
이 스니펫은 매장 그룹에서 판매하는 커피 음료를 설명하는 문서 컬렉션에 '콜롬비아 블렌드' 커피 음료를 설명하는 단일 문서를 삽입합니다.
// 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)") } }
여러 문서를 삽입합니다.
collection.insertMany()를 사용하여 여러 문서를 삽입할 수 있습니다.
이 스니펫은 매장 그룹에서 판매할 커피 음료를 설명하는 문서 컬렉션에 커피 음료를 설명하는 문서 3개를 삽입합니다.
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.") } }
문서 읽기
이 코드 스니펫은 MongoDB 컬렉션에 저장된 데이터를 모바일 애플리케이션에서 읽는 방법을 보여줍니다. 읽기 작업은 표준 쿼리 구문 을 사용하여 데이터베이스에서 반환할 문서를 지정합니다. 읽기 작업은 일치하는 단일 문서( findOneDocument()
의 경우), long
숫자 값( count()
의 경우) 또는 일치하는 문서 배열(다음의 경우)로 해석되는 결과를 반환합니다. find()
).
단일 문서 찾기
collection.findOneDocument()를 사용하여 단일 문서를 찾을 수 있습니다.
이 스니펫은 매장 그룹의 커피 음료를 설명하는 문서 컬렉션에서 단일 문서를 name
string 찾습니다.
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))") } }
여러 문서 찾기
collection.find()를 사용하여 여러 문서를 찾을 수 있습니다.
이 스니펫은 문서의 name
필드에 '아메리카노' 값이 포함된 매장 그룹의 판매용 커피 음료를 설명하는 문서 컬렉션에서 모든 문서를 찾습니다.
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"]))") } } }
문서 찾기 및 정렬
원하는 정렬 옵션으로 FindOptions 인스턴스를 초기화하여 컬렉션의 문서를 정렬할 수 있습니다. FindOptions
에는 limit, projection 및 sorting 의 세 가지 매개 변수가 있습니다. sorting
인수는 각 키가 필드를 나타내는 키-값 쌍의 배열일 수 있습니다. 각 키에 대해 1
값은 내림차순으로 정렬하거나 -1
값은 오름차순으로 정렬합니다.
그런 FindOptions
다음 collection.find()
쿼리를 실행할 때 인스턴스를 메서드에 전달합니다.
이 스니펫은 매장 그룹에서 판매 하는 커피 음료를 설명하는 문서 컬렉션에서 모든 문서를 찾으며, 문서의 name
필드에는 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)") } } }
collection의 문서 수 계산
collection.count() 를 사용하여 컬렉션의 문서 수를 계산할 수 있습니다. 선택적 쿼리 및 제한을 지정하여 계산할 문서를 결정할 수 있습니다. 쿼리를 지정하지 않으면 작업은 컬렉션의 모든 문서 수를 계산합니다.
이 스니펫은 매장 그룹에서 판매할 커피 음료를 설명하는 문서 컬렉션의 문서 수를 계산하며, 문서의 name
필드에는 '오늘의 원두' 값이 포함되어 있습니다.
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)") } }
문서 업데이트
이 코드 스니펫은 MongoDB 컬렉션에 저장된 데이터를 모바일 애플리케이션에서 업데이트하는 방법을 보여줍니다. 업데이트 작업은 쿼리를 사용하여 업데이트할 문서를 지정하고 업데이트 연산자 를 사용하여 쿼리와 일치하는 문서를 변경하는 방법을 설명합니다. 업데이트 작업은 UpdateResult 또는 Error
로 해석되는 결과를 반환합니다.
단일 문서 업데이트
collection.updateOneDocument()를 사용하여 단일 문서를 업데이트할 수 있습니다.
이 스니펫은 매장 그룹에서 판매할 커피 음료를 설명하는 문서 컬렉션에서 단일 문서를 업데이트합니다. 이 업데이트 작업은 name
필드에 " 오늘의 콩 " 값이 포함된 문서를 쿼리하고 containsDairy
필드를 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") } }
여러 문서 업데이트하기
collection.updateManyDocuments()를 사용하여 여러 문서를 업데이트할 수 있습니다.
이 스니펫은 매장 그룹에서 판매하는 커피 음료를 설명하는 문서 컬렉션에서 여러 문서를 업데이트합니다. 이 업데이트 작업은 name
필드에 "Bean of the Day" 값이 포함된 문서를 쿼리하고 containsDairy
필드를 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.") } }
문서 업서트
업데이트 작업이 컬렉션의 문서와 일치하지 않는 경우 upsert
옵션을 true
로 설정하여 업데이트 쿼리와 일치하는 컬렉션에 새 문서 하나를 자동으로 삽입할 수 있습니다.
다음 스니펫은 매장 그룹에서 판매하는 커피 음료를 설명하는 문서 컬렉션의 문서를 업데이트합니다. 쿼리와 일치하는 문서가 없으면 문서가 없으면 새 문서를 삽입합니다. 이 작업은 name
필드의 값이 " 오늘의 콩 " 이고 storeNumber
필드의 값이 55
인 문서를 쿼리합니다.
이 스니펫은 upsert
옵션을 true
로 설정하므로 쿼리와 일치하는 문서가 없으면 MongoDB는 쿼리와 지정된 업데이트를 모두 포함하는 새 문서를 만듭니다.
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") } } }
문서 삭제
이 코드 스니펫은 MongoDB collection에 저장된 문서를 모바일 애플리케이션에서 삭제하는 방법을 보여줍니다. 삭제 작업은 쿼리를 사용하여 삭제할 문서를 지정하고 삭제된 문서의 개수 Int
또는 Error
로 확인되는 결과를 반환합니다.
단일 문서 삭제
collection.deleteOneDocument()를 사용하여 컬렉션에서 단일 문서를 삭제할 수 있습니다.
이 스니펫은 매장 그룹에서 판매할 커피 음료를 설명하는 문서 모음에서 문서 한 개를 삭제합니다. 이 작업은 name
필드 값이 'Mocha'이고 storeNumber
필드 값이 17
인 문서를 쿼리하고 삭제합니다.
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.") } }
여러 문서 삭제
collection.deleteManyDocuments()를 사용하여 컬렉션에서 여러 항목을 삭제할 수 있습니다.
이 스니펫은 name
필드에 'Caramel Late' 값이 포함된 문서에 대한 쿼리와 일치하는 매장 그룹에서 판매할 커피 음료를 설명하는 문서 컬렉션의 모든 문서를 삭제합니다.
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.") } }
변화를 주시하세요
컬렉션의 문서가 생성, 수정 또는 삭제될 때마다 MongoDB 가 보내는 변경 알림을 컬렉션에서 확인할 수 있습니다. 각 알림은 변경된 문서, 변경 방법, 이벤트를 일으킨 작업 이후의 전체 문서를 지정합니다.
중요
서버리스 제한 사항
데이터 원본이 Atlas 서버리스 인스턴스인 경우 변경 사항을 모니터링할 수 없습니다. MongoDB 서버리스는 현재 변경 스트림을 지원하지 않습니다. 변경 스트림은 감시되는 컬렉션에서 변경 사항을 수신하는 데 사용됩니다.
컬렉션의 모든 변경 사항 보기
collection.watch() 를 호출하여 컬렉션에 대한 변경 사항 스트림을 열 수 있습니다. 이 함수는 MongoDB 컬렉션이 변경될 때 AnyBSON 변경 이벤트를 발생시키는 게시자를 생성합니다.
선택적으로 collection.watch(filterIds:) 를 사용하여 collection에서 필터링된 _ids
목록을 관찰하거나 collection.watch(matchFilter:)를 사용하여 수신 변경 이벤트에 $match
필터를 적용할 수 있습니다 .
.watch()
메서드는 ChangeEventDelegate 를 사용하여 스트림의 변경 사항을 구독할 수 있습니다. 예를 들어 다음과 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)") } }
이 코드는 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() } }
collection에서 특정 ID에 대한 변경 사항 확인
_id
을(를) 전달하여 특정 객체 목록에 대한 컬렉션의 변경 사항을 감시할 수 있습니다. ObjectId 배열을 사용하여 collection.watch(filterIds: ) 를 호출하여 해당 문서에 적용되는 변경 이벤트만 수신합니다.
이 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)") } }
아래 코드에서는 이 위임을 사용하여 CoffeeDrinks
collection의 특정 문서에 대한 변경 사항을 감시합니다.
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() } }
필터로 컬렉션의 변경 사항 보기
collection.watch(matchFilter: ) 를 호출하여 특정 기준을 충족하는 컬렉션의 문서에 대한 변경 사항 스트림을 열 수 있습니다. 이 메서드는 Document
컬렉션을 관찰하는 동안 발생하는 각 데이터베이스 이벤트 를 처리하기 위해 $match 연산자 의 쿼리로 사용되는 매개 변수를 허용합니다.
이 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)") } }
아래 코드에서는 이 위임을 사용하여 CoffeeDrink
collection의 문서에 대한 변경 사항을 감시합니다. 문서에 storeNumber
값이 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() } }
collection을 비동기 시퀀스로 보기
버전 10.37.0의 새로운 기능.
비동기 시퀀스를 열어 컬렉션의 변경 사항을 감시할 수 있습니다. 비동기 컨텍스트에서 컬렉션에서 changeEvents() 를 호출하여 변경 스트림을 엽니다. 이는 MongoDB 컬렉션의 각 변경 사항에 대한 정보가 포함된 AnyBSON 값의 비동기 시퀀스를 제공합니다.
서버에서 감시 스트림이 초기화될 때 호출되는 changeEvents(onOpen: ) 콜백을 선택적으로 제공할 수 있습니다.
changeEvents()
API는 위의 예와 마찬가지로 filterIds
또는 matchFilter
를 사용하여 컬렉션의 문서 하위 집합을 감시할 수 있습니다.
다음 스니펫은 collection 내 모든 문서의 변경 사항을 CoffeeDrinks
비동기 시퀀스로 감시합니다.
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
문서 애그리게이션
집계 작업은 집계 파이프라인이라고 하는 일련의 데이터 집계 단계를 통해 컬렉션의 모든 문서를 실행합니다. 집계를 통해 문서 필터링 및 변환, 관련 문서 그룹에 대한 요약 데이터 수집 등의 복잡한 데이터 작업을 수행할 수 있습니다.
collection.aggregate()를 사용하여 컬렉션에서 애그리게이션 작업을 구성하고 실행할 수 있습니다.
애그리게이션 작업은 애그리게이션 단계 목록을 입력으로 받아들이고 파이프라인에서 처리된 collection 또는 Error
로 해석되는 결과를 반환합니다.
문서 필터링
$match 단계를 사용하면 표준 MongoDB 쿼리 구문을 사용하여 문서를 필터링할 수 있습니다.
이 $match
단계는 storeNumber
필드 값이 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)") } } }
문서 그룹화
$group 단계를 사용하여 하나 이상의 문서에 대한 요약 데이터를 집계할 수 있습니다. MongoDB는 $group
단계의 _id
필드에 정의된 표현식을 기반으로 문서를 그룹화합니다. 필드 이름 앞에 $
를 붙여 특정 문서 필드를 참조할 수 있습니다.
이 $group
단계에서는 storeNumber
필드 값을 기준으로 문서를 정렬합니다. 그런 다음 필드 값에 해당 매장 번호가 포함된 커피 음료 문서 수를 계산합니다. 즉, 각 매장 번호별 커피 음료 수를 계산하는 것입니다.
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) } } }
문서 필드 프로젝트
$project 단계를 사용하여 문서의 특정 필드를 포함 또는 생략하거나 애그리게이션 연산자 를 사용하여 새 필드를 계산할 수 있습니다. 프로젝션은 두 가지 방식으로 작동합니다.
1
을 사용하여 필드를 포함하도록 지정합니다. 이 방법에는 지정되지 않은 모든 필드를 암시적으로 제외하는 부작용이 있습니다.0
을 사용하여 필드를 제외하도록 지정합니다. 이 방법에는 지정되지 않은 모든 필드를 암시적으로 포함하는 부작용이 있습니다.
이 두 가지 프로젝션 방법은 상호 배타적입니다. 포함할 필드를 지정한 경우 제외할 필드도 지정할 수 없으며, 그 반대의 경우도 마찬가지입니다.
참고
_id
필드는 특별한 경우에 해당하며 명시적으로 지정하지 않는 한 항상 모든 쿼리에 포함됩니다. 따라서 0
값이 있는 _id
필드를 제외하는 동시에 1
을 사용해 storeNumber
등의 다른 필드를 포함하는 것이 가능합니다. _id
필드를 제외하는 특수한 경우에만 하나의 $project
단계에서 제외와 포함을 동시에 적용할 수 있습니다.
이 예제에서는 CoffeeDrink
문서에 다음 문서와 유사한 'Store'라는 단어와 숫자를 포함하는 문자열 값인 store
필드가 있다고 가정합니다(예: 'Store 42').
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"]
다음 $project
단계에서는 _id
필드를 생략하고 name
필드를 포함하며 storeNumber
라는 새 필드를 생성합니다. storeNumber
는 다음 집계 연산자 두 개를 사용하여 생성됩니다.
$split
store
문자열 표현을 공백 문자를 둘러싼 두 개의 문자열 세그먼트로 분리합니다. 예를 들어 이처럼 '매장 42'로 분할된 값은 '매장' 및 '42'의 두 개의 요소가 있는 배열을 반환합니다.$arrayElemAt
두 번째 인수를 기반으로 배열에서 특정 요소를 선택합니다. 이 경우 배열 인덱스는0
이므로 값1
은$split
연산자로 생성된 배열에서 두 번째 요소를 선택합니다. 예를 들어 이 연산에 전달된 값 ['매장', '42']는 값 '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) } } }
문서에 필드 추가
$addFields 단계에서 애그리게이션 연산자를 사용하여 계산된 값이 있는 새 필드를 추가할 수 있습니다.
참고
$addFields
는 $project와 유사하지만 필드를 포함하거나 생략할 수 없습니다.
이 예제에서는 CoffeeDrink
문서에 다음 문서와 유사한 'Store'라는 단어와 숫자를 포함하는 문자열 값인 store
필드가 있다고 가정합니다(예: 'Store 42').
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"]
다음 $addFields
단계에서는 storeNumber
라는 새 필드를 만듭니다. 여기서 값은 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) } } }
배열 값 풀기
$unwind 단계를 사용하여 배열이 포함된 단일 문서를 해당 배열의 개별 값이 포함된 여러 문서로 변환할 수 있습니다. 배열 필드를 풀면 MongoDB는 배열 필드의 각 요소에 대해 각 문서를 한 번씩 복사하지만 배열 값을 각 사본의 배열 요소로 바꿉니다.
featuredInPromotions
배열이 포함된 다음 문서를 가정해 보겠습니다.
let drink: Document = [ "name": "Maple Latte", "beanRegion": "Yirgacheffe, Ethiopia", "containsDairy": true, "storeNumber": 42, "featuredInPromotions": [ "Spring into Spring", "Tastes of Fall", "Winter Delights" ] ]
다음 $unwind
단계에서는 각 문서에서 items
배열의 각 요소에 대해 새 문서를 만듭니다. 또한 각 새 문서에 itemIndex
라는 필드를 추가하여 원본 배열에서 요소의 위치 인덱스를 지정합니다:
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)") } } }
그런 다음 그룹 문서 예시 에서와 같이 각 프로모션의 커피 음료수 featuredInPromotions
및 $sum
의 값으로 $group
를 수행하거나 데이터를 기반으로 다른 계산 또는 변환을 수행할 수 있습니다.