Avanzado¶
Fluent se esfuerza por crear una API general e independiente de cualquier base de datos que te permita trabajar con tus datos. Esto facilita el proceso de aprendizaje de Fluent, sin importar el conector de base de datos que uses. Crear APIs generalizadas también puede hacer que trabajar con tu base de datos en Swift se sienta más cómodo.
Sin embargo, puede que necesites usar una característica de tu base de datos subyacente que todavía no tenga soporte en Fluent. Esta guía cubre APIs y patrones avanzados en Fluent que solo funcionan con ciertas bases de datos.
SQL¶
Todos los conectores SQL de Fluent están construidos con SQLKit. Esta implementación general de SQL se incluye con Fluent en el módulo FluentSQL
.
Base de datos SQL¶
Cualquier Database
de Fluent puede convertirse (cast) a una SQLDatabase
. Esto incluye req.db
, app.db
, la database
pasada a Migration
, etc.
import FluentSQL
if let sql = req.db as? SQLDatabase {
// El conector subyacente de base de datos es SQL.
let planets = try await sql.raw("SELECT * FROM planets").all(decoding: Planet.self)
} else {
// La base de datos subyacente _no_ es SQL.
}
Esta conversión solo funcionará si el conector de la base de datos subyacente es una base de datos SQL. Aprende más acerca de los métodos de SQLDatabase
en el README de SQLKit.
Bases de datos SQL específicas¶
También puedes convertir a bases de datos SQL específicas importando el conector (driver).
import FluentPostgresDriver
if let postgres = req.db as? PostgresDatabase {
// El conector subyacente de base de datos es PostgreSQL.
postgres.simpleQuery("SELECT * FROM planets").all()
} else {
// La base de datos subyacente _no_ es PostgreSQL.
}
En el momento de esta redacción, están soportados los siguientes conectores SQL:
Base de Datos | Conector | Librería |
---|---|---|
PostgresDatabase |
vapor/fluent-postgres-driver | vapor/postgres-nio |
MySQLDatabase |
vapor/fluent-mysql-driver | vapor/mysql-nio |
SQLiteDatabase |
vapor/fluent-sqlite-driver | vapor/sqlite-nio |
Visita los README de la librerías para más información sobre las APIs específicas de cada base de datos.
SQL Personalizado¶
Casi todos los tipos de consultas y esquemas de Fluent soportan la opción .custom
. Esto te permite utilizar características de bases de datos que Fluent todavía no soporta.
import FluentPostgresDriver
let query = Planet.query(on: req.db)
if req.db is PostgresDatabase {
// ILIKE soportado.
query.filter(\.$name, .custom("ILIKE"), "earth")
} else {
// ILIKE no soportado.
query.group(.or) { or in
or.filter(\.$name == "earth").filter(\.$name == "Earth")
}
}
query.all()
Las bases de datos SQL soportan tanto String
como SQLExpression
en todos los casos .custom
. El módulo FluentSQL
ofrece métodos convenientes para casos de uso comunes.
import FluentSQL
let query = Planet.query(on: req.db)
if req.db is SQLDatabase {
// El conector subyacente de base de datos es SQL.
query.filter(.sql(raw: "LOWER(name) = 'earth'"))
} else {
// La base de datos subyacente _no_ es SQL.
}
A continuación tienes un ejemplo de .custom
usando el método .sql(raw:)
con el constructor del esquema.
import FluentSQL
let builder = database.schema("planets").id()
if database is MySQLDatabase {
// El conector subyacente de base de datos es MySQL.
builder.field("name", .sql(raw: "VARCHAR(64)"), .required)
} else {
// La base de datos subyacente _no_ es MySQL.
builder.field("name", .string, .required)
}
builder.create()
MongoDB¶
Fluent MongoDB es una integración entre Fluent y el conector MongoKitten. Aprovecha el sistema de tipado fuerte de Swift y la interfaz no anclada a bases de datos de Fluent, usando MongoDB.
El identificador más común en MongoDB es ObjectId. Puedes usarlo para tus proyectos usando @ID(custom: .id)
.
Si necesitas usar los mismos modelos con SQL, no uses ObjectId
. Usa UUID
en su lugar.
final class User: Model {
// Nombre de la tabla o colección.
static let schema = "users"
// Identificador único para este User.
// En este caso, se usa ObjectId
// Fluent recomienda usar UUID por defecto, sin embargo ObjectId también está soportado
@ID(custom: .id)
var id: ObjectId?
// La dirección email del User
@Field(key: "email")
var email: String
// La contraseña de User se guarda como un hash de BCrypt
@Field(key: "password")
var passwordHash: String
// Crea una nueva instancia de User vacía para el uso de Fluent
init() { }
// Crea un nuevo User con todas las propiedades establecidas.
init(id: ObjectId? = nil, email: String, passwordHash: String, profile: Profile) {
self.id = id
self.email = email
self.passwordHash = passwordHash
self.profile = profile
}
}
Modelado de Datos¶
En MongoDB, los Modelos se definen de la misma manera que en cualquier otro entorno de Fluent. La principal diferencia entre bases de datos SQL y MongoDB reside en las relaciones y la arquitectura.
En entornos SQL, es muy común crear tablas de unión para relaciones entre dos entidades. En MongoDB, sin embargo, una colección (array) puede usarse para guardar identificadores relacionados. Debido al diseño de MongoDB, es más práctico y eficiente diseñar tus modelos con estructuras de datos anidadas.
Datos Flexibles¶
Puedes añadir datos flexibles en MongoDB, pero este código no funcionará en entornos SQL.
Para crear un almacenamiento de datos arbitrarios agrupados puedes usar Document
.
@Field(key: "document")
var document: Document
Fluent no puede soportar consultas de tipos estrictos en estos valores. Puedes usar un key path con notación de punto (dot notated key path) en tus consultas. Esto está aceptado en MongoDB para acceder a valores anidados.
Something.query(on: db).filter("document.key", .equal, 5).first()
Uso de expresiones regulares¶
Puedes consultar a MongoDB usando el caso .custom()
, y pasando una expresión regular. MongoDB acepta expresiones regulares compatibles con Perl.
Por ejemplo, puedes hacer una consulta que obvie mayúsculas y minúsculas sobre el campo 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()
Esto devolverá planetas que contengan 'e' y 'E'. También puedes crear otras RegEx complejas aceptadas por MongoDB.
Raw Access¶
Para acceder a la instancia MongoDatabase
en bruto, convierte (cast) la instancia de base de datos a MongoDatabaseRepresentable
de la siguiente manera:
guard let db = req.db as? MongoDatabaseRepresentable else {
throw Abort(.internalServerError)
}
let mongodb = db.raw
A partir de aquí puedes usar todas las APIs de MongoKitten.