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 将在 Query
和 DBIO
(操作)之间出现类型不匹配。
因为您要 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行数改变了。
我在 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 将在 Query
和 DBIO
(操作)之间出现类型不匹配。
因为您要 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行数改变了。