大师网-带你快速走向大师之路 解决你在学习过程中的疑惑,带你快速进入大师之门。节省时间,提升效率

Vapor3初探——使用Fluent查询数据

Vapor升级到Vapor3的时间恰巧赶上WWDC,本来想从美国回来就开始写一些关于Vapor3的文章,但是WWDC信息量太大了,直到现在才转过来写一点关于Vapor的东西。

这篇文章主要介绍如何使用Fluent查询数据,后面我会补上如何配置相关的教程。

Fluent为我们提供了一个Model类, MySQLModel、 PostgreSQLModel、SQLiteModel均继承自这个类。在Model的一个extension中,Flunt为我们提供了一个query方法并返回 QueryBuilder<Self.Database, Self>

下面,我们再看看这个QueryBuilder:

QueryBuilder<Self.Database, Self>

这个QueryBuilder会含有Model的类型,数据库类型,利用这些信息来连接数据库进行查询。

现在有了这个QueryBuilder了,下面就可以利用它来进行查询操作了:

为了简化对问题的理解,这里提供一个最简单的Model来验证:

import FluentMySQL
import Vapor

/// A single entry of a Todo list.
final class Todo: Codable {
    /// The unique identifier for this `Todo`.
    var id: Int?

    /// A title describing what this `Todo` entails.
    var title: String

    /// Creates a new `Todo`.
    init(id: Int? = nil, title: String) {
        self.id = id
        self.title = title
    }
}


extension Todo: MySQLModel { }

/// Allows `Todo` to be used as a dynamic migration.
extension Todo: Migration { }

/// Allows `Todo` to be encoded to and decoded from HTTP messages.
extension Todo: Content { }

/// Allows `Todo` to be used as a dynamic parameter in route definitions.
extension Todo: Parameter { }

可以看到这里的Todo类只含有一个id和title,而id由mysql数据库自动生成,只需要传入一个title则可以添加一条记录。

查询所有记录

Todo.query(on: req).all()

查询title为'work'的记录

try Todo.query(on: req).filter(\.title == "work" ).all()

查询title不为'work'的记录

try Todo.query(on: req).filter(\.title != "work" ).all()

查询id大于5的记录

try Todo.query(on: req).filter(\.id > 5 ).all()

查询id小于5的记录

try Todo.query(on: req).filter(\.id < 5 ).all()

查询id大于等于5的记录

try Todo.query(on: req).filter(\.id >= 5 ).all()

查询id小于等于5的记录

try Todo.query(on: req).filter(\.id <= 5 ).all()

查询id为 5、7、11的数据

try Todo.query(on: req).filter(\.id ~~ [5, 7, 11]).all()

查询id不为 5、7、11的数据

try Todo.query(on: req).filter(\.id !~ [5, 7, 11]).all()

模糊查询

查询title前缀为‘work’的数据

Todo.query(on: req).filter(\.title, .like, "work%").all()

查询title后缀为‘work’的数据

Todo.query(on: req).filter(\.title, .like, "%work").all()

查询title包含‘work’的数据

Todo.query(on: req).filter(\.title, .like, "%work%").all()

如果只查询一条数据

Todo.query(on: req).filter(\.title, .like, "%work%").first()

如果什么都查不出来,则会返回nil

其实模糊查询也有提供类似于 ==, !=, >, <这样的操作符,但是经过测试并没有达到比较好的效果,所以这里的demo只是展示目前我测试可用的方法,如果未来能够支持的更好,我会再补充。

更多查询方法:

《SQL》库中的GenericSQLBinaryOperator包含sql中需要的操作符,这里贴出部分源码一看究竟:

/// See `SQLSerializable`.
    public func serialize(_ binds: inout [Encodable]) -> String {
        switch self {
        case ._add: return "+"
        case ._bitwiseAnd: return "&"
        case ._bitwiseOr: return "|"
        case ._bitwiseShiftLeft: return "<<"
        case ._bitwiseShiftRight: return ">>"
        case ._concatenate: return "||"
        case ._divide: return "/"
        case ._equal: return "="
        case ._greaterThan: return ">"
        case ._greaterThanOrEqual: return ">="
        case ._lessThan: return "<"
        case ._lessThanOrEqual: return "<="
        case ._modulo: return "%"
        case ._multiply: return "*"
        case ._notEqual: return "!="
        case ._subtract: return "-"
        case ._and: return "AND"
        case ._or: return "OR"
        case ._in: return "IN"
        case ._notIn: return "NOT IN"
        case ._is: return "IS"
        case ._isNot: return "IS NOT"
        case ._like: return "LIKE"
        case ._glob: return "GLOB"
        case ._match: return "MATCH"
        case ._regexp: return "REGEXP"
        case ._notLike: return "NOT LIKE"
        case ._notGlob: return "NOT GLOB"
        case ._notMatch: return "NOT MATCH"
        case ._notRegexp: return "NOT REGEXP"
        }
    }

我们可以在filter中选择适用的操作符。

Order

查询结果可以给一个order进行升序和降序排序

Todo.query(on: req).sort(\.id, .ascending).all()
Todo.query(on: req).sort(\.id, .descending).all()

Union?

目前vapor3配套的Flunt3还没有union功能,并且官方文档和源代码中并未找到union相关的方法,相信不久就会添加相关的特性,还是耐心等待,如果一旦添加了这样的方法,我会第一时间更新。

总结

vapor3的查询还是和vapor2有着比较大的差异的,之前vapor2的一个Model一统江湖的特点在vapor3上也没有得到体现,目前各个子模块也都还在开发中,希望会越来越好用吧。