Contents

perfectlysoft/perfect-mongodb

简体中文

Requirements

libmongoc 2.x must be installed where pkg-config can find mongoc2 and bson2. Tested with 2.2.2 and 2.5.5.

macOS (Homebrew):

brew install mongo-c-driver

On Apple silicon, if SwiftPM can't find the library, set PKG_CONFIG_PATH=/opt/homebrew/lib/pkgconfig.

Linux: whether your distribution's package is new enough depends on the release:

| Distribution | libmongoc-dev version | What to do | |---|---|---| | Ubuntu 26.04 LTS and later | 2.2 or later | apt-get install libmongoc-dev libbson-dev | | Debian forky / sid | 2.5 | apt-get install libmongoc-dev libbson-dev | | Ubuntu 24.04 LTS (used by the swift:*-noble images), Debian 13 and older | 1.x, not supported | Build from source (below) |

To build libmongoc 2 from source, as the CI workflow in .github/workflows/ci.yml does:

apt-get install cmake libssl-dev libsasl2-dev libzstd-dev
curl -fsSL https://github.com/mongodb/mongo-c-driver/releases/download/2.5.5/mongo-c-driver-2.5.5.tar.gz | tar xz
cmake -S mongo-c-driver-2.5.5 -B build -DCMAKE_BUILD_TYPE=Release -DENABLE_TESTS=OFF -DENABLE_EXAMPLES=OFF
cmake --build build --parallel && sudo cmake --install build

Example

import PerfectMongoDB

let client = try MongoClient(uri: "mongodb://localhost")
let users = client.getDatabase(name: "app").getCollection(name: "users")!

_ = users.insert(document: try BSON(json: #"{"name": "Ada", "age": 36}"#))

if let cursor = users.find(query: try BSON(json: #"{"age": {"$gt": 30}}"#)) {
    for user in cursor {
        print(user.asString)
    }
}

Codable and async/await

BSONEncoder and BSONDecoder map Codable types to BSON documents, keeping dates, binary data, UUIDs and ObjectIds as native BSON types. Collections have typed, throwing methods built on them. For concurrency, share one MongoClientPool. withClient runs blocking driver work off Swift's cooperative thread pool, and find streams results as an AsyncSequence:

struct User: Codable, Sendable {
    var _id: BSON.OID
    var name: String
    var age: Int
    var joined: Date
}

let pool = try MongoClientPool(validatingURI: "mongodb://localhost")

try await pool.withClient { client in
    let users = client.getCollection(databaseName: "app", collectionName: "users")
    try users.insert(User(_id: BSON.OID(), name: "Ada", age: 36, joined: Date()))
    try users.updateOne(filter: try BSON(json: #"{"name": "Ada"}"#),
                        update: try BSON(json: #"{"$inc": {"age": 1}}"#))
}

for try await user in pool.find(User.self, database: "app", collection: "users",
                                filter: try BSON(json: #"{"age": {"$gt": 30}}"#)) {
    print(user.name)
}

A MongoClient and the collections and cursors made from it are not thread-safe: use each from one task at a time, which withClient and find do for you.

Testing

The tests expect a MongoDB server on mongodb://localhost:

mongod --dbpath /tmp/mongo-test
swift test

To run the suite on Linux locally the way CI does (Swift 6.4, libmongoc 2.5.5, MongoDB 8), use Apple's container tool:

container system start          # once
Scripts/test-linux.sh           # extra arguments go to swift test, e.g. --filter Phase4Tests

The first run compiles libmongoc into a cached volume (under a minute); later runs take seconds.

Package Metadata

Repository: perfectlysoft/perfect-mongodb

Default branch: main

README: README.md