Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 0 additions & 160 deletions Guides/BSON-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,163 +102,3 @@ Previously, `Document`/`BSONDocument` had computed properties, `extendedJSON` an

#### Errors
Previously, the BSON library used the same types of errors as the driver. As of 1.0.0, the BSON library has its own set of errors. Please see the [error handling guide](https://github.com/mongodb/mongo-swift-driver/blob/main/Guides/Error-Handling.md) for more details.

### Migrating from the 0.0.1-0.1.3 API to the 0.2.0 BSON API
In version 0.2.0 of `MongoSwift`, the public API for using BSON values was changed dramatically. This section will describe the process for migrating from the old API (BSON API v1) to this new one (BSON API v2).
#### Overview of BSON API v1
The previous API was based around the `BSONValue` protocol. Types that conformed to this protocol could be inserted to or read out of `Document` and could aslo be used in `Document` literals. The protocol was also used in various places around the driver as an existential type or conformance requirement. A related protocol, `BSONNumber`, inherited from `BSONValue` and provided some numeric conversion helpers for the various BSON number types (e.g. `Double`, `Int32`, `Int`).
```swift
var doc: Document = [
"a": 5
"b": ObjectId()
]
let value: BSONValue? = doc["a"] // 5
let intValue = (value as? BSONNumber)?.int32Value // Int32(5)
doc["c"] = "i am a string"
```
This API provided a number of benefits, the principal one being the seamless integration of standard Swift types (e.g. `Int`) and driver custom ones (e.g `ObjectId`) into `Document`'s methods. It also had a few drawbacks, however. In order for `BSONValue` to be used as an existential type, it could not have `Self` or associated type requirements. This ended being a big restriction as it meant `BSONValue` could not be `Equatable`, `Hashable`, or `Codable`. Instead, all of this functionaltiy was put onto the separate wrapper type `AnyBSONValue`, which was used instead of an existential `BSONValue` in many places in order to leverage these common protocol conformances.

Another drawback is that subdocument literals could not be inferred and had to be explicitly casted:
```swift
let x: Document = [
"x": [
"y": [
z: 4
] as Document
] as Document
]
```
#### Required Updates
In BSON API v2, `BSONNumber`, `BSONValue`, and `AnyBSONValue` no longer exist. They are all entirely replaced by the `BSON` enum.

##### Updating `BSONValue` references

Anywhere in the driver that formerly accepted or returned a `BSONValue` will now accept or return a `BSON`. Wherever `BSONValue` is used as an existential value in your application, a `BSON` will probably work as a drop-in replacement. Any casts will need to be updated to call the appropriate helper property instead.
```swift
func foo(x: BSONValue) -> String? {
guard let stringValue = x as? String else {
return nil
}
return "foo" + stringValue
}
```
becomes:
```swift
func foo(x: BSON) -> String? {
guard let stringValue = x.stringValue else {
return nil
}
// or
guard case let .string(stringValue) = x else {
return nil
}
return "foo" + stringValue
}
```
Similarly, `BSON`'s `Equatable` conformance can be leveraged instead of the old `bsonEquals`.
```swift
func foo(x: BSONValue, y: BSONValue) {
if x.bsonEquals(y) { ... }
}
```
becomes simply:
```swift
func foo(x: BSON, y: BSON) {
if x == y { ... }
}
```
**Generic Requirement**
Currently, there is no equivalent protocol in BSON API v2 to `BSONValue`, so if your application was using it as a generic requirement there is no alternative in the driver. You may have to implement your own similar protocol to achieve the same effect. If such a protocol would be useful to you, please [file a ticket on the driver's Jira project](https://github.com/mongodb/mongo-swift-driver#bugs--feature-requests).

##### Updating `BSONNumber` references
`BSON` should be a drop-in replacement for anywhere `BSONNumber` is used, except for as a generic requirement. One thing to note that `BSONNumber`'s properties (e.g. `.int32Value`) are _conversions_, whereas `BSON`'s are simple unwraps. The conversions on `BSON` are implemented as methods (e.g. `asInt32()`).

```swift
// old
func foo(doc: Document) -> Int? {
// conversion
guard let int = (doc["a"] as? BSONNumber)?.intValue else {
return nil
}
// cast
guard let otherInt = doc["b"] as? Int32 else {
return nil
}
return int*Int(otherInt)
}

// new
func foo(doc: Document) -> Int? {
// conversion
guard let int = doc["a"]?.asInt() else {
return nil
}
// cast
guard let otherInt = doc["b"]?.int32Value else {
return nil
}
// or can use case let
guard case let .int32(otherInt) = doc["b"] else {
return nil
}
return int * Int(otherInt)
}
```
##### Updating `AnyBSONValue` references
`BSON` should be able to serve as a complete replacement for `AnyBSONValue`.
`Codable` usage:
```swift
// old
struct X: Codable {
let x: AnyBSONValue
}
// new
struct X: Codable {
let x: BSON
}
```
`Equatable` usage:
```swift
// old
let a: [BSONValue] = ["1", 2, false]
let b: [BSONValue] = ["not", "equal"]
return a.map { AnyBSONValue($0) } == b.map { AnyBSONValue($0) }
// new
let a: [BSON] = ["1", 2, false]
let b: [BSON] = ["not", "equal"]
return a == b
```
`Hashable` usage:
```swift
// old
let a: [AnyBSONValue: Int] = [AnyBSONValue("hello"): 4, AnyBSONValue(ObjectId()): 26]
print(a[AnyBSONValue(true)] ?? "nil")
// new
let a: [BSON, Int] = ["hello": 4, .objectId(ObjectId()): 26]
print(a[true] ?? "nil")
```

##### Updating `Document` literals
`BSON` can be expressed by a dictionary literal, string literal, integer literal, float literal, boolean literal, and array literal, so document literals consisting of those literals can largely be left alone. All the other types that formerly conformed to `BSONValue` will need to have their cases explicitly constructed. The cast to `Document` will no longer be required for subdocuments and will need to be removed. All runtime variables will also need to have their cases explicitly constructed whereas in BSON API v1 they could just be inserted directly.
```swift
// BSON API v1
let x: Document = [
"_id": self.getDocument()
"x": Date()
"y": [
"z": [1, 2, false, ["x": 123] as Document]
] as Document
]
```
becomes
```swift
// BSON API v2
let x: Document = [
"_id": .document(self.getDocument())
"x": .datetime(Date()),
"y": [
"z": [1, 2, false, ["x": 123]
]
]
```

51 changes: 43 additions & 8 deletions Guides/Multithreaded-Usage.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,54 @@
# Using MongoSwift in Mulithreaded Applications
# Using the Driver in Multithreaded Applications

## Threadsafe Types
As of MongoSwift 0.2.0, the following types are safe to use across threads:
## Async API
Our asynchronous API is designed to be used in SwiftNIO-based applications running atop `EventLoopGroup`s
composed of one or more `EventLoop`s.

You must pass in your application's `EventLoopGroup` when initializing a `MongoClient`, like:
```swift
let client = try MongoClient("mongodb://localhost:27017", using: myEventLoopGroup)
```

We strongly recommend using a single, global `MongoClient` per application. Each client is backed by a pool of connections per each server in the in MongoDB deployment, and utilizes a background thread to continuously monitor
the state of the MongoDB deployment. Using a single client allows these resources to be efficiently shared
throughout your application.

### Safe Use Across Event Loops
The following types are all designed to be safe to access across multiple threads/event loops:
* `MongoClient`
* `MongoDatabase`
* `MongoCollection`

*We make no guarantees about the safety of using any other type across threads.*

## Best Practices
Each `MongoClient` is backed by a pool of server connections. Any time a client or one of its child objects (a `MongoDatabase` or `MongoCollection`) makes a request to the database (a `find`, `insertOne`, etc.) a connection will be retrieved from the pool, used to execute the operation, and then returned to the pool when it is finished.
That said: each of these types will, by default, not necessarily always return `EventLoopFuture`s on the
same `EventLoop` you are using them on. Each time an `EventLoopFuture` is generated, they will call
`EventLoopGroup.next()` on the `MongoClient`'s underyling `EventLoopGroup` to select a next `EventLoop` to use.

Each `MongoClient` uses its own background thread to monitor the MongoDB topology you are connected to.
To ensure thread safety when working with these returned futures, you should call `hop(to:)` on them in order
to "hop" the future over to your current event loop, which ensures any callbacks you register on the future
will fire on your current event loop.

**In order to share the connection pool across threads and minimize the number of background monitoring threads, we recommend sharing `MongoClient`s across threads.**
Depending on your use case, a more convenient alternative for you may be to use versions of these core driver
types which are "bound" to particular `EventLoop`s, i.e. that always automatically return `EventLoopFuture`s
on the `EventLoop` they are bound to (as opposed to any `EventLoop` from the underlying `EventLoopGroup`).

## Usage With Server-side Swift Frameworks
To use the "bound" API, you can call `bound(to:)` on your global `MongoClient` to instantiate an `EventLoopBoundMongoClient`, which a small wrapper type around a `MongoClient` that returns futures solely
on its bound `EventLoop`. Any child `MongoDatabase`s or `MongoCollection`s retrieved from the bound client will automatically be bound to the same `EventLoop` as the client.

Please see the [EventLoopFuture](https://apple.github.io/swift-nio/docs/current/NIO/Classes/EventLoopFuture.html)
documentation for more details on multithreading.
### Usage With Server-side Swift Frameworks
See the [`Examples/`](https://github.com/mongodb/mongo-swift-driver/tree/main/Examples) directory in the driver GitHub repository for examples of how to integrate the driver in multithreaded frameworks.

## Sync API
In the synchronous API, we strongly recommend using a single, global `MongoClient` per application. Each client is backed by a pool of connections per each server in the in MongoDB deployment, and utilizes a background thread to continuously monitor
the state of the MongoDB deployment. Using a single client allows these resources to be efficiently shared
throughout your application.

The following types are safe to share across threads:
* `MongoClient`
* `MongoDatabase`
* `MongoCollection`

*We make no guarantees about the safety of using any other type across threads.*