Skip to content

SWIFT-143: ReadPreference option for core classes and operations #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions Sources/MongoSwift/MongoClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,21 @@ public struct ClientOptions: Encodable {
/// the server's default read concern will be used.
public let readConcern: ReadConcern?

/// Specifies a ReadPreference to use for the client.
public let readPreference: ReadPreference?

/// Specifies a WriteConcern to use for the client. If one is not specified,
/// the server's default write concern will be used.
public let writeConcern: WriteConcern?

/// Convenience initializer allowing any/all to be omitted or optional
public init(eventMonitoring: Bool = false, readConcern: ReadConcern? = nil, retryWrites: Bool? = nil,
public init(eventMonitoring: Bool = false, readConcern: ReadConcern? = nil,
readPreference: ReadPreference? = nil, retryWrites: Bool? = nil,
writeConcern: WriteConcern? = nil) {
self.retryWrites = retryWrites
self.eventMonitoring = eventMonitoring
self.readConcern = readConcern
self.readPreference = readPreference
self.writeConcern = writeConcern
}

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

/// A read preference to set on the retrieved database. If one is not
/// specified, the database will inherit the client's read preference.
public let readPreference: ReadPreference?

/// A write concern to set on the retrieved database. If one is not specified,
/// the database will inherit the client's write concern.
public let writeConcern: WriteConcern?

/// Convenience initializer allowing any/all arguments to be omitted or optional
public init(readConcern: ReadConcern? = nil, writeConcern: WriteConcern? = nil) {
public init(readConcern: ReadConcern? = nil, readPreference: ReadPreference? = nil,
writeConcern: WriteConcern? = nil) {
self.readConcern = readConcern
self.readPreference = readPreference
self.writeConcern = writeConcern
}
}
Expand All @@ -87,6 +98,11 @@ public class MongoClient {
return rcObj
}

/// The `ReadPreference` set on this client
public var readPreference: ReadPreference? {
return ReadPreference(from: mongoc_client_get_read_prefs(self._client))
}

/// The write concern set on this client, or nil if one is not set.
public var writeConcern: WriteConcern? {
// per libmongoc docs, we don't need to handle freeing this ourselves
Expand Down Expand Up @@ -121,6 +137,11 @@ public class MongoClient {
mongoc_client_set_read_concern(self._client, rc._readConcern)
}

// if a readPreference is provided, set it on the client
if let rp = options?.readPreference {
mongoc_client_set_read_prefs(self._client, rp._readPreference)
}

// if a writeConcern is provided, set it on the client
if let wc = options?.writeConcern {
mongoc_client_set_write_concern(self._client, wc._writeConcern)
Expand Down Expand Up @@ -209,6 +230,10 @@ public class MongoClient {
mongoc_database_set_read_concern(db, rc._readConcern)
}

if let rp = options?.readPreference {
mongoc_database_set_read_prefs(db, rp._readPreference)
}

if let wc = options?.writeConcern {
mongoc_database_set_write_concern(db, wc._writeConcern)
}
Expand Down
53 changes: 44 additions & 9 deletions Sources/MongoSwift/MongoCollection+Read.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ extension MongoCollection {
*/
public func find(_ filter: Document = [:], options: FindOptions? = nil) throws -> MongoCursor<CollectionType> {
let opts = try BsonEncoder().encode(options)
guard let cursor = mongoc_collection_find_with_opts(self._collection, filter.data, opts?.data, nil) else {
let rp = options?.readPreference?._readPreference
guard let cursor = mongoc_collection_find_with_opts(self._collection, filter.data, opts?.data, rp) else {
throw MongoError.invalidResponse()
}
guard let client = self._client else {
Expand All @@ -33,9 +34,10 @@ extension MongoCollection {
*/
public func aggregate(_ pipeline: [Document], options: AggregateOptions? = nil) throws -> MongoCursor<Document> {
let opts = try BsonEncoder().encode(options)
let rp = options?.readPreference?._readPreference
let pipeline: Document = ["pipeline": pipeline]
guard let cursor = mongoc_collection_aggregate(
self._collection, MONGOC_QUERY_NONE, pipeline.data, opts?.data, nil) else {
self._collection, MONGOC_QUERY_NONE, pipeline.data, opts?.data, rp) else {
throw MongoError.invalidResponse()
}
guard let client = self._client else {
Expand All @@ -55,11 +57,12 @@ extension MongoCollection {
*/
public func count(_ filter: Document = [:], options: CountOptions? = nil) throws -> Int {
let opts = try BsonEncoder().encode(options)
let rp = options?.readPreference?._readPreference
var error = bson_error_t()
// because we already encode skip and limit in the options,
// pass in 0s so we don't get duplicate parameter errors.
let count = mongoc_collection_count_with_opts(
self._collection, MONGOC_QUERY_NONE, filter.data, 0, 0, opts?.data, nil, &error)
self._collection, MONGOC_QUERY_NONE, filter.data, 0, 0, opts?.data, rp, &error)

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

Expand Down Expand Up @@ -90,10 +93,11 @@ extension MongoCollection {
]

let opts = try BsonEncoder().encode(options)
let rp = options?.readPreference?._readPreference
let reply = Document()
var error = bson_error_t()
if !mongoc_collection_read_command_with_opts(
self._collection, command.data, nil, opts?.data, reply.data, &error) {
self._collection, command.data, rp, opts?.data, reply.data, &error) {
throw MongoError.commandError(message: toErrorString(error))
}

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

/// A ReadPreference to use for this operation.
public let readPreference: ReadPreference?

/// A `WriteConcern` to use in `$out` stages of this operation.
public let writeConcern: WriteConcern?

/// Convenience initializer allowing any/all parameters to be optional
public init(allowDiskUse: Bool? = nil, batchSize: Int32? = nil, bypassDocumentValidation: Bool? = nil,
collation: Document? = nil, comment: String? = nil, hint: Hint? = nil, maxTimeMS: Int64? = nil,
readConcern: ReadConcern? = nil, writeConcern: WriteConcern? = nil) {
readConcern: ReadConcern? = nil, readPreference: ReadPreference? = nil,
writeConcern: WriteConcern? = nil) {
self.allowDiskUse = allowDiskUse
self.batchSize = batchSize
self.bypassDocumentValidation = bypassDocumentValidation
Expand All @@ -179,8 +187,14 @@ public struct AggregateOptions: Encodable {
self.hint = hint
self.maxTimeMS = maxTimeMS
self.readConcern = readConcern
self.readPreference = readPreference
self.writeConcern = writeConcern
}

private enum CodingKeys: String, CodingKey {
case allowDiskUse, batchSize, bypassDocumentValidation, collation, maxTimeMS, comment, hint, readConcern,
writeConcern
}
}

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

/// A ReadPreference to use for this operation.
public let readPreference: ReadPreference?

/// Convenience initializer allowing any/all parameters to be optional
public init(collation: Document? = nil, hint: Hint? = nil, limit: Int64? = nil, maxTimeMS: Int64? = nil,
readConcern: ReadConcern? = nil, skip: Int64? = nil) {
readConcern: ReadConcern? = nil, readPreference: ReadPreference? = nil, skip: Int64? = nil) {
self.collation = collation
self.hint = hint
self.limit = limit
self.maxTimeMS = maxTimeMS
self.readConcern = readConcern
self.readPreference = readPreference
self.skip = skip
}

private enum CodingKeys: String, CodingKey {
case collation, hint, limit, maxTimeMS, readConcern, skip
}
}

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

/// A ReadPreference to use for this operation.
public let readPreference: ReadPreference?

/// Convenience initializer allowing any/all parameters to be optional
public init(collation: Document? = nil, maxTimeMS: Int64? = nil, readConcern: ReadConcern? = nil) {
public init(collation: Document? = nil, maxTimeMS: Int64? = nil, readConcern: ReadConcern? = nil,
readPreference: ReadPreference? = nil) {
self.collation = collation
self.maxTimeMS = maxTimeMS
self.readConcern = readConcern
self.readPreference = readPreference
}

private enum CodingKeys: String, CodingKey {
case collation, maxTimeMS, readConcern
}
}

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

/// A ReadPreference to use for this operation.
public let readPreference: ReadPreference?

/// Convenience initializer allowing any/all parameters to be optional
public init(allowPartialResults: Bool? = nil, batchSize: Int32? = nil, collation: Document? = nil,
comment: String? = nil, cursorType: CursorType? = nil, hint: Hint? = nil, limit: Int64? = nil,
max: Document? = nil, maxAwaitTimeMS: Int64? = nil, maxScan: Int64? = nil, maxTimeMS: Int64? = nil,
min: Document? = nil, noCursorTimeout: Bool? = nil, projection: Document? = nil,
readConcern: ReadConcern? = nil, returnKey: Bool? = nil, showRecordId: Bool? = nil, skip: Int64? = nil,
sort: Document? = nil) {
readConcern: ReadConcern? = nil, readPreference: ReadPreference? = nil, returnKey: Bool? = nil,
showRecordId: Bool? = nil, skip: Int64? = nil, sort: Document? = nil) {
self.allowPartialResults = allowPartialResults
self.batchSize = batchSize
self.collation = collation
Expand All @@ -358,6 +392,7 @@ public struct FindOptions: Encodable {
self.noCursorTimeout = noCursorTimeout
self.projection = projection
self.readConcern = readConcern
self.readPreference = readPreference
self.returnKey = returnKey
self.showRecordId = showRecordId
self.skip = skip
Expand Down
5 changes: 5 additions & 0 deletions Sources/MongoSwift/MongoCollection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public class MongoCollection<T: Codable> {
return rcObj
}

/// The `ReadPreference` set on this collection
public var readPreference: ReadPreference? {
return ReadPreference(from: mongoc_collection_get_read_prefs(self._collection))
}

/// The `WriteConcern` set on this collection, or nil if one is not set.
public var writeConcern: WriteConcern? {
// per libmongoc docs, we don't need to handle freeing this ourselves
Expand Down
33 changes: 29 additions & 4 deletions Sources/MongoSwift/MongoDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,25 @@ public struct RunCommandOptions: Encodable {
/// An optional `ReadConcern` to use for this operation
public let readConcern: ReadConcern?

/// An optional `ReadPreference` to use for this operation
public let readPreference: ReadPreference?

/// An optional WriteConcern to use for this operation
public let writeConcern: WriteConcern?

/// Convenience initializer allowing session to be omitted or optional
public init(readConcern: ReadConcern? = nil, session: ClientSession? = nil,
writeConcern: WriteConcern? = nil) {
public init(readConcern: ReadConcern? = nil, readPreference: ReadPreference? = nil,
session: ClientSession? = nil, writeConcern: WriteConcern? = nil) {
self.readConcern = readConcern
self.readPreference = readPreference
self.session = session
self.writeConcern = writeConcern
}

private enum CodingKeys: String, CodingKey {
// TODO: Encode ClientSession as "sessionId" (see: SWIFT-28)
case readConcern, writeConcern
}
}

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

/// A read preference to set on the returned collection. If one is not
/// specified, the collection will inherit the database's read preference.
public let readPreference: ReadPreference?

/// A write concern to set on the returned collection. If one is not specified,
/// the collection will inherit the database's write concern.
public let writeConcern: WriteConcern?

/// Convenience initializer allowing any/all arguments to be omitted or optional
public init(readConcern: ReadConcern? = nil, writeConcern: WriteConcern? = nil) {
public init(readConcern: ReadConcern? = nil, readPreference: ReadPreference? = nil,
writeConcern: WriteConcern? = nil) {
self.readConcern = readConcern
self.readPreference = readPreference
self.writeConcern = writeConcern
}
}
Expand All @@ -140,6 +155,11 @@ public class MongoDatabase {
return rcObj
}

/// The `ReadPreference` set on this database
public var readPreference: ReadPreference? {
return ReadPreference(from: mongoc_collection_get_read_prefs(self._database))
}

/// The `WriteConcern` set on this database, or `nil` if one is not set.
public var writeConcern: WriteConcern? {
// per libmongoc docs, we don't need to handle freeing this ourselves
Expand Down Expand Up @@ -205,6 +225,10 @@ public class MongoDatabase {
mongoc_collection_set_read_concern(collection, rc._readConcern)
}

if let rp = options?.readPreference {
mongoc_collection_set_read_prefs(collection, rp._readPreference)
}

if let wc = options?.writeConcern {
mongoc_collection_set_write_concern(collection, wc._writeConcern)
}
Expand Down Expand Up @@ -289,10 +313,11 @@ public class MongoDatabase {
*/
@discardableResult
public func runCommand(_ command: Document, options: RunCommandOptions? = nil) throws -> Document {
let rp = options?.readPreference?._readPreference
let opts = try BsonEncoder().encode(options)
let reply = Document()
var error = bson_error_t()
if !mongoc_database_command_with_opts(self._database, command.data, nil, opts?.data, reply.data, &error) {
if !mongoc_database_command_with_opts(self._database, command.data, rp, opts?.data, reply.data, &error) {
throw MongoError.commandError(message: toErrorString(error))
}
return reply
Expand Down