vapor-docs/docs/fluent/advanced.zh.md

6.4 KiB
Raw Permalink Blame History

进阶

Fluent 致力于创建一个通用的、与数据库无关的 API 来处理数据。无论你使用哪种数据库驱动程序,都可以轻松的学习 Fluent。创建通用 API 还可以让你在 Swift 中更轻松自在的使用数据库。

然而,你可能需要使用 Fluent 尚不支持的某个功能来驱动底层数据库。本指南涵盖了 Fluent 中仅适用于某些数据库的高级模式和 API。

SQL

Fluent 的所有 SQL 数据库驱动程序都是建立在 SQLKit 之上。此通用 SQL 实现是在 Fluent 的 FluentSQL 模块中提供的。

SQL 数据库

任何 Fluent 数据库 都可以转换为 SQLDatabase。 这包括 req.dbapp.db,传递给迁移数据库等。

import FluentSQL

if let sql = req.db as? SQLDatabase {
    // 底层数据库驱动程序是 SQL
    let planets = try await sql.raw("SELECT * FROM planets").all(decoding: Planet.self)
} else {
    // 其它驱动
}

此转换仅在底层数据库驱动程序是 SQL 数据库时才有效。了解有关 SQLDatabase 方法的更多信息,请参阅 SQLKit's README

指定 SQL 数据库

你还可以通过导入驱动程序转换为指定的 SQL 数据库。

import FluentPostgresDriver

if let postgres = req.db as? PostgresDatabase {
    // 底层数据库驱动程序是 PostgreSQL.
    postgres.simpleQuery("SELECT * FROM planets").all()
} else {
    // 其它驱动
}

在撰写本文时,支持以下 SQL 驱动程序。

数据库 驱动
PostgresDatabase vapor/fluent-postgres-driver vapor/postgres-nio
MySQLDatabase vapor/fluent-mysql-driver vapor/mysql-nio
SQLiteDatabase vapor/fluent-sqlite-driver vapor/sqlite-nio

了解更多特定数据库的 API 信息,请参阅该库的 README。

自定义 SQL

几乎所有 Fluent 的查询和模式类型都支持 .custom 方法。你就可以使用那些 Fluent 还未支持的数据库功能。

import FluentPostgresDriver

let query = Planet.query(on: req.db)
if req.db is PostgresDatabase {
    // 支持 ILIKE 语句查询。
    query.filter(\.$name, .custom("ILIKE"), "earth")
} else {
    // 不支持 ILIKE 语句的查询。
    query.group(.or) { or in
        or.filter(\.$name == "earth").filter(\.$name == "Earth")
    }
}
query.all()

在 SQL 数据库中所有 .custom 用例都支持字符串SQL 表达式FluentSQL 模块为常见的用例提供了方便的方法。

import FluentSQL

let query = Planet.query(on: req.db)
if req.db is SQLDatabase {
    // 底层数据库驱动程序是 SQL.
    query.filter(.sql(raw: "LOWER(name) = 'earth'"))
} else {
    // 其它驱动
}

下面是 .custom 的一个示例,通过 .sql(raw:) 方法与模式构建器一起使用的便利性。

import FluentSQL

let builder = database.schema("planets").id()
if database is MySQLDatabase {
    // 底层数据库驱动程序是 MySQL.
    builder.field("name", .sql(raw: "VARCHAR(64)"), .required)
} else {
    // 其它驱动
    builder.field("name", .string, .required)
}
builder.create()

MongoDB

Fluent MongoDB 是一个集成了 FluentMongoKitten 的驱动程序。它利用 Swift 的强类型特性以及 Fluent 使用与 MongoDB 数据库无关的接口。

MongoDB 中最常见的标识符是 ObjectId。你可以在项目中使用 @ID(custom: .id) 自定义标志符。 如果需要在 SQL 中使用相同的模型,请不要使用 ObjectId。改为使用 UUID

final class User: Model {
    // 表名或集合名
    static let schema = "users"

    // 用户标志符
    // 本例中使用 ObjectId
    // Fluent默认推荐使用 UUID当然 ObjectId 也是支持的
    @ID(custom: .id)
    var id: ObjectId?

    // 用户邮箱
    @Field(key: "email")
    var email: String

    // 用户密码存储为 BCrypt 散列
    @Field(key: "password")
    var passwordHash: String

    // 创建一个新的空 User 实例,供 Fluent 使用
    init() { }

    // 创建用户时设置所有属性
    init(id: ObjectId? = nil, email: String, passwordHash: String, profile: Profile) {
        self.id = id
        self.email = email
        self.passwordHash = passwordHash
        self.profile = profile
    }
}

数据建模

在 MongoDB 中,模型的定义与任何其它 Fluent 环境中的定义相同。SQL 数据库和 MongoDB 的主要区别在于关系和架构。

在 SQL 环境中,为两个实体之间的关系创建连接表是很常见的。然而,在 MongoDB 中,可以使用数组来存储相关的标识符。由于 MongoDB 的设计,使用嵌套数据结构设计模型更加高效和实用。

Flexible Data

您可以在 MongoDB 中添加灵活的数据,但此代码在 SQL 环境中不起作用。要创建分组的任意数据存储,你可以使用 Document

@Field(key: "document")
var document: Document

Fluent 不支持对这些值进行严格的类型查询。你可以在查询中使用 .key 路径进行查询。MongoDB 中接受这样的参数,用以访问嵌套值。

Something.query(on: db).filter("document.key", .equal, 5).first()

使用正则表达式

你可以使用 .custom() 方法,并传递一个正则表达式来查询 MongoDB。MongoDB 接受与 Perl 兼容的正则表达式。

例如,你可以在字段 name 下查询不区分大小写的字符:

import FluentMongoDriver
       
var queryDocument = Document()
queryDocument["name"]["$regex"] = "e"
queryDocument["name"]["$options"] = "i"

let planets = try Planet.query(on: req.db).filter(.custom(queryDocument)).all()

这将返回包含 'e' 和 'E' 的行星。你还可以创建 MongoDB 接受的任何其他复杂的 RegEx。

访问原始数据

要访问原始的 MongoDatabase 实例,将数据库实例转换为 MongoDatabaseRepresentable,如下所示:

guard let db = req.db as? MongoDatabaseRepresentable else {
  throw Abort(.internalServerError)
}

let mongodb = db.raw

接下来你可以使用所有 MongoKitten 的 API。