Skip to content

Commit 650ed0a

Browse files
committed
Sketch remainder of CRUD API
1 parent 28db28b commit 650ed0a

File tree

7 files changed

+367
-7
lines changed

7 files changed

+367
-7
lines changed

Sources/MongoSwift/BSON/BsonValue.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ public struct Binary: BsonValue, Equatable, Codable {
200200
/// Throws an error if the base64 `String` is invalid.
201201
public init(base64: String, subtype: UInt8) throws {
202202
guard let dataObj = Data(base64Encoded: base64) else {
203-
throw MongoError.invalidValue(message: "failed to create Data object from invalid base64 string \(base64)")
203+
throw MongoError.invalidArgument(message: "failed to create Data object from invalid base64 string \(base64)")
204204
}
205205
self.init(data: dataObj, subtype: subtype)
206206
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import libmongoc
2+
3+
/// An extension of `MongoCollection` encapsulating bulk write operations.
4+
extension MongoCollection {
5+
/**
6+
* Execute multiple write operations.
7+
*
8+
* - Parameters:
9+
* - requests: a `[WriteModel]` containing the writes to perform
10+
* - options: optional `BulkWriteOptions` to use while executing the operation
11+
*
12+
* - Returns: a `BulkWriteResult`, or `nil` if the write concern is unacknowledged.
13+
*
14+
* - Throws:
15+
* - `MongoError.invalidArgument` if `requests` is empty
16+
* - `MongoError.bulkWriteError` if any error occurs while performing the writes
17+
*/
18+
@discardableResult
19+
public func bulkWrite(_ requests: [WriteModel], options: BulkWriteOptions? = nil) throws -> BulkWriteResult? {
20+
throw MongoError.commandError(message: "Unimplemented command")
21+
}
22+
}
23+
24+
/// Options to use when performing a bulk write operation on a `MongoCollection`.
25+
public struct BulkWriteOptions: Encodable {
26+
/// If `true` (the default), when an insert fails, return without performing the remaining writes.
27+
/// If `false`, when a write fails, continue with the remaining writes, if any.
28+
public var ordered: Bool = true
29+
30+
/// If `true`, allows the write to opt-out of document level validation.
31+
public let bypassDocumentValidation: Bool?
32+
}
33+
34+
/// A protocol indicating write types that can be batched together using `MongoCollection.bulkWrite`.
35+
public protocol WriteModel: Encodable {}
36+
37+
/// A model for an `insertOne` command.
38+
public struct InsertOneModel: WriteModel {
39+
/// The `Document` to insert.
40+
public let document: Document
41+
}
42+
43+
/// A model for a `deleteOne` command.
44+
public struct DeleteOneModel: WriteModel {
45+
/// A `Document` representing the match criteria.
46+
public let filter: Document
47+
48+
/// Specifies a collation to use.
49+
public let collation: Document?
50+
}
51+
52+
/// A model for a `deleteMany` command.
53+
public struct DeleteManyModel: WriteModel {
54+
/// A `Document` representing the match criteria.
55+
public let filter: Document
56+
57+
/// Specifies a collation to use.
58+
public let collation: Document?
59+
}
60+
61+
/// A model for a `replaceOne` command.
62+
public struct ReplaceOneModel: WriteModel {
63+
/// A `Document` representing the match criteria.
64+
public let filter: Document
65+
66+
/// The `Document` with which to replace the matched document.
67+
public let replacement: Document
68+
69+
/// Specifies a collation to use.
70+
public let collation: Document?
71+
72+
/// When `true`, creates a new document if no document matches the query.
73+
public let upsert: Bool?
74+
}
75+
76+
/// A model for an `updateOne` command.
77+
public struct UpdateOneModel: WriteModel {
78+
/// A `Document` representing the match criteria.
79+
public let filter: Document
80+
81+
/// A `Document` containing update operators.
82+
public let update: Document
83+
84+
/// A set of filters specifying to which array elements an update should apply.
85+
public let arrayFilters: [Document]?
86+
87+
/// Specifies a collation to use.
88+
public let collation: Document?
89+
90+
/// When `true`, creates a new document if no document matches the query.
91+
public let upsert: Bool?
92+
}
93+
94+
/// A model for an `updateMany` command.
95+
public struct UpdateManyModel: WriteModel {
96+
/// A `Document` representing the match criteria.
97+
public let filter: Document
98+
99+
/// A `Document` containing update operators.
100+
public let update: Document
101+
102+
/// A set of filters specifying to which array elements an update should apply.
103+
public let arrayFilters: [Document]?
104+
105+
/// Specifies a collation to use.
106+
public let collation: Document?
107+
108+
/// When `true`, creates a new document if no document matches the query.
109+
public let upsert: Bool?
110+
}
111+
112+
/// The result of a bulk write operation on a `MongoCollection`.
113+
public struct BulkWriteResult: Decodable {
114+
/// Number of documents inserted.
115+
public let insertedCount: Int
116+
117+
/// Map of the index of the operation to the id of the inserted document.
118+
public let insertedIds: [Int: AnyBsonValue]
119+
120+
/// Number of documents matched for update.
121+
public let matchedCount: Int
122+
123+
/// Number of documents modified.
124+
public let modifiedCount: Int
125+
126+
/// Number of documents deleted.
127+
public let deletedCount: Int
128+
129+
/// Number of documents upserted.
130+
public let upsertedCount: Int
131+
132+
/// Map of the index of the operation to the id of the upserted document.
133+
public let upsertedIds: [Int: AnyBsonValue]
134+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import libmongoc
2+
3+
/// An extension of `MongoCollection` encapsulating find and modify operations.
4+
extension MongoCollection {
5+
/**
6+
* Finds a single document and deletes it, returning the original.
7+
*
8+
* - Parameters:
9+
* - filter: `Document` representing the match criteria
10+
* - options: Optional `FindOneAndDeleteOptions` to use when executing the command
11+
*
12+
* - Returns: The deleted document, represented as a `CollectionType`, or `nil` if no document was deleted.
13+
* - Throws:
14+
* - `MongoError.commandError` if there are any errors executing the command.
15+
* - A `DecodingError` if the deleted document cannot be decoded to a `CollectionType` value
16+
*/
17+
@discardableResult
18+
public func findOneAndDelete(_ filter: Document, options: FindOneAndDeleteOptions? = nil) throws -> CollectionType? {
19+
throw MongoError.commandError(message: "Unimplemented command")
20+
}
21+
22+
/**
23+
* Finds a single document and replaces it, returning either the original or the replaced document.
24+
*
25+
* - Parameters:
26+
* - filter: `Document` representing the match criteria
27+
* - replacement: a `CollectionType` to replace the found document
28+
* - options: Optional `FindOneAndReplaceOptions` to use when executing the command
29+
*
30+
* - Returns: A `CollectionType`, representing either the original document or its replacement,
31+
* depending on selected options, or `nil` if there was no match.
32+
* - Throws:
33+
* - `MongoError.commandError` if there are any errors executing the command.
34+
* - An `EncodingError` if `replacement` cannot be encoded to a `Document`
35+
* - A `DecodingError` if the replaced document cannot be decoded to a `CollectionType` value
36+
*/
37+
@discardableResult
38+
public func findOneAndReplace(filter: Document, replacement: CollectionType,
39+
options: FindOneAndDeleteOptions? = nil) throws -> CollectionType? {
40+
throw MongoError.commandError(message: "Unimplemented command")
41+
}
42+
43+
/**
44+
* Finds a single document and updates it, returning either the original or the updated document.
45+
*
46+
* - Parameters:
47+
* - filter: `Document` representing the match criteria
48+
* - update: a `Document` containing updates to apply
49+
* - options: Optional `FindOneAndUpdateOptions` to use when executing the command
50+
*
51+
* - Returns: A `CollectionType` representing either the original or updated document,
52+
* depending on selected options, or `nil` if there was no match.
53+
* - Throws:
54+
* - `MongoError.commandError` if there are any errors executing the command.
55+
* - A `DecodingError` if the updated document cannot be decoded to a `CollectionType` value
56+
*/
57+
@discardableResult
58+
public func findOneAndUpdate(filter: Document, update: Document,
59+
options: FindOneAndUpdateOptions? = nil) throws -> CollectionType? {
60+
throw MongoError.commandError(message: "Unimplemented command")
61+
}
62+
}
63+
64+
/// Indicates which document to return in a find and modify operation.
65+
public enum ReturnDocument: Encodable {
66+
/// Indicates to return the document before the update, replacement, or insert occured.
67+
case before
68+
69+
/// Indicates to return the document after the update, replacement, or insert occured.
70+
case after
71+
72+
public func encode(to encoder: Encoder) throws {
73+
// fill in later on
74+
}
75+
}
76+
77+
/// Options to use when executing a `findOneAndDelete` command on a `MongoCollection`.
78+
public struct FindOneAndDeleteOptions: Encodable {
79+
/// Specifies a collation to use.
80+
public let collation: Document?
81+
82+
/// The maximum amount of time to allow the query to run.
83+
public let maxTimeMS: Int64?
84+
85+
/// Limits the fields to return for the matching document.
86+
public let projection: Document?
87+
88+
/// Determines which document the operation modifies if the query selects multiple documents.
89+
public let sort: Document?
90+
91+
/// An optional `WriteConcern` to use for the command.
92+
public let writeConcern: WriteConcern?
93+
}
94+
95+
/// Options to use when executing a `findOneAndReplace` command on a `MongoCollection`.
96+
public struct FindOneAndReplaceOptions: Encodable {
97+
/// If `true`, allows the write to opt-out of document level validation.
98+
public let bypassDocumentValidation: Bool?
99+
100+
/// Specifies a collation to use.
101+
public let collation: Document?
102+
103+
/// The maximum amount of time to allow the query to run.
104+
public let maxTimeMS: Int64?
105+
106+
/// Limits the fields to return for the matching document.
107+
public let projection: Document?
108+
109+
/// When `ReturnDocument.After`, returns the replaced or inserted document rather than the original.
110+
public let returnDocument: ReturnDocument?
111+
112+
/// Determines which document the operation modifies if the query selects multiple documents.
113+
public let sort: Document?
114+
115+
/// When `true`, creates a new document if no document matches the query.
116+
public let upsert: Bool?
117+
118+
/// An optional `WriteConcern` to use for the command.
119+
public let writeConcern: WriteConcern?
120+
}
121+
122+
/// Options to use when executing a `findOneAndUpdate` command on a `MongoCollection`.
123+
public struct FindOneAndUpdateOptions: Encodable {
124+
/// A set of filters specifying to which array elements an update should apply.
125+
public let arrayFilters: [Document]?
126+
127+
/// If `true`, allows the write to opt-out of document level validation.
128+
public let bypassDocumentValidation: Bool?
129+
130+
/// Specifies a collation to use.
131+
public let collation: Document?
132+
133+
/// The maximum amount of time to allow the query to run.
134+
public let maxTimeMS: Int64?
135+
136+
/// Limits the fields to return for the matching document.
137+
public let projection: Document?
138+
139+
/// When`ReturnDocument.After`, returns the updated or inserted document rather than the original.
140+
public let returnDocument: ReturnDocument?
141+
142+
/// Determines which document the operation modifies if the query selects multiple documents.
143+
public let sort: Document?
144+
145+
/// When `true`, creates a new document if no document matches the query.
146+
public let upsert: Bool?
147+
148+
/// An optional `WriteConcern` to use for the command.
149+
public let writeConcern: WriteConcern?
150+
}

Sources/MongoSwift/MongoCollection+Read.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ extension MongoCollection {
4646
return MongoCursor(fromCursor: cursor, withClient: client)
4747
}
4848

49+
// TODO SWIFT-133: mark this method deprecated https://jira.mongodb.org/browse/SWIFT-133
4950
/**
5051
* Counts the number of documents in this collection matching the provided filter.
5152
*
@@ -69,6 +70,33 @@ extension MongoCollection {
6970
return Int(count)
7071
}
7172

73+
/**
74+
* Counts the number of documents in this collection matching the provided filter.
75+
*
76+
* - Parameters:
77+
* - filter: a `Document`, the filter that documents must match in order to be counted
78+
* - options: Optional `CountDocumentsOptions` to use when executing the command
79+
*
80+
* - Returns: The count of the documents that matched the filter
81+
*/
82+
public func countDocuments(_ filter: Document = [:], options: CountDocumentsOptions? = nil) throws -> Int {
83+
// TODO SWIFT-133: implement this https://jira.mongodb.org/browse/SWIFT-133
84+
throw MongoError.commandError(message: "Unimplemented command")
85+
}
86+
87+
/**
88+
* Gets an estimate of the count of documents in this collection using collection metadata.
89+
*
90+
* - Parameters:
91+
* - options: Optional `EstimatedDocumentCountOptions` to use when executing the command
92+
*
93+
* - Returns: an estimate of the count of documents in this collection
94+
*/
95+
public func estimatedDocumentCount(options: EstimatedDocumentCountOptions? = nil) throws -> Int {
96+
// TODO SWIFT-133: implement this https://jira.mongodb.org/browse/SWIFT-133
97+
throw MongoError.commandError(message: "Unimplemented command")
98+
}
99+
72100
/**
73101
* Finds the distinct values for a specified field across the collection.
74102
*
@@ -237,6 +265,15 @@ public struct CountOptions: Encodable {
237265
}
238266
}
239267

268+
/// The `countDocuments` command takes the same options as the deprecated `count`.
269+
public typealias CountDocumentsOptions = CountOptions
270+
271+
/// Options to use when executing an `estimatedDocumentCount` command on a `MongoCollection`.
272+
public struct EstimatedDocumentCountOptions {
273+
/// The maximum amount of time to allow the query to run.
274+
public let maxTimeMS: Int64?
275+
}
276+
240277
/// Options to use when executing a `distinct` command on a `MongoCollection`.
241278
public struct DistinctOptions: Encodable {
242279
/// Specifies a collation.

Sources/MongoSwift/MongoError.swift

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import libmongoc
55
public enum MongoError {
66
/// Thrown when an invalid connection string is provided when initializing a `MongoClient`.
77
case invalidUri(message: String)
8-
/// Thrown when a user-provided value is invalid.
9-
case invalidValue(message: String)
108
/// Thrown when a `MongoClient` is invalid.
119
case invalidClient()
1210
/// Thrown when the server sends an invalid response.
@@ -28,7 +26,13 @@ public enum MongoError {
2826
/// Thrown when there is an error involving a `ReadPreference`.
2927
case readPreferenceError(message: String)
3028
/// Thrown when there is an error involving a `WriteConcern`.
31-
case writeConcernError(message: String)
29+
case writeConcernError(code: Int32, message: String)
30+
/// Thrown when a user-provided argument is invalid.
31+
case invalidArgument(message: String)
32+
/// Thrown when there is an error executing a write command.
33+
case writeError(code: Int32, message: String)
34+
/// Thrown when there is an error executing a bulk write command.
35+
indirect case bulkWriteError(code: Int32, message: String, result: BulkWriteResult?, writeErrors: [MongoError]?, writeConcernError: MongoError?)
3236
}
3337

3438
/// An extension of `MongoError` to support printing out descriptive error messages.
@@ -39,9 +43,11 @@ extension MongoError: LocalizedError {
3943
let .invalidCollection(message), let .commandError(message),
4044
let .bsonParseError(_, _, message), let .bsonEncodeError(message),
4145
let .typeError(message), let .readConcernError(message),
42-
let .readPreferenceError(message), let .writeConcernError(message),
43-
let .invalidValue(message):
46+
let .readPreferenceError(message), let .invalidArgument(message):
4447
return message
48+
case let .writeConcernError(code, message), let .writeError(code, message),
49+
let .bulkWriteError(code, message, _, _, _):
50+
return "\(message) (error code \(code))"
4551
default:
4652
return nil
4753
}

Sources/MongoSwift/WriteConcern.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public class WriteConcern: Codable {
128128
let journalStr = String(describing: journal)
129129
let wStr = String(describing: w)
130130
let timeoutStr = String(describing: wtimeoutMS)
131-
throw MongoError.writeConcernError(message:
131+
throw MongoError.invalidArgument(message:
132132
"Invalid combination of WriteConcern options: journal=\(journalStr), w=\(wStr), wtimeoutMS=\(timeoutStr)")
133133
}
134134
}

0 commit comments

Comments
 (0)