12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091 |
- ////////////////////////////////////////////////////////////////////////////
- //
- // Copyright 2020 Realm Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //
- ////////////////////////////////////////////////////////////////////////////
- import Foundation
- import Realm
- import Realm.Private
- /**
- * The `MongoClient` enables reading and writing on a MongoDB database via the Realm Cloud service.
- *
- * It provides access to instances of `MongoDatabase`, which in turn provide access to specific
- * `MongoCollection`s that hold your data.
- *
- * - Note:
- * Before you can read or write data, a user must log in.
- *
- * - SeeAlso:
- * `App`, `MongoDatabase`, `MongoCollection`
- */
- public typealias MongoClient = RLMMongoClient
- /**
- * The `MongoDatabase` represents a MongoDB database, which holds a group
- * of collections that contain your data.
- *
- * It can be retrieved from the `MongoClient`.
- *
- * Use it to get `MongoCollection`s for reading and writing data.
- *
- * - Note:
- * Before you can read or write data, a user must log in`.
- *
- * - SeeAlso:
- * `MongoClient`, `MongoCollection`
- */
- public typealias MongoDatabase = RLMMongoDatabase
- /// Options to use when executing a `find` command on a `MongoCollection`.
- public typealias FindOptions = RLMFindOptions
- extension FindOptions {
- /// Limits the fields to return for all matching documents.
- public var projection: Document? {
- get {
- return ObjectiveCSupport.convert(object: __projection)?.documentValue
- }
- set {
- __projection = newValue.map(AnyBSON.init).map(ObjectiveCSupport.convert) as? RLMBSON
- }
- }
- /// The order in which to return matching documents.
- public var sort: Document? {
- get {
- return ObjectiveCSupport.convert(object: __sort)?.documentValue
- }
- set {
- __sort = newValue.map(AnyBSON.init).map(ObjectiveCSupport.convert) as? RLMBSON
- }
- }
- /// Options to use when executing a `find` command on a `MongoCollection`.
- /// - Parameters:
- /// - limit: The maximum number of documents to return. Specifying 0 will return all documents.
- /// - projected: Limits the fields to return for all matching documents.
- /// - sort: The order in which to return matching documents.
- public convenience init(_ limit: Int?, _ projection: Document?, _ sort: Document?) {
- self.init()
- self.limit = limit ?? 0
- self.projection = projection
- self.sort = sort
- }
- /// Options to use when executing a `find` command on a `MongoCollection`.
- /// - Parameters:
- /// - limit: The maximum number of documents to return. Specifying 0 will return all documents.
- /// - projected: Limits the fields to return for all matching documents.
- /// - sort: The order in which to return matching documents.
- public convenience init(limit: Int?, projection: Document?, sort: Document?) {
- self.init(limit, projection, sort)
- }
- }
- /// Options to use when executing a `findOneAndUpdate`, `findOneAndReplace`,
- /// or `findOneAndDelete` command on a `MongoCollection`.
- public typealias FindOneAndModifyOptions = RLMFindOneAndModifyOptions
- extension FindOneAndModifyOptions {
- /// Limits the fields to return for all matching documents.
- public var projection: Document? {
- get {
- return ObjectiveCSupport.convert(object: __projection)?.documentValue
- }
- set {
- __projection = newValue.map(AnyBSON.init).map(ObjectiveCSupport.convert) as? RLMBSON
- }
- }
- /// The order in which to return matching documents.
- public var sort: Document? {
- get {
- return ObjectiveCSupport.convert(object: __sort)?.documentValue
- }
- set {
- __sort = newValue.map(AnyBSON.init).map(ObjectiveCSupport.convert) as? RLMBSON
- }
- }
- /// Options to use when executing a `findOneAndUpdate`, `findOneAndReplace`,
- /// or `findOneAndDelete` command on a `MongoCollection`
- /// - Parameters:
- /// - projection: Limits the fields to return for all matching documents.
- /// - sort: The order in which to return matching documents.
- /// - upsert: Whether or not to perform an upsert, default is false
- /// (only available for findOneAndReplace and findOneAndUpdate)
- /// - shouldReturnNewDocument: When true then the new document is returned,
- /// Otherwise the old document is returned (default)
- /// (only available for findOneAndReplace and findOneAndUpdate)
- public convenience init(_ projection: Document?,
- _ sort: Document?,
- _ upsert: Bool=false,
- _ shouldReturnNewDocument: Bool=false) {
- self.init()
- self.projection = projection
- self.sort = sort
- self.upsert = upsert
- self.shouldReturnNewDocument = shouldReturnNewDocument
- }
- /// Options to use when executing a `findOneAndUpdate`, `findOneAndReplace`,
- /// or `findOneAndDelete` command on a `MongoCollection`
- /// - Parameters:
- /// - projection: Limits the fields to return for all matching documents.
- /// - sort: The order in which to return matching documents.
- /// - upsert: Whether or not to perform an upsert, default is false
- /// (only available for findOneAndReplace and findOneAndUpdate)
- /// - shouldReturnNewDocument: When true then the new document is returned,
- /// Otherwise the old document is returned (default)
- /// (only available for findOneAndReplace and findOneAndUpdate)
- public convenience init(projection: Document?,
- sort: Document?,
- upsert: Bool=false,
- shouldReturnNewDocument: Bool=false) {
- self.init(projection, sort, upsert, shouldReturnNewDocument)
- }
- }
- /// The result of an `updateOne` or `updateMany` operation a `MongoCollection`.
- public typealias UpdateResult = RLMUpdateResult
- /// Block which returns Result.success(DocumentId) on a successful insert or Result.failure(error)
- public typealias MongoInsertBlock = (Result<AnyBSON, Error>) -> Void
- /// Block which returns Result.success([ObjectId]) on a successful insertMany or Result.failure(error)
- public typealias MongoInsertManyBlock = (Result<[AnyBSON], Error>) -> Void
- /// Block which returns Result.success([Document]) on a successful find operation or Result.failure(error)
- public typealias MongoFindBlock = (Result<[Document], Error>) -> Void
- /// Block which returns Result.success(Document?) on a successful findOne operation or Result.failure(error)
- public typealias MongoFindOneBlock = (Result<Document?, Error>) -> Void
- /// Block which returns Result.success(Int) on a successful count operation or Result.failure(error)
- public typealias MongoCountBlock = (Result<Int, Error>) -> Void
- /// Block which returns Result.success(UpdateResult) on a successful update operation or Result.failure(error)
- public typealias MongoUpdateBlock = (Result<UpdateResult, Error>) -> Void
- /**
- * The `MongoCollection` represents a MongoDB collection.
- *
- * You can get an instance from a `MongoDatabase`.
- *
- * Create, read, update, and delete methods are available.
- *
- * Operations against the Realm Cloud server are performed asynchronously.
- *
- * - Note:
- * Before you can read or write data, a user must log in.
- *
- * - SeeAlso:
- * `MongoClient`, `MongoDatabase`
- */
- public typealias MongoCollection = RLMMongoCollection
- /// Acts as a middleman and processes events with WatchStream
- public typealias ChangeStream = RLMChangeStream
- /// Delegate which is used for subscribing to changes on a `MongoCollection.watch()` stream.
- public protocol ChangeEventDelegate: AnyObject {
- /// The stream was opened.
- /// - Parameter changeStream: The `ChangeStream` subscribing to the stream changes.
- func changeStreamDidOpen(_ changeStream: ChangeStream )
- /// The stream has been closed.
- /// - Parameter error: If an error occured when closing the stream, an error will be passed.
- func changeStreamDidClose(with error: Error?)
- /// A error has occured while streaming.
- /// - Parameter error: The streaming error.
- func changeStreamDidReceive(error: Error)
- /// Invoked when a change event has been received.
- /// - Parameter changeEvent:The change event in BSON format.
- func changeStreamDidReceive(changeEvent: AnyBSON?)
- }
- extension MongoCollection {
- /// Opens a MongoDB change stream against the collection to watch for changes. The resulting stream will be notified
- /// of all events on this collection that the active user is authorized to see based on the configured MongoDB
- /// rules.
- /// - Parameters:
- /// - delegate: The delegate that will react to events and errors from the resulting change stream.
- /// - queue: Dispatches streaming events to an optional queue, if no queue is provided the main queue is used
- /// - Returns: A ChangeStream which will manage the streaming events.
- public func watch(delegate: ChangeEventDelegate, queue: DispatchQueue = .main) -> ChangeStream {
- return self.__watch(with: ChangeEventDelegateProxy(delegate),
- delegateQueue: queue)
- }
- /// Opens a MongoDB change stream against the collection to watch for changes. The provided BSON document will be
- /// used as a match expression filter on the change events coming from the stream.
- ///
- /// See https://docs.mongodb.com/manual/reference/operator/aggregation/match/ for documentation around how to define
- /// a match filter.
- ///
- /// Defining the match expression to filter ChangeEvents is similar to defining the match expression for triggers:
- /// https://docs.mongodb.com/realm/triggers/database-triggers/
- /// - Parameters:
- /// - matchFilter: The $match filter to apply to incoming change events
- /// - delegate: The delegate that will react to events and errors from the resulting change stream.
- /// - queue: Dispatches streaming events to an optional queue, if no queue is provided the main queue is used
- /// - Returns: A ChangeStream which will manage the streaming events.
- public func watch(matchFilter: Document, delegate: ChangeEventDelegate, queue: DispatchQueue = .main) -> ChangeStream {
- let filterBSON = ObjectiveCSupport.convert(object: .document(matchFilter)) as! [String: RLMBSON]
- return self.__watch(withMatchFilter: filterBSON,
- delegate: ChangeEventDelegateProxy(delegate),
- delegateQueue: queue)
- }
- /// Opens a MongoDB change stream against the collection to watch for changes
- /// made to specific documents. The documents to watch must be explicitly
- /// specified by their _id.
- /// - Parameters:
- /// - filterIds: The list of _ids in the collection to watch.
- /// - delegate: The delegate that will react to events and errors from the resulting change stream.
- /// - queue: Dispatches streaming events to an optional queue, if no queue is provided the main queue is used
- /// - Returns: A ChangeStream which will manage the streaming events.
- public func watch(filterIds: [ObjectId], delegate: ChangeEventDelegate, queue: DispatchQueue = .main) -> ChangeStream {
- let filterBSON = ObjectiveCSupport.convert(object: .array(filterIds.map {AnyBSON($0)})) as! [RLMObjectId]
- return self.__watch(withFilterIds: filterBSON,
- delegate: ChangeEventDelegateProxy(delegate),
- delegateQueue: queue)
- }
- }
- // MongoCollection methods with result type completions
- extension MongoCollection {
- /// Encodes the provided value to BSON and inserts it. If the value is missing an identifier, one will be
- /// generated for it.
- /// - Parameters:
- /// - document: document A `Document` value to insert.
- /// - completion: The result of attempting to perform the insert. An Id will be returned for the inserted object on sucess
- public func insertOne(_ document: Document, _ completion: @escaping MongoInsertBlock) {
- let bson = ObjectiveCSupport.convert(object: .document(document))
- self.__insertOneDocument(bson as! [String: RLMBSON]) { objectId, error in
- if let objectId = ObjectiveCSupport.convert(object: objectId) {
- completion(.success(objectId))
- } else {
- completion(.failure(error ?? Realm.Error.callFailed))
- }
- }
- }
- /// Encodes the provided values to BSON and inserts them. If any values are missing identifiers,
- /// they will be generated.
- /// - Parameters:
- /// - documents: The `Document` values in a bson array to insert.
- /// - completion: The result of the insert, returns an array inserted document ids in order.
- public func insertMany(_ documents: [Document], _ completion: @escaping MongoInsertManyBlock) {
- let bson = ObjectiveCSupport.convert(object: .array(documents.map {.document($0)}))
- self.__insertManyDocuments(bson as! [[String: RLMBSON]]) { objectIds, error in
- if let objectIds = objectIds?.compactMap(ObjectiveCSupport.convert) {
- completion(.success(objectIds))
- } else {
- completion(.failure(error ?? Realm.Error.callFailed))
- }
- }
- }
- /// Finds the documents in this collection which match the provided filter.
- /// - Parameters:
- /// - filter: A `Document` as bson that should match the query.
- /// - options: `FindOptions` to use when executing the command.
- /// - completion: The resulting bson array of documents or error if one occurs
- public func find(filter: Document,
- options: FindOptions,
- _ completion: @escaping MongoFindBlock) {
- let bson = ObjectiveCSupport.convert(object: .document(filter))
- self.__findWhere(bson as! [String: RLMBSON], options: options) { documents, error in
- let bson: [Document]? = documents?.map { $0.mapValues { ObjectiveCSupport.convert(object: $0) } }
- if let bson = bson {
- completion(.success(bson))
- } else {
- completion(.failure(error ?? Realm.Error.callFailed))
- }
- }
- }
- /// Finds the documents in this collection which match the provided filter.
- /// - Parameters:
- /// - filter: A `Document` as bson that should match the query.
- /// - completion: The resulting bson array of documents or error if one occurs
- public func find(filter: Document,
- _ completion: @escaping MongoFindBlock) {
- let bson = ObjectiveCSupport.convert(object: .document(filter))
- self.__findWhere(bson as! [String: RLMBSON]) { documents, error in
- let bson: [Document]? = documents?.map { $0.mapValues { ObjectiveCSupport.convert(object: $0) } }
- if let bson = bson {
- completion(.success(bson))
- } else {
- completion(.failure(error ?? Realm.Error.callFailed))
- }
- }
- }
- /// Returns one document from a collection or view which matches the
- /// provided filter. If multiple documents satisfy the query, this method
- /// returns the first document according to the query's sort order or natural
- /// order.
- /// - Parameters:
- /// - filter: A `Document` as bson that should match the query.
- /// - options: `FindOptions` to use when executing the command.
- /// - completion: The resulting bson or error if one occurs
- public func findOneDocument(filter: Document,
- options: FindOptions,
- _ completion: @escaping MongoFindOneBlock) {
- let bson = ObjectiveCSupport.convert(object: .document(filter))
- self.__findOneDocumentWhere(bson as! [String: RLMBSON], options: options) { document, error in
- if let error = error {
- completion(.failure(error))
- } else {
- let bson: Document? = document?.mapValues { ObjectiveCSupport.convert(object: $0) }
- completion(.success(bson))
- }
- }
- }
- /// Returns one document from a collection or view which matches the
- /// provided filter. If multiple documents satisfy the query, this method
- /// returns the first document according to the query's sort order or natural
- /// order.
- /// - Parameters:
- /// - filter: A `Document` as bson that should match the query.
- /// - completion: The resulting bson or error if one occurs
- public func findOneDocument(filter: Document,
- _ completion: @escaping MongoFindOneBlock) {
- let bson = ObjectiveCSupport.convert(object: .document(filter))
- self.__findOneDocumentWhere(bson as! [String: RLMBSON]) { document, error in
- if let error = error {
- completion(.failure(error))
- } else {
- let bson: Document? = document?.mapValues { ObjectiveCSupport.convert(object: $0) }
- completion(.success(bson))
- }
- }
- }
- /// Runs an aggregation framework pipeline against this collection.
- /// - Parameters:
- /// - pipeline: A bson array made up of `Documents` containing the pipeline of aggregation operations to perform.
- /// - completion: The resulting bson array of documents or error if one occurs
- public func aggregate(pipeline: [Document],
- _ completion: @escaping MongoFindBlock) {
- let bson = ObjectiveCSupport.convert(object: .array(pipeline.map {.document($0)}))
- self.__aggregate(withPipeline: bson as! [[String: RLMBSON]]) { documents, error in
- let bson: [Document]? = documents?.map { $0.mapValues { ObjectiveCSupport.convert(object: $0) } }
- if let bson = bson {
- completion(.success(bson))
- } else {
- completion(.failure(error ?? Realm.Error.callFailed))
- }
- }
- }
- /// Counts the number of documents in this collection matching the provided filter.
- /// - Parameters:
- /// - filter: A `Document` as bson that should match the query.
- /// - limit: The max amount of documents to count
- /// - completion: Returns the count of the documents that matched the filter.
- public func count(filter: Document,
- limit: Int,
- _ completion: @escaping MongoCountBlock) {
- let bson = ObjectiveCSupport.convert(object: .document(filter))
- self.__countWhere(bson as! [String: RLMBSON], limit: limit) { count, error in
- if let error = error {
- completion(.failure(error))
- } else {
- completion(.success(count))
- }
- }
- }
- /// Counts the number of documents in this collection matching the provided filter.
- /// - Parameters:
- /// - filter: A `Document` as bson that should match the query.
- /// - completion: Returns the count of the documents that matched the filter.
- public func count(filter: Document,
- _ completion: @escaping MongoCountBlock) {
- let bson = ObjectiveCSupport.convert(object: .document(filter))
- self.__countWhere(bson as! [String: RLMBSON]) { count, error in
- if let error = error {
- completion(.failure(error))
- } else {
- completion(.success(count))
- }
- }
- }
- /// Deletes a single matching document from the collection.
- /// - Parameters:
- /// - filter: A `Document` as bson that should match the query.
- /// - completion: The result of performing the deletion. Returns the count of deleted objects
- public func deleteOneDocument(filter: Document,
- _ completion: @escaping MongoCountBlock) {
- let bson = ObjectiveCSupport.convert(object: .document(filter))
- self.__deleteOneDocumentWhere(bson as! [String: RLMBSON]) { count, error in
- if let error = error {
- completion(.failure(error))
- } else {
- completion(.success(count))
- }
- }
- }
- /// Deletes multiple documents
- /// - Parameters:
- /// - filter: Document representing the match criteria
- /// - completion: The result of performing the deletion. Returns the count of the deletion
- public func deleteManyDocuments(filter: Document,
- _ completion: @escaping MongoCountBlock) {
- let bson = ObjectiveCSupport.convert(object: .document(filter))
- self.__deleteManyDocumentsWhere(bson as! [String: RLMBSON]) { count, error in
- if let error = error {
- completion(.failure(error))
- } else {
- completion(.success(count))
- }
- }
- }
- /// Updates a single document matching the provided filter in this collection.
- /// - Parameters:
- /// - filter: A bson `Document` representing the match criteria.
- /// - update: A bson `Document` representing the update to be applied to a matching document.
- /// - upsert: When true, creates a new document if no document matches the query.
- /// - completion: The result of the attempt to update a document.
- public func updateOneDocument(filter: Document,
- update: Document,
- upsert: Bool,
- _ completion: @escaping MongoUpdateBlock) {
- let filterBSON = ObjectiveCSupport.convert(object: .document(filter))
- let updateBSON = ObjectiveCSupport.convert(object: .document(update))
- self.__updateOneDocumentWhere(filterBSON as! [String: RLMBSON],
- updateDocument: updateBSON as! [String: RLMBSON],
- upsert: upsert) { updateResult, error in
- if let updateResult = updateResult {
- completion(.success(updateResult))
- } else {
- completion(.failure(error ?? Realm.Error.callFailed))
- }
- }
- }
- /// Updates a single document matching the provided filter in this collection.
- /// - Parameters:
- /// - filter: A bson `Document` representing the match criteria.
- /// - update: A bson `Document` representing the update to be applied to a matching document.
- /// - completion: The result of the attempt to update a document.
- public func updateOneDocument(filter: Document,
- update: Document,
- _ completion: @escaping MongoUpdateBlock) {
- let filterBSON = ObjectiveCSupport.convert(object: .document(filter))
- let updateBSON = ObjectiveCSupport.convert(object: .document(update))
- self.__updateOneDocumentWhere(filterBSON as! [String: RLMBSON],
- updateDocument: updateBSON as! [String: RLMBSON]) { updateResult, error in
- if let updateResult = updateResult {
- completion(.success(updateResult))
- } else {
- completion(.failure(error ?? Realm.Error.callFailed))
- }
- }
- }
- /// Updates multiple documents matching the provided filter in this collection.
- /// - Parameters:
- /// - filter: A bson `Document` representing the match criteria.
- /// - update: A bson `Document` representing the update to be applied to a matching document.
- /// - upsert: When true, creates a new document if no document matches the query.
- /// - completion: The result of the attempt to update a document.
- public func updateManyDocuments(filter: Document,
- update: Document,
- upsert: Bool,
- _ completion: @escaping MongoUpdateBlock) {
- let filterBSON = ObjectiveCSupport.convert(object: .document(filter))
- let updateBSON = ObjectiveCSupport.convert(object: .document(update))
- self.__updateManyDocumentsWhere(filterBSON as! [String: RLMBSON],
- updateDocument: updateBSON as! [String: RLMBSON],
- upsert: upsert) { updateResult, error in
- if let updateResult = updateResult {
- completion(.success(updateResult))
- } else {
- completion(.failure(error ?? Realm.Error.callFailed))
- }
- }
- }
- /// Updates multiple documents matching the provided filter in this collection.
- /// - Parameters:
- /// - filter: A bson `Document` representing the match criteria.
- /// - update: A bson `Document` representing the update to be applied to a matching document.
- /// - completion: The result of the attempt to update a document.
- public func updateManyDocuments(filter: Document,
- update: Document,
- _ completion: @escaping MongoUpdateBlock) {
- let filterBSON = ObjectiveCSupport.convert(object: .document(filter))
- let updateBSON = ObjectiveCSupport.convert(object: .document(update))
- self.__updateManyDocumentsWhere(filterBSON as! [String: RLMBSON],
- updateDocument: updateBSON as! [String: RLMBSON]) { updateResult, error in
- if let updateResult = updateResult {
- completion(.success(updateResult))
- } else {
- completion(.failure(error ?? Realm.Error.callFailed))
- }
- }
- }
- /// Updates a single document in a collection based on a query filter and
- /// returns the document in either its pre-update or post-update form. Unlike
- /// `updateOneDocument`, this action allows you to atomically find, update, and
- /// return a document with the same command. This avoids the risk of other
- /// update operations changing the document between separate find and update
- /// operations.
- /// - Parameters:
- /// - filter: A bson `Document` representing the match criteria.
- /// - update: A bson `Document` representing the update to be applied to a matching document.
- /// - options: `RemoteFindOneAndModifyOptions` to use when executing the command.
- /// - completion: The result of the attempt to update a document.
- public func findOneAndUpdate(filter: Document,
- update: Document,
- options: FindOneAndModifyOptions,
- _ completion: @escaping MongoFindOneBlock) {
- let filterBSON = ObjectiveCSupport.convert(object: .document(filter))
- let updateBSON = ObjectiveCSupport.convert(object: .document(update))
- self.__findOneAndUpdateWhere(filterBSON as! [String: RLMBSON],
- updateDocument: updateBSON as! [String: RLMBSON],
- options: options) { document, error in
- if let error = error {
- completion(.failure(error))
- } else {
- let bson: Document? = document?.mapValues { ObjectiveCSupport.convert(object: $0) }
- completion(.success(bson))
- }
- }
- }
- /// Updates a single document in a collection based on a query filter and
- /// returns the document in either its pre-update or post-update form. Unlike
- /// `updateOneDocument`, this action allows you to atomically find, update, and
- /// return a document with the same command. This avoids the risk of other
- /// update operations changing the document between separate find and update
- /// operations.
- /// - Parameters:
- /// - filter: A bson `Document` representing the match criteria.
- /// - update: A bson `Document` representing the update to be applied to a matching document.
- /// - completion: The result of the attempt to update a document.
- public func findOneAndUpdate(filter: Document,
- update: Document,
- _ completion: @escaping MongoFindOneBlock) {
- let filterBSON = ObjectiveCSupport.convert(object: .document(filter))
- let updateBSON = ObjectiveCSupport.convert(object: .document(update))
- self.__findOneAndUpdateWhere(filterBSON as! [String: RLMBSON],
- updateDocument: updateBSON as! [String: RLMBSON]) { document, error in
- if let error = error {
- completion(.failure(error))
- } else {
- let bson: Document? = document?.mapValues { ObjectiveCSupport.convert(object: $0) }
- completion(.success(bson))
- }
- }
- }
- /// Overwrites a single document in a collection based on a query filter and
- /// returns the document in either its pre-replacement or post-replacement
- /// form. Unlike `updateOneDocument`, this action allows you to atomically find,
- /// replace, and return a document with the same command. This avoids the
- /// risk of other update operations changing the document between separate
- /// find and update operations.
- /// - Parameters:
- /// - filter: A `Document` that should match the query.
- /// - replacement: A `Document` describing the replacement.
- /// - options: `FindOneAndModifyOptions` to use when executing the command.
- /// - completion: The result of the attempt to replace a document.
- public func findOneAndReplace(filter: Document,
- replacement: Document,
- options: FindOneAndModifyOptions,
- _ completion: @escaping MongoFindOneBlock) {
- let filterBSON = ObjectiveCSupport.convert(object: .document(filter))
- let replacementBSON = ObjectiveCSupport.convert(object: .document(replacement))
- self.__findOneAndReplaceWhere(filterBSON as! [String: RLMBSON],
- replacementDocument: replacementBSON as! [String: RLMBSON],
- options: options) { document, error in
- if let error = error {
- completion(.failure(error))
- } else {
- let bson: Document? = document?.mapValues { ObjectiveCSupport.convert(object: $0) }
- completion(.success(bson))
- }
- }
- }
- /// Overwrites a single document in a collection based on a query filter and
- /// returns the document in either its pre-replacement or post-replacement
- /// form. Unlike `updateOneDocument`, this action allows you to atomically find,
- /// replace, and return a document with the same command. This avoids the
- /// risk of other update operations changing the document between separate
- /// find and update operations.
- /// - Parameters:
- /// - filter: A `Document` that should match the query.
- /// - replacement: A `Document` describing the replacement.
- /// - options: `RLMFindOneAndModifyOptions` to use when executing the command.
- /// - completion: The result of the attempt to replace a document.
- public func findOneAndReplace(filter: Document,
- replacement: Document,
- _ completion: @escaping MongoFindOneBlock) {
- let filterBSON = ObjectiveCSupport.convert(object: .document(filter))
- let replacementBSON = ObjectiveCSupport.convert(object: .document(replacement))
- self.__findOneAndReplaceWhere(filterBSON as! [String: RLMBSON],
- replacementDocument: replacementBSON as! [String: RLMBSON]) { document, error in
- if let error = error {
- completion(.failure(error))
- } else {
- let bson: Document? = document?.mapValues { ObjectiveCSupport.convert(object: $0) }
- completion(.success(bson))
- }
- }
- }
- /// Removes a single document from a collection based on a query filter and
- /// returns a document with the same form as the document immediately before
- /// it was deleted. Unlike `deleteOneDocument`, this action allows you to atomically
- /// find and delete a document with the same command. This avoids the risk of
- /// other update operations changing the document between separate find and
- /// delete operations.
- /// - Parameters:
- /// - filter: A `Document` that should match the query.
- /// - options: `FindOneAndModifyOptions` to use when executing the command.
- /// - completion: The result of the attempt to delete a document.
- public func findOneAndDelete(filter: Document,
- options: FindOneAndModifyOptions,
- _ completion: @escaping MongoFindOneBlock) {
- let filterBSON = ObjectiveCSupport.convert(object: .document(filter))
- self.__findOneAndDeleteWhere(filterBSON as! [String: RLMBSON],
- options: options) { document, error in
- if let error = error {
- completion(.failure(error))
- } else {
- let bson: Document? = document?.mapValues { ObjectiveCSupport.convert(object: $0) }
- completion(.success(bson))
- }
- }
- }
- /// Removes a single document from a collection based on a query filter and
- /// returns a document with the same form as the document immediately before
- /// it was deleted. Unlike `deleteOneDocument`, this action allows you to atomically
- /// find and delete a document with the same command. This avoids the risk of
- /// other update operations changing the document between separate find and
- /// delete operations.
- /// - Parameters:
- /// - filter: A `Document` that should match the query.
- /// - completion: The result of the attempt to delete a document.
- public func findOneAndDelete(filter: Document,
- _ completion: @escaping MongoFindOneBlock) {
- let filterBSON = ObjectiveCSupport.convert(object: .document(filter))
- self.__findOneAndDeleteWhere(filterBSON as! [String: RLMBSON]) { document, error in
- if let error = error {
- completion(.failure(error))
- } else {
- let bson: Document? = document?.mapValues { ObjectiveCSupport.convert(object: $0) }
- completion(.success(bson))
- }
- }
- }
- }
- private class ChangeEventDelegateProxy: RLMChangeEventDelegate {
- private weak var proxyDelegate: ChangeEventDelegate?
- init(_ proxyDelegate: ChangeEventDelegate) {
- self.proxyDelegate = proxyDelegate
- }
- func changeStreamDidOpen(_ changeStream: RLMChangeStream) {
- proxyDelegate?.changeStreamDidOpen(changeStream)
- }
- func changeStreamDidCloseWithError(_ error: Error?) {
- proxyDelegate?.changeStreamDidClose(with: error)
- }
- func changeStreamDidReceiveError(_ error: Error) {
- proxyDelegate?.changeStreamDidReceive(error: error)
- }
- func changeStreamDidReceiveChangeEvent(_ changeEvent: RLMBSON) {
- let bson = ObjectiveCSupport.convert(object: changeEvent)
- proxyDelegate?.changeStreamDidReceive(changeEvent: bson)
- }
- }
- #if canImport(Combine)
- import Combine
- @available(OSX 10.15, watchOS 6.0, iOS 13.0, iOSApplicationExtension 13.0, OSXApplicationExtension 10.15, tvOS 13.0, *)
- extension Publishers {
- class WatchSubscription<S: Subscriber>: ChangeEventDelegate, Subscription where S.Input == AnyBSON, S.Failure == Error {
- private let collection: MongoCollection
- private var changeStream: ChangeStream?
- private var subscriber: S?
- private var onOpen: (() -> Void)?
- init(collection: MongoCollection,
- subscriber: S,
- queue: DispatchQueue = .main,
- filterIds: [ObjectId]? = nil,
- matchFilter: Document? = nil,
- onOpen: (() -> Void)? = nil) {
- self.collection = collection
- self.subscriber = subscriber
- self.onOpen = onOpen
- if let matchFilter = matchFilter {
- changeStream = collection.watch(matchFilter: matchFilter,
- delegate: self,
- queue: queue)
- } else if let filterIds = filterIds {
- changeStream = collection.watch(filterIds: filterIds,
- delegate: self,
- queue: queue)
- } else {
- changeStream = collection.watch(delegate: self,
- queue: queue)
- }
- }
- func request(_ demand: Subscribers.Demand) { }
- func cancel() {
- changeStream?.close()
- }
- func changeStreamDidOpen(_ changeStream: RLMChangeStream) {
- onOpen?()
- }
- func changeStreamDidClose(with error: Error?) {
- guard let error = error else {
- subscriber?.receive(completion: .finished)
- return
- }
- subscriber?.receive(completion: .failure(error))
- }
- func changeStreamDidReceive(error: Error) {
- subscriber?.receive(completion: .failure(error))
- }
- func changeStreamDidReceive(changeEvent: AnyBSON?) {
- guard let changeEvent = changeEvent else {
- return
- }
- _ = subscriber?.receive(changeEvent)
- }
- }
- /// A publisher that emits a change event each time the remote MongoDB collection changes.
- public struct WatchPublisher: Publisher {
- public typealias Output = AnyBSON
- public typealias Failure = Error
- private let collection: MongoCollection
- private let queue: DispatchQueue
- private let filterIds: [ObjectId]?
- private let matchFilter: Document?
- private let openEvent: (() -> Void)?
- init(collection: MongoCollection,
- queue: DispatchQueue,
- filterIds: [ObjectId]? = nil,
- matchFilter: Document? = nil,
- onOpen: (() -> Void)? = nil) {
- self.collection = collection
- self.queue = queue
- self.filterIds = filterIds
- self.matchFilter = matchFilter
- self.openEvent = onOpen
- }
- /// Triggers an event when the watch change stream is opened.
- ///
- /// Use this function when you require a change stream to be open before you perform any work.
- /// This should be called directly after invoking the publisher.
- ///
- /// - Parameter event: Callback which will be invoked once the change stream is open.
- /// - Returns: A publisher that emits a change event each time the remote MongoDB collection changes.
- public func onOpen(_ event: @escaping (() -> Void)) -> Self {
- Self(collection: collection,
- queue: queue,
- filterIds: filterIds,
- matchFilter: matchFilter,
- onOpen: event)
- }
- /// :nodoc:
- public func receive<S: Subscriber>(subscriber: S) where Failure == S.Failure, Output == S.Input {
- let subscription = WatchSubscription(collection: collection,
- subscriber: subscriber,
- queue: queue,
- filterIds: filterIds,
- matchFilter: matchFilter,
- onOpen: openEvent)
- subscriber.receive(subscription: subscription)
- }
- /// Specifies the scheduler on which to perform subscribe, cancel, and request operations.
- ///
- /// - parameter scheduler: The dispatch queue to perform the subscription on.
- /// - returns: A publisher which subscribes on the given scheduler.
- public func subscribe<S: Scheduler>(on scheduler: S) -> WatchPublisher {
- guard let queue = scheduler as? DispatchQueue else {
- fatalError("Cannot subscribe on scheduler \(scheduler): only dispatch queues are currently implemented.")
- }
- return Self(collection: collection,
- queue: queue,
- filterIds: filterIds,
- matchFilter: matchFilter,
- onOpen: openEvent)
- }
- }
- }
- @available(OSX 10.15, watchOS 6.0, iOS 13.0, iOSApplicationExtension 13.0, OSXApplicationExtension 10.15, tvOS 13.0, *)
- extension MongoCollection {
- /// Creates a publisher that emits a AnyBSON change event each time the MongoDB collection changes.
- ///
- /// - returns: A publisher that emits the AnyBSON change event each time the collection changes.
- public func watch() -> Publishers.WatchPublisher {
- return Publishers.WatchPublisher(collection: self, queue: .main)
- }
- /// Creates a publisher that emits a AnyBSON change event each time the MongoDB collection changes.
- ///
- /// - Parameter filterIds: The list of _ids in the collection to watch.
- /// - Returns: A publisher that emits the AnyBSON change event each time the collection changes.
- public func watch(filterIds: [ObjectId]) -> Publishers.WatchPublisher {
- return Publishers.WatchPublisher(collection: self, queue: .main, filterIds: filterIds)
- }
- /// Creates a publisher that emits a AnyBSON change event each time the MongoDB collection changes.
- ///
- /// - Parameter matchFilter: The $match filter to apply to incoming change events.
- /// - Returns: A publisher that emits the AnyBSON change event each time the collection changes.
- public func watch(matchFilter: Document) -> Publishers.WatchPublisher {
- return Publishers.WatchPublisher(collection: self, queue: .main, matchFilter: matchFilter)
- }
- }
- @available(OSX 10.15, watchOS 6.0, iOS 13.0, iOSApplicationExtension 13.0, OSXApplicationExtension 10.15, tvOS 13.0, macCatalyst 13.0, macCatalystApplicationExtension 13.0, *)
- public extension MongoCollection {
- /// Encodes the provided value to BSON and inserts it. If the value is missing an identifier, one will be
- /// generated for it.
- /// @param document: A `Document` value to insert.
- /// @returns A publisher that eventually return the object id of the inserted document or `Error`.
- func insertOne(_ document: Document) -> Future<AnyBSON, Error> {
- return Future { self.insertOne(document, $0) }
- }
- /// Encodes the provided values to BSON and inserts them. If any values are missing identifiers,
- /// they will be generated.
- /// @param documents: The `Document` values in a bson array to insert.
- /// @returns A publisher that eventually return the object ids of inserted documents or `Error`.
- func insertMany(_ documents: [Document]) -> Future<[AnyBSON], Error> {
- return Future { self.insertMany(documents, $0) }
- }
- /// Finds the documents in this collection which match the provided filter.
- /// @param filter: A `Document` as bson that should match the query.
- /// @param options: `FindOptions` to use when executing the command.
- /// @returns A publisher that eventually return `[ObjectId]` of documents or `Error`.
- func find(filter: Document, options: FindOptions) -> Future<[Document], Error> {
- return Future { self.find(filter: filter, options: options, $0) }
- }
- /// Finds the documents in this collection which match the provided filter.
- /// @param filter: A `Document` as bson that should match the query.
- /// @returns A publisher that eventually return `[ObjectId]` of documents or `Error`.
- func find(filter: Document) -> Future<[Document], Error> {
- return Future { self.find(filter: filter, $0) }
- }
- /// Returns one document from a collection or view which matches the
- /// provided filter. If multiple documents satisfy the query, this method
- /// returns the first document according to the query's sort order or natural
- /// order.
- /// @param filter: A `Document` as bson that should match the query.
- /// @param options: `FindOptions` to use when executing the command.
- /// @returns A publisher that eventually return `Document` or `Error`.
- func findOneDocument(filter: Document, options: FindOptions) -> Future<Document?, Error> {
- return Future { self.findOneDocument(filter: filter, $0) }
- }
- /// Returns one document from a collection or view which matches the
- /// provided filter. If multiple documents satisfy the query, this method
- /// returns the first document according to the query's sort order or natural
- /// order.
- /// @param filter: A `Document` as bson that should match the query.
- /// @returns A publisher that eventually return `Document` or `nil` if document wasn't found or `Error`.
- func findOneDocument(filter: Document) -> Future<Document?, Error> {
- return Future { self.findOneDocument(filter: filter, $0) }
- }
- /// Runs an aggregation framework pipeline against this collection.
- /// @param pipeline: A bson array made up of `Documents` containing the pipeline of aggregation operations to perform.
- /// @returns A publisher that eventually return `Document` or `Error`.
- func aggregate(pipeline: [Document]) -> Future<[Document], Error> {
- return Future { self.aggregate(pipeline: pipeline, $0) }
- }
- /// Counts the number of documents in this collection matching the provided filter.
- /// @param filter: A `Document` as bson that should match the query.
- /// @param limit: The max amount of documents to count
- /// @returns A publisher that eventually return `Int` count of documents or `Error`.
- func count(filter: Document, limit: Int) -> Future<Int, Error> {
- return Future { self.count(filter: filter, limit: limit, $0) }
- }
- /// Counts the number of documents in this collection matching the provided filter.
- /// @param filter: A `Document` as bson that should match the query.
- /// @returns A publisher that eventually return `Int` count of documents or `Error`.
- func count(filter: Document) -> Future<Int, Error> {
- return Future { self.count(filter: filter, $0) }
- }
- /// Deletes a single matching document from the collection.
- /// @param filter: A `Document` as bson that should match the query.
- /// @returns A publisher that eventually return `Int` count of deleted documents or `Error`.
- func deleteOneDocument(filter: Document) -> Future<Int, Error> {
- return Future { self.deleteOneDocument(filter: filter, $0) }
- }
- /// Deletes multiple documents
- /// @param filter: Document representing the match criteria
- /// @returns A publisher that eventually return `Int` count of deleted documents or `Error`.
- func deleteManyDocuments(filter: Document) -> Future<Int, Error> {
- return Future { self.deleteManyDocuments(filter: filter, $0) }
- }
- /// Updates a single document matching the provided filter in this collection.
- /// @param filter: A bson `Document` representing the match criteria.
- /// @param update: A bson `Document` representing the update to be applied to a matching document.
- /// @param upsert: When true, creates a new document if no document matches the query.
- /// @returns A publisher that eventually return `UpdateResult` or `Error`.
- func updateOneDocument(filter: Document, update: Document, upsert: Bool) -> Future<UpdateResult, Error> {
- return Future { self.updateOneDocument(filter: filter, update: update, upsert: upsert, $0) }
- }
- /// Updates a single document matching the provided filter in this collection.
- /// @param filter: A bson `Document` representing the match criteria.
- /// @param update: A bson `Document` representing the update to be applied to a matching document.
- /// @returns A publisher that eventually return `UpdateResult` or `Error`.
- func updateOneDocument(filter: Document, update: Document) -> Future<UpdateResult, Error> {
- return Future { self.updateOneDocument(filter: filter, update: update, $0) }
- }
- /// Updates multiple documents matching the provided filter in this collection.
- /// @param filter: A bson `Document` representing the match criteria.
- /// @param update: A bson `Document` representing the update to be applied to a matching document.
- /// @param upsert: When true, creates a new document if no document matches the query.
- /// @returns A publisher that eventually return `UpdateResult` or `Error`.
- func updateManyDocuments(filter: Document, update: Document, upsert: Bool) -> Future<UpdateResult, Error> {
- return Future { self.updateManyDocuments(filter: filter, update: update, upsert: upsert, $0) }
- }
- /// Updates multiple documents matching the provided filter in this collection.
- /// @param filter: A bson `Document` representing the match criteria.
- /// @param update: A bson `Document` representing the update to be applied to a matching document.
- /// @returns A publisher that eventually return `UpdateResult` or `Error`.
- func updateManyDocuments(filter: Document, update: Document) -> Future<UpdateResult, Error> {
- return Future { self.updateManyDocuments(filter: filter, update: update, $0) }
- }
- /// Updates a single document in a collection based on a query filter and
- /// returns the document in either its pre-update or post-update form. Unlike
- /// `updateOneDocument`, this action allows you to atomically find, update, and
- /// return a document with the same command. This avoids the risk of other
- /// update operations changing the document between separate find and update
- /// operations.
- /// @param filter: A bson `Document` representing the match criteria.
- /// @param update: A bson `Document` representing the update to be applied to a matching document.
- /// @param options: `RemoteFindOneAndModifyOptions` to use when executing the command.
- /// @returns A publisher that eventually return `Document` or `nil` if document wasn't found or `Error`.
- func findOneAndUpdate(filter: Document, update: Document, options: FindOneAndModifyOptions) -> Future<Document?, Error> {
- return Future { self.findOneAndUpdate(filter: filter, update: update, options: options, $0) }
- }
- /// Updates a single document in a collection based on a query filter and
- /// returns the document in either its pre-update or post-update form. Unlike
- /// `updateOneDocument`, this action allows you to atomically find, update, and
- /// return a document with the same command. This avoids the risk of other
- /// update operations changing the document between separate find and update
- /// operations.
- /// @param filter: A bson `Document` representing the match criteria.
- /// @param update: A bson `Document` representing the update to be applied to a matching document.
- /// @returns A publisher that eventually return `Document` or `nil` if document wasn't found or `Error`.
- func findOneAndUpdate(filter: Document, update: Document) -> Future<Document?, Error> {
- return Future { self.findOneAndUpdate(filter: filter, update: update, $0) }
- }
- /// Overwrites a single document in a collection based on a query filter and
- /// returns the document in either its pre-replacement or post-replacement
- /// form. Unlike `updateOneDocument`, this action allows you to atomically find,
- /// replace, and return a document with the same command. This avoids the
- /// risk of other update operations changing the document between separate
- /// find and update operations.
- /// @param filter: A `Document` that should match the query.
- /// @param replacement: A `Document` describing the replacement.
- /// @param options: `FindOneAndModifyOptions` to use when executing the command.
- /// @returns A publisher that eventually return `Document` or `nil` if document wasn't found or `Error`.
- func findOneAndReplace(filter: Document, replacement: Document, options: FindOneAndModifyOptions) -> Future<Document?, Error> {
- return Future { self.findOneAndReplace(filter: filter, replacement: replacement, options: options, $0) }
- }
- /// Overwrites a single document in a collection based on a query filter and
- /// returns the document in either its pre-replacement or post-replacement
- /// form. Unlike `updateOneDocument`, this action allows you to atomically find,
- /// replace, and return a document with the same command. This avoids the
- /// risk of other update operations changing the document between separate
- /// find and update operations.
- /// @param filter: A `Document` that should match the query.
- /// @param replacement: A `Document` describing the replacement.
- /// @returns A publisher that eventually return `Document` or `nil` if document wasn't found or `Error`.
- func findOneAndReplace(filter: Document, replacement: Document) -> Future<Document?, Error> {
- return Future { self.findOneAndReplace(filter: filter, replacement: replacement, $0) }
- }
- /// Removes a single document from a collection based on a query filter and
- /// returns a document with the same form as the document immediately before
- /// it was deleted. Unlike `deleteOneDocument`, this action allows you to atomically
- /// find and delete a document with the same command. This avoids the risk of
- /// other update operations changing the document between separate find and
- /// delete operations.
- /// @param filter: A `Document` that should match the query.
- /// @param options: `FindOneAndModifyOptions` to use when executing the command.
- /// @returns A publisher that eventually return `Document` or `nil` if document wasn't found or `Error`.
- func findOneAndDelete(filter: Document, options: FindOneAndModifyOptions) -> Future<Document?, Error> {
- return Future { self.findOneAndDelete(filter: filter, options: options, $0) }
- }
- /// Removes a single document from a collection based on a query filter and
- /// returns a document with the same form as the document immediately before
- /// it was deleted. Unlike `deleteOneDocument`, this action allows you to atomically
- /// find and delete a document with the same command. This avoids the risk of
- /// other update operations changing the document between separate find and
- /// delete operations.
- /// @param filter: A `Document` that should match the query.
- /// @returns A publisher that eventually return `Document` or `nil` if document wasn't found or `Error`.
- func findOneAndDelete(filter: Document) -> Future<Document?, Error> {
- return Future { self.findOneAndDelete(filter: filter, $0) }
- }
- }
- #endif // canImport(Combine)
|