Zaawansowane#
Fluent stara się stworzyć ogólne, niezależne od bazy danych API do pracy z danymi. Ułatwia to naukę Fluent niezależnie od tego, jakiego sterownika bazy danych używasz. Tworzenie generalizowanych API może również sprawić, że praca z bazą danych będzie sprawiać wrażenie bardziej naturalnej w Swift.
Może się jednak zdarzyć, że będziesz potrzebować użyć funkcji Twojego sterownika bazy danych, która nie jest jeszcze obsługiwana przez Fluent. Ten przewodnik omawia zaawansowane wzorce i API we Fluent, które działają tylko z określonymi bazami danych.
SQL#
Wszystkie sterowniki baz danych SQL Fluent są zbudowane na SQLKit. Ta ogólna implementacja SQL jest dostarczana razem z Fluent w module FluentSQL.
SQL Database#
Każdy Database Fluent może zostać rzutowany na SQLDatabase. Dotyczy to req.db, app.db, obiektu database przekazywanego do Migration itd.
import FluentSQL
if let sql = req.db as? SQLDatabase {
// The underlying database driver is SQL.
let planets = try await sql.raw("SELECT * FROM planets").all(decoding: Planet.self)
} else {
// The underlying database driver is _not_ SQL.
}
To rzutowanie zadziała tylko wtedy, gdy sterownik bazy danych jest bazą SQL. Dowiedz się więcej o metodach SQLDatabase w README SQLKit.
Konkretna baza danych SQL#
Możesz również rzutować na konkretne bazy danych SQL, importując dany sterownik.
import FluentPostgresDriver
if let postgres = req.db as? PostgresDatabase {
// The underlying database driver is PostgreSQL.
postgres.simpleQuery("SELECT * FROM planets").all()
} else {
// The underlying database is _not_ PostgreSQL.
}
W chwili pisania tego tekstu obsługiwane są następujące sterowniki SQL.
| Baza danych | Sterownik | Biblioteka |
|---|---|---|
PostgresDatabase |
vapor/fluent-postgres-driver | vapor/postgres-nio |
MySQLDatabase |
vapor/fluent-mysql-driver | vapor/mysql-nio |
SQLiteDatabase |
vapor/fluent-sqlite-driver | vapor/sqlite-nio |
Odwiedź README danej biblioteki, aby dowiedzieć się więcej o API specyficznych dla danej bazy danych.
SQL Custom#
Prawie wszystkie typy zapytań i schematów Fluent obsługują przypadek .custom. Pozwala on wykorzystać funkcje bazy danych, które nie są jeszcze obsługiwane przez Fluent.
import FluentPostgresDriver
let query = Planet.query(on: req.db)
if req.db is PostgresDatabase {
// ILIKE supported.
query.filter(\.$name, .custom("ILIKE"), "earth")
} else {
// ILIKE not supported.
query.group(.or) { or in
or.filter(\.$name == "earth").filter(\.$name == "Earth")
}
}
query.all()
Bazy danych SQL obsługują zarówno String, jak i SQLExpression we wszystkich przypadkach .custom. Moduł FluentSQL udostępnia metody pomocnicze dla typowych przypadków użycia.
import FluentSQL
let query = Planet.query(on: req.db)
if req.db is SQLDatabase {
// The underlying database driver is SQL.
query.filter(.sql(raw: "LOWER(name) = 'earth'"))
} else {
// The underlying database driver is _not_ SQL.
}
Poniżej znajduje się przykład użycia .custom poprzez metodę pomocniczą .sql(raw:) wraz z konstruktorem schematu.
import FluentSQL
let builder = database.schema("planets").id()
if database is MySQLDatabase {
// The underlying database driver is MySQL.
builder.field("name", .sql(raw: "VARCHAR(64)"), .required)
} else {
// The underlying database driver is _not_ MySQL.
builder.field("name", .string, .required)
}
builder.create()
MongoDB#
Fluent MongoDB to integracja pomiędzy Fluent a sterownikiem MongoKitten. Wykorzystuje ona silny system typów Swift oraz niezależny od bazy danych interfejs Fluent, korzystając z MongoDB.
Najczęstszym identyfikatorem w MongoDB jest ObjectId. Możesz go użyć w swoim projekcie za pomocą @ID(custom: .id).
Jeśli musisz używać tych samych modeli z SQL, nie używaj ObjectId. Zamiast tego użyj UUID.
final class User: Model {
// Name of the table or collection.
static let schema = "users"
// Unique identifier for this User.
// In this case, ObjectId is used
// Fluent recommends using UUID by default, however ObjectId is also supported
@ID(custom: .id)
var id: ObjectId?
// The User's email address
@Field(key: "email")
var email: String
// The User's password stores as a BCrypt hash
@Field(key: "password")
var passwordHash: String
// Creates a new, empty User instance, for use by Fluent
init() { }
// Creates a new User with all properties set.
init(id: ObjectId? = nil, email: String, passwordHash: String, profile: Profile) {
self.id = id
self.email = email
self.passwordHash = passwordHash
self.profile = profile
}
}
Modelowanie danych#
W MongoDB modele definiuje się w taki sam sposób, jak w każdym innym środowisku Fluent. Główna różnica pomiędzy bazami danych SQL a MongoDB leży w relacjach i architekturze.
W środowiskach SQL bardzo powszechne jest tworzenie tabel łączących (join tables) dla relacji pomiędzy dwoma encjami. W MongoDB natomiast do przechowywania powiązanych identyfikatorów można użyć tablicy. Ze względu na sposób, w jaki zaprojektowano MongoDB, bardziej efektywne i praktyczne jest projektowanie modeli z zagnieżdżonymi strukturami danych.
Elastyczne dane#
Możesz dodawać elastyczne dane w MongoDB, jednak taki kod nie będzie działał w środowiskach SQL.
Aby utworzyć zgrupowane, dowolne przechowywanie danych, możesz użyć Document.
@Field(key: "document")
var document: Document
Fluent nie może obsługiwać ściśle typowanych zapytań na tych wartościach. Do zapytań możesz użyć ścieżki klucza zapisanej z kropkami (dot notation). Jest to akceptowane w MongoDB do uzyskiwania dostępu do zagnieżdżonych wartości.
Something.query(on: db).filter("document.key", .equal, 5).first()
Użycie wyrażeń regularnych#
Możesz zapytywać MongoDB, używając przypadku .custom() i przekazując wyrażenie regularne. MongoDB akceptuje wyrażenia regularne zgodne z Perl.
Na przykład możesz zapytać o znaki bez rozróżniania wielkości liter w polu 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()
Zwróci to planety zawierające ‘e’ oraz ‘E’. Możesz również utworzyć dowolne inne złożone wyrażenie RegEx akceptowane przez MongoDB.
Surowy dostęp#
Aby uzyskać dostęp do surowej instancji MongoDatabase, rzutuj instancję bazy danych na MongoDatabaseRepresentable w następujący sposób:
guard let db = req.db as? MongoDatabaseRepresentable else {
throw Abort(.internalServerError)
}
let mongodb = db.raw
Stąd możesz korzystać ze wszystkich API MongoKitten.