Skip to content

Commit 03f9d0b

Browse files
authored
Updates to guides (#606)
1 parent 04af5ce commit 03f9d0b

File tree

2 files changed

+43
-168
lines changed

2 files changed

+43
-168
lines changed

Guides/BSON-Guide.md

Lines changed: 0 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -102,163 +102,3 @@ Previously, `Document`/`BSONDocument` had computed properties, `extendedJSON` an
102102

103103
#### Errors
104104
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.
105-
106-
### Migrating from the 0.0.1-0.1.3 API to the 0.2.0 BSON API
107-
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).
108-
#### Overview of BSON API v1
109-
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`).
110-
```swift
111-
var doc: Document = [
112-
"a": 5
113-
"b": ObjectId()
114-
]
115-
let value: BSONValue? = doc["a"] // 5
116-
let intValue = (value as? BSONNumber)?.int32Value // Int32(5)
117-
doc["c"] = "i am a string"
118-
```
119-
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.
120-
121-
Another drawback is that subdocument literals could not be inferred and had to be explicitly casted:
122-
```swift
123-
let x: Document = [
124-
"x": [
125-
"y": [
126-
z: 4
127-
] as Document
128-
] as Document
129-
]
130-
```
131-
#### Required Updates
132-
In BSON API v2, `BSONNumber`, `BSONValue`, and `AnyBSONValue` no longer exist. They are all entirely replaced by the `BSON` enum.
133-
134-
##### Updating `BSONValue` references
135-
136-
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.
137-
```swift
138-
func foo(x: BSONValue) -> String? {
139-
guard let stringValue = x as? String else {
140-
return nil
141-
}
142-
return "foo" + stringValue
143-
}
144-
```
145-
becomes:
146-
```swift
147-
func foo(x: BSON) -> String? {
148-
guard let stringValue = x.stringValue else {
149-
return nil
150-
}
151-
// or
152-
guard case let .string(stringValue) = x else {
153-
return nil
154-
}
155-
return "foo" + stringValue
156-
}
157-
```
158-
Similarly, `BSON`'s `Equatable` conformance can be leveraged instead of the old `bsonEquals`.
159-
```swift
160-
func foo(x: BSONValue, y: BSONValue) {
161-
if x.bsonEquals(y) { ... }
162-
}
163-
```
164-
becomes simply:
165-
```swift
166-
func foo(x: BSON, y: BSON) {
167-
if x == y { ... }
168-
}
169-
```
170-
**Generic Requirement**
171-
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).
172-
173-
##### Updating `BSONNumber` references
174-
`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()`).
175-
176-
```swift
177-
// old
178-
func foo(doc: Document) -> Int? {
179-
// conversion
180-
guard let int = (doc["a"] as? BSONNumber)?.intValue else {
181-
return nil
182-
}
183-
// cast
184-
guard let otherInt = doc["b"] as? Int32 else {
185-
return nil
186-
}
187-
return int*Int(otherInt)
188-
}
189-
190-
// new
191-
func foo(doc: Document) -> Int? {
192-
// conversion
193-
guard let int = doc["a"]?.asInt() else {
194-
return nil
195-
}
196-
// cast
197-
guard let otherInt = doc["b"]?.int32Value else {
198-
return nil
199-
}
200-
// or can use case let
201-
guard case let .int32(otherInt) = doc["b"] else {
202-
return nil
203-
}
204-
return int * Int(otherInt)
205-
}
206-
```
207-
##### Updating `AnyBSONValue` references
208-
`BSON` should be able to serve as a complete replacement for `AnyBSONValue`.
209-
`Codable` usage:
210-
```swift
211-
// old
212-
struct X: Codable {
213-
let x: AnyBSONValue
214-
}
215-
// new
216-
struct X: Codable {
217-
let x: BSON
218-
}
219-
```
220-
`Equatable` usage:
221-
```swift
222-
// old
223-
let a: [BSONValue] = ["1", 2, false]
224-
let b: [BSONValue] = ["not", "equal"]
225-
return a.map { AnyBSONValue($0) } == b.map { AnyBSONValue($0) }
226-
// new
227-
let a: [BSON] = ["1", 2, false]
228-
let b: [BSON] = ["not", "equal"]
229-
return a == b
230-
```
231-
`Hashable` usage:
232-
```swift
233-
// old
234-
let a: [AnyBSONValue: Int] = [AnyBSONValue("hello"): 4, AnyBSONValue(ObjectId()): 26]
235-
print(a[AnyBSONValue(true)] ?? "nil")
236-
// new
237-
let a: [BSON, Int] = ["hello": 4, .objectId(ObjectId()): 26]
238-
print(a[true] ?? "nil")
239-
```
240-
241-
##### Updating `Document` literals
242-
`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.
243-
```swift
244-
// BSON API v1
245-
let x: Document = [
246-
"_id": self.getDocument()
247-
"x": Date()
248-
"y": [
249-
"z": [1, 2, false, ["x": 123] as Document]
250-
] as Document
251-
]
252-
```
253-
becomes
254-
```swift
255-
// BSON API v2
256-
let x: Document = [
257-
"_id": .document(self.getDocument())
258-
"x": .datetime(Date()),
259-
"y": [
260-
"z": [1, 2, false, ["x": 123]
261-
]
262-
]
263-
```
264-

Guides/Multithreaded-Usage.md

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,54 @@
1-
# Using MongoSwift in Mulithreaded Applications
1+
# Using the Driver in Multithreaded Applications
22

3-
## Threadsafe Types
4-
As of MongoSwift 0.2.0, the following types are safe to use across threads:
3+
## Async API
4+
Our asynchronous API is designed to be used in SwiftNIO-based applications running atop `EventLoopGroup`s
5+
composed of one or more `EventLoop`s.
6+
7+
You must pass in your application's `EventLoopGroup` when initializing a `MongoClient`, like:
8+
```swift
9+
let client = try MongoClient("mongodb://localhost:27017", using: myEventLoopGroup)
10+
```
11+
12+
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
13+
the state of the MongoDB deployment. Using a single client allows these resources to be efficiently shared
14+
throughout your application.
15+
16+
### Safe Use Across Event Loops
17+
The following types are all designed to be safe to access across multiple threads/event loops:
518
* `MongoClient`
619
* `MongoDatabase`
720
* `MongoCollection`
821

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

11-
## Best Practices
12-
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.
24+
That said: each of these types will, by default, not necessarily always return `EventLoopFuture`s on the
25+
same `EventLoop` you are using them on. Each time an `EventLoopFuture` is generated, they will call
26+
`EventLoopGroup.next()` on the `MongoClient`'s underyling `EventLoopGroup` to select a next `EventLoop` to use.
1327

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

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

18-
## Usage With Server-side Swift Frameworks
36+
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
37+
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.
38+
39+
Please see the [EventLoopFuture](https://apple.github.io/swift-nio/docs/current/NIO/Classes/EventLoopFuture.html)
40+
documentation for more details on multithreading.
41+
### Usage With Server-side Swift Frameworks
1942
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.
43+
44+
## Sync API
45+
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
46+
the state of the MongoDB deployment. Using a single client allows these resources to be efficiently shared
47+
throughout your application.
48+
49+
The following types are safe to share across threads:
50+
* `MongoClient`
51+
* `MongoDatabase`
52+
* `MongoCollection`
53+
54+
*We make no guarantees about the safety of using any other type across threads.*

0 commit comments

Comments
 (0)