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 BSONs 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