Hi @Mark_Windrim, welcome to the forums and thanks for reaching out!
The short answer, and what you need to get your code to compile, is to both declare that passedInArray
is an [BSON]
when you create it, and explicitly state the corresponding BSON
enum case that passedInArray
corresponds to:
let passedInArray: [BSON] = [1, 2, 3]
let query: BSONDocument = [
"someID": [
"$in": .array(passedInArray)
]
]
The long answer and why you need this is slightly tricky, but I will do my best to explain. It involves Swift’s type inference capabilities and ExpressibleBy
protocols.
The BSON library has both a BSONDocument
type which is essentially an ordered map of strings to BSON values, and a BSON
type, which is an enum with associated values, where each case corresponds to a different BSON type.
The BSONDocument
type conforms to the ExpressibleByDictionaryLiteral
protocol, where the dictionary is a [String: BSON]
.
The BSON
type conforms to a number of ExpressibleBy
protocols:
-
ExpressibleByDictionaryLiteral
with a [String: BSON]
: when used, this initializes a new BSON.document
-
ExpressibleByArrayLiteral
with a [BSON]
, iinitializes a new BSON.array
-
ExpressibleByIntegerLiteral
, initializes a new BSON.int32
or BSON.int64
, depending on what the width of Int
on the platform you’re using is
-
ExpressibleByBooleanLiteral
- initializes a new BSON.bool
For example, one could do something like
let d: BSON = ["a": 1] // BSON.document(["a": 1])
Which would result in an instance of the BSON
enum with case .document
wrapping a BSONDocument
created from ["a": 1]
.
Or:
let b: BSON = true // BSON.bool(true)
So in your first example, since you’ve added the BSONDocument
type annotation, the compiler infers that "someID"
is a String
and that
[
"$in": [1,2,3]
]
is a BSON
, and since it is a dictionary literal it is inferred to be a [String: BSON]
.
Thus the compiler infers that $in
is a String
. Since [1, 2, 3]
is an array literal, the compiler infers it to be an [BSON]
, and infers the individual elements 1, 2, 3, which are integer literals, to be BSON
s as well.
Written without the help of the ExpressibleBy
protocol implementations for BSON
, your first document would look like:
let query: BSONDocument = [
"someID": BSON.document([
"$in": BSON.array([BSON.int64(1), BSON.int64(2), BSON.int64(3)])
])
]
Fortunately, you do not need to include the explicit enum cases for all of those.
Now, visiting your second example: the problem is that, since passedInArray
is not an array literal and is just a plain old array, the corresponding BSON
type, BSON.array
, cannot be automatically instantiated from it. Thus, you need the .array(...)
around it (the compiler can infer the BSON prefix), and when initializing it you need to tell the compiler that it’s an [BSON]
and not an [Int]
.
You could also convert an [Int]
to an [BSON]
like: myArray.map { BSON.int64($0) }
.
Hopefully that is helpful, and let me know if you have further questions! In short, when writing out a document, if you are using a literal you do not need to state which BSON enum case it corresponds to, but if you are using a variable, you do.
Relevant links:
Documentation for BSONDocument
: BSONDocument Structure Reference
Documentation for BSON
type: BSON Enumeration Reference
Blog post on the ExpressibleBy
protocols: Swift ExpressibleBy protocols: What they are and how they work internally in the compiler