Skip to content

Commit 16e73ca

Browse files
authored
SWIFT-143: ReadPreference option for core classes and operations (#86)
1 parent 32b8ea4 commit 16e73ca

File tree

4 files changed

+105
-15
lines changed

4 files changed

+105
-15
lines changed

Sources/MongoSwift/MongoClient.swift

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,21 @@ public struct ClientOptions: Encodable {
1414
/// the server's default read concern will be used.
1515
public let readConcern: ReadConcern?
1616

17+
/// Specifies a ReadPreference to use for the client.
18+
public let readPreference: ReadPreference?
19+
1720
/// Specifies a WriteConcern to use for the client. If one is not specified,
1821
/// the server's default write concern will be used.
1922
public let writeConcern: WriteConcern?
2023

2124
/// Convenience initializer allowing any/all to be omitted or optional
22-
public init(eventMonitoring: Bool = false, readConcern: ReadConcern? = nil, retryWrites: Bool? = nil,
25+
public init(eventMonitoring: Bool = false, readConcern: ReadConcern? = nil,
26+
readPreference: ReadPreference? = nil, retryWrites: Bool? = nil,
2327
writeConcern: WriteConcern? = nil) {
2428
self.retryWrites = retryWrites
2529
self.eventMonitoring = eventMonitoring
2630
self.readConcern = readConcern
31+
self.readPreference = readPreference
2732
self.writeConcern = writeConcern
2833
}
2934

@@ -57,13 +62,19 @@ public struct DatabaseOptions {
5762
/// the database will inherit the client's read concern.
5863
public let readConcern: ReadConcern?
5964

65+
/// A read preference to set on the retrieved database. If one is not
66+
/// specified, the database will inherit the client's read preference.
67+
public let readPreference: ReadPreference?
68+
6069
/// A write concern to set on the retrieved database. If one is not specified,
6170
/// the database will inherit the client's write concern.
6271
public let writeConcern: WriteConcern?
6372

6473
/// Convenience initializer allowing any/all arguments to be omitted or optional
65-
public init(readConcern: ReadConcern? = nil, writeConcern: WriteConcern? = nil) {
74+
public init(readConcern: ReadConcern? = nil, readPreference: ReadPreference? = nil,
75+
writeConcern: WriteConcern? = nil) {
6676
self.readConcern = readConcern
77+
self.readPreference = readPreference
6778
self.writeConcern = writeConcern
6879
}
6980
}
@@ -87,6 +98,11 @@ public class MongoClient {
8798
return rcObj
8899
}
89100

101+
/// The `ReadPreference` set on this client
102+
public var readPreference: ReadPreference? {
103+
return ReadPreference(from: mongoc_client_get_read_prefs(self._client))
104+
}
105+
90106
/// The write concern set on this client, or nil if one is not set.
91107
public var writeConcern: WriteConcern? {
92108
// per libmongoc docs, we don't need to handle freeing this ourselves
@@ -121,6 +137,11 @@ public class MongoClient {
121137
mongoc_client_set_read_concern(self._client, rc._readConcern)
122138
}
123139

140+
// if a readPreference is provided, set it on the client
141+
if let rp = options?.readPreference {
142+
mongoc_client_set_read_prefs(self._client, rp._readPreference)
143+
}
144+
124145
// if a writeConcern is provided, set it on the client
125146
if let wc = options?.writeConcern {
126147
mongoc_client_set_write_concern(self._client, wc._writeConcern)
@@ -209,6 +230,10 @@ public class MongoClient {
209230
mongoc_database_set_read_concern(db, rc._readConcern)
210231
}
211232

233+
if let rp = options?.readPreference {
234+
mongoc_database_set_read_prefs(db, rp._readPreference)
235+
}
236+
212237
if let wc = options?.writeConcern {
213238
mongoc_database_set_write_concern(db, wc._writeConcern)
214239
}

Sources/MongoSwift/MongoCollection+Read.swift

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ extension MongoCollection {
1313
*/
1414
public func find(_ filter: Document = [:], options: FindOptions? = nil) throws -> MongoCursor<CollectionType> {
1515
let opts = try BsonEncoder().encode(options)
16-
guard let cursor = mongoc_collection_find_with_opts(self._collection, filter.data, opts?.data, nil) else {
16+
let rp = options?.readPreference?._readPreference
17+
guard let cursor = mongoc_collection_find_with_opts(self._collection, filter.data, opts?.data, rp) else {
1718
throw MongoError.invalidResponse()
1819
}
1920
guard let client = self._client else {
@@ -33,9 +34,10 @@ extension MongoCollection {
3334
*/
3435
public func aggregate(_ pipeline: [Document], options: AggregateOptions? = nil) throws -> MongoCursor<Document> {
3536
let opts = try BsonEncoder().encode(options)
37+
let rp = options?.readPreference?._readPreference
3638
let pipeline: Document = ["pipeline": pipeline]
3739
guard let cursor = mongoc_collection_aggregate(
38-
self._collection, MONGOC_QUERY_NONE, pipeline.data, opts?.data, nil) else {
40+
self._collection, MONGOC_QUERY_NONE, pipeline.data, opts?.data, rp) else {
3941
throw MongoError.invalidResponse()
4042
}
4143
guard let client = self._client else {
@@ -55,11 +57,12 @@ extension MongoCollection {
5557
*/
5658
public func count(_ filter: Document = [:], options: CountOptions? = nil) throws -> Int {
5759
let opts = try BsonEncoder().encode(options)
60+
let rp = options?.readPreference?._readPreference
5861
var error = bson_error_t()
5962
// because we already encode skip and limit in the options,
6063
// pass in 0s so we don't get duplicate parameter errors.
6164
let count = mongoc_collection_count_with_opts(
62-
self._collection, MONGOC_QUERY_NONE, filter.data, 0, 0, opts?.data, nil, &error)
65+
self._collection, MONGOC_QUERY_NONE, filter.data, 0, 0, opts?.data, rp, &error)
6366

6467
if count == -1 { throw MongoError.commandError(message: toErrorString(error)) }
6568

@@ -90,10 +93,11 @@ extension MongoCollection {
9093
]
9194

9295
let opts = try BsonEncoder().encode(options)
96+
let rp = options?.readPreference?._readPreference
9397
let reply = Document()
9498
var error = bson_error_t()
9599
if !mongoc_collection_read_command_with_opts(
96-
self._collection, command.data, nil, opts?.data, reply.data, &error) {
100+
self._collection, command.data, rp, opts?.data, reply.data, &error) {
97101
throw MongoError.commandError(message: toErrorString(error))
98102
}
99103

@@ -164,13 +168,17 @@ public struct AggregateOptions: Encodable {
164168
/// A `ReadConcern` to use in read stages of this operation.
165169
public let readConcern: ReadConcern?
166170

171+
/// A ReadPreference to use for this operation.
172+
public let readPreference: ReadPreference?
173+
167174
/// A `WriteConcern` to use in `$out` stages of this operation.
168175
public let writeConcern: WriteConcern?
169176

170177
/// Convenience initializer allowing any/all parameters to be optional
171178
public init(allowDiskUse: Bool? = nil, batchSize: Int32? = nil, bypassDocumentValidation: Bool? = nil,
172179
collation: Document? = nil, comment: String? = nil, hint: Hint? = nil, maxTimeMS: Int64? = nil,
173-
readConcern: ReadConcern? = nil, writeConcern: WriteConcern? = nil) {
180+
readConcern: ReadConcern? = nil, readPreference: ReadPreference? = nil,
181+
writeConcern: WriteConcern? = nil) {
174182
self.allowDiskUse = allowDiskUse
175183
self.batchSize = batchSize
176184
self.bypassDocumentValidation = bypassDocumentValidation
@@ -179,8 +187,14 @@ public struct AggregateOptions: Encodable {
179187
self.hint = hint
180188
self.maxTimeMS = maxTimeMS
181189
self.readConcern = readConcern
190+
self.readPreference = readPreference
182191
self.writeConcern = writeConcern
183192
}
193+
194+
private enum CodingKeys: String, CodingKey {
195+
case allowDiskUse, batchSize, bypassDocumentValidation, collation, maxTimeMS, comment, hint, readConcern,
196+
writeConcern
197+
}
184198
}
185199

186200
/// Options to use when executing a `count` command on a `MongoCollection`.
@@ -203,16 +217,24 @@ public struct CountOptions: Encodable {
203217
/// A ReadConcern to use for this operation.
204218
public let readConcern: ReadConcern?
205219

220+
/// A ReadPreference to use for this operation.
221+
public let readPreference: ReadPreference?
222+
206223
/// Convenience initializer allowing any/all parameters to be optional
207224
public init(collation: Document? = nil, hint: Hint? = nil, limit: Int64? = nil, maxTimeMS: Int64? = nil,
208-
readConcern: ReadConcern? = nil, skip: Int64? = nil) {
225+
readConcern: ReadConcern? = nil, readPreference: ReadPreference? = nil, skip: Int64? = nil) {
209226
self.collation = collation
210227
self.hint = hint
211228
self.limit = limit
212229
self.maxTimeMS = maxTimeMS
213230
self.readConcern = readConcern
231+
self.readPreference = readPreference
214232
self.skip = skip
215233
}
234+
235+
private enum CodingKeys: String, CodingKey {
236+
case collation, hint, limit, maxTimeMS, readConcern, skip
237+
}
216238
}
217239

218240
/// Options to use when executing a `distinct` command on a `MongoCollection`.
@@ -226,11 +248,20 @@ public struct DistinctOptions: Encodable {
226248
/// A ReadConcern to use for this operation.
227249
public let readConcern: ReadConcern?
228250

251+
/// A ReadPreference to use for this operation.
252+
public let readPreference: ReadPreference?
253+
229254
/// Convenience initializer allowing any/all parameters to be optional
230-
public init(collation: Document? = nil, maxTimeMS: Int64? = nil, readConcern: ReadConcern? = nil) {
255+
public init(collation: Document? = nil, maxTimeMS: Int64? = nil, readConcern: ReadConcern? = nil,
256+
readPreference: ReadPreference? = nil) {
231257
self.collation = collation
232258
self.maxTimeMS = maxTimeMS
233259
self.readConcern = readConcern
260+
self.readPreference = readPreference
261+
}
262+
263+
private enum CodingKeys: String, CodingKey {
264+
case collation, maxTimeMS, readConcern
234265
}
235266
}
236267

@@ -333,13 +364,16 @@ public struct FindOptions: Encodable {
333364
/// A ReadConcern to use for this operation.
334365
public let readConcern: ReadConcern?
335366

367+
/// A ReadPreference to use for this operation.
368+
public let readPreference: ReadPreference?
369+
336370
/// Convenience initializer allowing any/all parameters to be optional
337371
public init(allowPartialResults: Bool? = nil, batchSize: Int32? = nil, collation: Document? = nil,
338372
comment: String? = nil, cursorType: CursorType? = nil, hint: Hint? = nil, limit: Int64? = nil,
339373
max: Document? = nil, maxAwaitTimeMS: Int64? = nil, maxScan: Int64? = nil, maxTimeMS: Int64? = nil,
340374
min: Document? = nil, noCursorTimeout: Bool? = nil, projection: Document? = nil,
341-
readConcern: ReadConcern? = nil, returnKey: Bool? = nil, showRecordId: Bool? = nil, skip: Int64? = nil,
342-
sort: Document? = nil) {
375+
readConcern: ReadConcern? = nil, readPreference: ReadPreference? = nil, returnKey: Bool? = nil,
376+
showRecordId: Bool? = nil, skip: Int64? = nil, sort: Document? = nil) {
343377
self.allowPartialResults = allowPartialResults
344378
self.batchSize = batchSize
345379
self.collation = collation
@@ -358,6 +392,7 @@ public struct FindOptions: Encodable {
358392
self.noCursorTimeout = noCursorTimeout
359393
self.projection = projection
360394
self.readConcern = readConcern
395+
self.readPreference = readPreference
361396
self.returnKey = returnKey
362397
self.showRecordId = showRecordId
363398
self.skip = skip

Sources/MongoSwift/MongoCollection.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ public class MongoCollection<T: Codable> {
2929
return rcObj
3030
}
3131

32+
/// The `ReadPreference` set on this collection
33+
public var readPreference: ReadPreference? {
34+
return ReadPreference(from: mongoc_collection_get_read_prefs(self._collection))
35+
}
36+
3237
/// The `WriteConcern` set on this collection, or nil if one is not set.
3338
public var writeConcern: WriteConcern? {
3439
// per libmongoc docs, we don't need to handle freeing this ourselves

Sources/MongoSwift/MongoDatabase.swift

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,25 @@ public struct RunCommandOptions: Encodable {
88
/// An optional `ReadConcern` to use for this operation
99
public let readConcern: ReadConcern?
1010

11+
/// An optional `ReadPreference` to use for this operation
12+
public let readPreference: ReadPreference?
13+
1114
/// An optional WriteConcern to use for this operation
1215
public let writeConcern: WriteConcern?
1316

1417
/// Convenience initializer allowing session to be omitted or optional
15-
public init(readConcern: ReadConcern? = nil, session: ClientSession? = nil,
16-
writeConcern: WriteConcern? = nil) {
18+
public init(readConcern: ReadConcern? = nil, readPreference: ReadPreference? = nil,
19+
session: ClientSession? = nil, writeConcern: WriteConcern? = nil) {
1720
self.readConcern = readConcern
21+
self.readPreference = readPreference
1822
self.session = session
1923
self.writeConcern = writeConcern
2024
}
25+
26+
private enum CodingKeys: String, CodingKey {
27+
// TODO: Encode ClientSession as "sessionId" (see: SWIFT-28)
28+
case readConcern, writeConcern
29+
}
2130
}
2231

2332
/// Options to use when executing a `listCollections` command on a `MongoDatabase`.
@@ -110,13 +119,19 @@ public struct CollectionOptions {
110119
/// the collection will inherit the database's read concern.
111120
public let readConcern: ReadConcern?
112121

122+
/// A read preference to set on the returned collection. If one is not
123+
/// specified, the collection will inherit the database's read preference.
124+
public let readPreference: ReadPreference?
125+
113126
/// A write concern to set on the returned collection. If one is not specified,
114127
/// the collection will inherit the database's write concern.
115128
public let writeConcern: WriteConcern?
116129

117130
/// Convenience initializer allowing any/all arguments to be omitted or optional
118-
public init(readConcern: ReadConcern? = nil, writeConcern: WriteConcern? = nil) {
131+
public init(readConcern: ReadConcern? = nil, readPreference: ReadPreference? = nil,
132+
writeConcern: WriteConcern? = nil) {
119133
self.readConcern = readConcern
134+
self.readPreference = readPreference
120135
self.writeConcern = writeConcern
121136
}
122137
}
@@ -140,6 +155,11 @@ public class MongoDatabase {
140155
return rcObj
141156
}
142157

158+
/// The `ReadPreference` set on this database
159+
public var readPreference: ReadPreference? {
160+
return ReadPreference(from: mongoc_collection_get_read_prefs(self._database))
161+
}
162+
143163
/// The `WriteConcern` set on this database, or `nil` if one is not set.
144164
public var writeConcern: WriteConcern? {
145165
// per libmongoc docs, we don't need to handle freeing this ourselves
@@ -205,6 +225,10 @@ public class MongoDatabase {
205225
mongoc_collection_set_read_concern(collection, rc._readConcern)
206226
}
207227

228+
if let rp = options?.readPreference {
229+
mongoc_collection_set_read_prefs(collection, rp._readPreference)
230+
}
231+
208232
if let wc = options?.writeConcern {
209233
mongoc_collection_set_write_concern(collection, wc._writeConcern)
210234
}
@@ -289,10 +313,11 @@ public class MongoDatabase {
289313
*/
290314
@discardableResult
291315
public func runCommand(_ command: Document, options: RunCommandOptions? = nil) throws -> Document {
316+
let rp = options?.readPreference?._readPreference
292317
let opts = try BsonEncoder().encode(options)
293318
let reply = Document()
294319
var error = bson_error_t()
295-
if !mongoc_database_command_with_opts(self._database, command.data, nil, opts?.data, reply.data, &error) {
320+
if !mongoc_database_command_with_opts(self._database, command.data, rp, opts?.data, reply.data, &error) {
296321
throw MongoError.commandError(message: toErrorString(error))
297322
}
298323
return reply

0 commit comments

Comments
 (0)