Thanks for the details @Mordil @tomerd!
One question that's come up for us recently we'd love to hear community thoughts on is how options are provided to API methods. As it stands, we have struct
s for each API method that contain allowed options for that method. But we've considered an alternative approach where we instead just accept all the options directly as arguments to the method.
For example, countDocuments
looks like this:
public func countDocuments(
_ filter: Document = [:],
options: CountDocumentsOptions? = nil,
session: ClientSession? = nil
) -> EventLoopFuture<Int>
and CountDocumentsOptions
looks like this:
public struct CountDocumentsOptions: Codable {
/// Specifies a collation.
public var collation: Document?
/// A hint for the index to use.
public var hint: Hint?
/// The maximum number of documents to count.
public var limit: Int?
/// The maximum amount of time to allow the query to run.
public var maxTimeMS: Int?
/// A ReadConcern to use for this operation.
public var readConcern: ReadConcern?
/// A ReadPreference to use for this operation.
public var readPreference: ReadPreference?
/// The number of documents to skip before counting.
public var skip: Int?
/// Convenience initializer allowing any/all parameters to be optional
public init(
collation: Document? = nil,
hint: Hint? = nil,
limit: Int? = nil,
maxTimeMS: Int? = nil,
readConcern: ReadConcern? = nil,
readPreference: ReadPreference? = nil,
skip: Int? = nil
) { ... }
}
So right now if you wanted to perform a count using some options you'd do something like
var opts = CountDocumentsOptions()
opts.readPreference = .primaryPreferred
opts.skip = 100
// alternatively construct options in one line
let opts = CountDocumentsOptions(readPreference: .primaryPreferred, skip: 100)
collection.countDocuments(["a": 1], options: opts).map { ... }
The alternative way of doing this would be to have countDocuments
looks like this:
public func countDocuments(
_ filter: Document = [:],
collation: Document? = nil,
hint: Hint? = nil,
limit: Int? = nil,
maxTimeMS: Int? = nil,
readConcern: ReadConcern? = nil,
readPreference: ReadPreference? = nil,
skip: Int? = nil,
session: ClientSession? = nil
) -> EventLoopFuture<Int>
And then to call the method with options you'd do
collection.countDocuments(
["a": 1],
readPreference: .primaryPreferred,
skip: 100
).map { ... }
The first approach is more verbose, but it does allow reuse of options structs and prevents our method signatures from becoming extremely lengthy (some API methods like find
have 20+ valid options users can specify).
Both approaches have precedent amongst our other drivers.
Would love to hear what you all think!