Skip to content

Using BSON

Joannis Orlandos edited this page Oct 10, 2016 · 4 revisions

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

Let's use some more complex features

Clone this wiki locally