Slick:更新数据库中的列表

Slick: update List in db

我在 Postgres 中的 table 架构如下:

我将 List[String] 存储在第 2 列中,并编写了使用新列表和旧列表的联合更新此列表的工作方法:

  def update(userId: Long, unknownWords: List[String]) = db.run {
    for {
      y <- lists.filter(_.userId === userId).result
      words = y.map(_.unknownWords).flatMap(_.union(unknownWords)).distinct.toList
      x <- lists.filter(_.userId === userId).map(_.unknownWords).update(words)
    } yield x
  }

有没有更好的写法?也许这个问题很愚蠢,但我不太明白为什么我应该将 .result() 应用于 for 表达式的第一行,3d 线上的 filter().map() 链工作正常,是类型有问题吗?

为什么.result

您需要应用 .result 的原因是为了处理 Slick 中的查询(Query 类型)和操作(DBIO)之间的区别。

lists.filter 行本身就是一个查询。然而,第三行(update)是一个动作。如果您将 .result 关闭,您的 for comprehension 将在 QueryDBIO (操作)之间出现类型不匹配。

因为您要 db.run for comprehension 的结果,for comprehension 需要产生 DBIO 操作,而不是查询。换句话说,放置 .result 是正确的做法,因为您正在为数据库中的 运行 构造一个操作(即,为用户获取一些数据)。

然后您将 运行 稍后对 update 数据库执行另一个操作。因此,总而言之,您正在使用 for 将两个操作(两个 运行nable SQL 表达式)组合到一个 DBIO 中。那就是你让出的x,由db.run.

执行

更好?

这对你有用,很好。

有少量重复。您可能会在第一行发现您的查询,非常 类似于更新查询。您可以将其抽象为一个值:

val userLists = lists.filter(_.userId === userId)

这是一个查询。事实上,您可以更进一步,将查询修改为 select unknownWords 列:

val userUnknownWords = lists.filter(_.userId === userId).map(_.unknownWords)

我没有尝试编译它,但这会使您的代码类似于:

def update(userId: Long, unknownWords: List[String]) = {
  val userUnknownWords = lists.filter(_.userId === userId).map(_.unknownWords)
  db.run {
    for {
      y <- userUnknowlWords.result
      words = y.flatMap(_.union(unknownWords)).distinct.toList
      x <- userUnknownWords.update(words)
    } yield x
  }

鉴于您正在组合两个动作(一个 select 和一个更新),您可以使用 DBIO.flatMap 代替 for comprehension。你可能会发现它更清楚。或不。但这里有一个例子...

DBIO.flatMap 的参数需要是另一个动作。也就是说,flatMap 是一种对动作进行排序的方法。特别是,这是一种在使用数据库中的值时执行此操作的方法。

所以你可以 将 for comprehension 替换为:

val action: DBIO[Int] =
  userUnknowlWords.result.flatMap { currentWords =>
      userUnknownWords.update(
        currentWords.flatMap(_.union(unknownWords)).distinct.toList
      )
  }

(再次为没有编译上面的内容而道歉:我没有类型的详细信息,但希望这能让您了解代码的工作方式)。

最后的 action 是您可以传递给 db.run 的那个。它returns行数改变了。