-
-
Notifications
You must be signed in to change notification settings - Fork 35
Using BSON
BSON Documents can be instantiated using multiple methods.
- Using an Array
- Using an ArrayLiteral
- Using a Dictionary
- Using a DictionaryLiteral
- Using JSON
- Using the binary representation of a Document
The literals have been covered in the previous page
Now we'll cover the types of BSON and how to use them in a Document.
So let's create a struct:
import struct Foundation.Date
import BSON
struct User {
let uniqueIdentifier: ObjectId
let username: String
let password: [UInt8] // Hashed
let age: Int32 // Int32 on purpose
let male: Bool
let registrationDate: Date
}
This looks pretty simple for now. So let's instantiate it:
let user = User(uniqueIdentifier: ObjectId(), username: "Joannis", password: [0x00, 0x01, 0x02, 0x03, 0x04], age: 20, male: true, registrationDate: Date())
Now suppose we're trying to write this User to a database (like MongoDB). We'll need to convert the User Struct to a Document.
extension User {
var document: Document {
return [
"_id": ~uniqueIdentifier,
"username": ~username,
"password": .binary(subtype: BinarySubtype.userDefined(0x80), data: password),
"age": ~age,
"male": ~male,
"registrationDate": ~registrationDate
]
}
}
Notice above that we are using a prefix operator
, namely ~
. This is used to automatically generate a BSON Value from a recognised source. Any String
will be converted to BSON.Value.string
. Because any BSON Value
is an enum.
The reason we chose an Int32
for our age is because BSON differentiates Int32 and Int64. Any regular Swift Int
will be seen as Int64
.
As you also notice, we're not storing our password directly with the ~
operator. We're actually creating a BSON.Value (.binary
) that we provide with a custom subtype. This subtype is custom, or userDefined
to the type identifier of 0x80
.
The next use case would be to serialize (part of) the User
to a JSON API. We could do this by converting the Document to JSON.
user.document.makeExtendedJSON()
Our Extended JSON parser and serializer make use of MongoDB Extended JSON. Which is specially formatted JSON that supports keeping the type information of non-native JSON objects like ObjectId
.
If you were to convert a Document to a JSON String, and parse it again the result will be the same.
let special = true
let document = ["my": "Str1ng", "number": .int32(3), "special": true]
let json = document.makeExtendedJSON()
let copyDocument = try Document(extendedJSON: json) // equal to "document"
Another use case of BSON would be to fetch a user's username from it's Document. This is especially useful when instantiating a Model or other object from BSON. Or when working with BSON Documents directly.
To make this easy to use, we support subscripting Document
in the same way you'd expect from an Array and/or Dictionary.
We'll process the next example Document:
import Foundation
let specialDocument: Document = [
"special": true,
"details": [
"first_name": "Bob",
"last_name": "Smith",
"age": 24
],
"createDate": ~Date()
]
import Foundation
struct SpecialObject {
let special: Bool
let firstName: String
let lastName: String
let age: Int
let created: Date
init(_ document: Document) {
// TODO: OpenKitten example code
}
}