Vapor 4不区分大小写查询

5
在Vapor 3中,您可以使用带有SQLiteBinaryOperator的filter方法,因此可以创建具有like操作符的查询。我正在尝试在Vapor 4中完成完全相同的操作,但找不到可用于此目的的内容。
这是我的代码。
func queryUserMovies(_ req: Request) throws -> Future<[Users]> {
    let title = req.parameters.get("title")!
    return Movies.query(on: req.db).filter(\.$title == title).first().unwrap(or:Abort(.notFound, reason: "There's no movie")).flatMap{ movie in
        return movie.$users.query(on: req.db).all()
    }
}

Vapor 3

func queryUserMovies(_ req: Request) throws -> Future<[Users]> {
    guard let movie = req.query[String.self, at: "movie"] else {
        throw Abort(.badRequest, reason: "Not such movie")
    }
    return Movies.query(on: req).filter(\.title, .like, movie).first().unwrap(or:Abort(.notFound, reason: "There's no movie")).flatMap{ movie in
        return movie.users.query(on: req).all()
    }
}

Vapor 4中是否有类似的功能,还是我需要在SQL中执行原始查询?

4个回答

12

Vapor 4中的等效方式为:

func queryUserMovies(_ req: Request) throws -> Future<[Users]> {
    let title = try req.query.get(String.self, at: "title")
    return Movies.query(on: req.db)
        .filter(\.$title, .custom("ilike"), title)
        .first()
        .unwrap(or:Abort(.notFound, reason: "There's no movie"))
        .flatMap{ movie in
            return movie.$users.query(on: req.db).all()
    }
}

你甚至可以进行更广泛的搜索,以查找包含该标题的任何内容:

.filter(\.$title, .custom("ilike"), "%\(title)%")

1
这个程序可以安全地与用户输入的标题一起使用吗?例如,防止 SQL 注入? - swift-lynx
2
是的,关于为什么,请参见此答案 - 0xTim
1
在 SQLite 上使用 .custom("like") - Klaas

2

我刚遇到了同样的问题,但还多了一个障碍!

这个设置涉及到一个机器关系和一个类别关系的连接,我想用一个表达式在Machine.nameCategory.name中搜索一个或多个搜索词的出现。

(MachineCategory关系都有一个name属性。)

search[String.SubSequence]类型,因此我们可以迭代多个搜索词关键字,这些关键字必须全部出现在名称中的某个位置。

我的解决方法是:

return Machine
  .query(on: req.db)
  .join(Category.self, on: \Category.$id == \Machine.$category.$id)
  // For each search-term the must be at least one fit with Machine.name and/or Category.name
  .group(.and) {
    var result = $0
    for term in search.map({ "%\(String($0))%" }) {
      // One or both must fit the search-term ...
      result = result.group(.or) {
        $0
          // Does the Machine name fit?
          .filter(\Machine.$name, .custom("ilike"), term)
          // Does the Category.path name fit?
          .filter(
            DatabaseQuery.Field.path(
              Category.path(for: \Category.$name),
              schema: Category.schema
            ),
            DatabaseQuery.Filter.Method.custom("ilike"),
            DatabaseQuery.Value.bind(term)
          )
      }
    }
  }

正如你所看到的,有两个.group(...)函数。外部组(.or)表示"每个搜索词必须有一个匹配的内部组规则",而内部组(.or)表示"必须至少有一个匹配的Machine.name或Category.name"。
由于"joined-relation-filter"不支持.custom("ilike"),我使用了我在这里找到的解决方法。
虽然问题可能已经得到了回答,但我花了一些时间来弄清楚这个问题,并想在这里分享一下。

1

有一个包含运算符~~可以用于此目的:

Movie.query(on: req).filter(\.$title ~~ movie)

它是不区分大小写的。

1
我正在使用 ~~,但它对我来说不是不区分大小写的。 - LinusGeffarth
1
@LinusGeffarth,我在使用MySQL,这可能是底层数据库的原因吗?我还没有在Fluent中找到实现方式。刚刚检查了我的代码,它没有做任何折叠等操作,但是正常工作。 - Nick
1
也许是的,我正在使用PostgreSQL。我发现Vapor 3.0.0版本的0xTim的答案对我有用,而这个运算符不幸地没有用。 - LinusGeffarth

0
如果你想进行大小写不敏感的排序,你可以这样做:
Movie.query(on: req).sort(.custom("lower(title)"))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接