如何使用Mongo驱动将JS的updateMany迁移至Scala?
问题描述
我有一段可正常运行的MongoDB JS查询,作用是将$_id字段的值转为字符串后设置为idempotencyKey字段:
db.getCollection('my_collection') .updateMany({}, [{ $set: { idempotencyKey: { $convert: { input: "$_id", to: "string" } } } }] )
我尝试将其迁移到Scala代码,但未达到预期效果:
val setDefaultIdempotencyKeyExpression = """{ $set: { idempotencyKey: { $convert: { input: "$_id", to: "string" } } } }""".stripMargin getCollection("my_collection").updateMany( Filters.not(Filters.exists("idempotencyKey")), Document(setDefaultIdempotencyKeyExpression) ).toFuture() .map(_.getModifiedCount)
我不清楚如何用Scala DSL表达该JS查询,于是尝试传递原始JSON到Document,但代码直接把{"$convert": {"input": "$_id", "to": "string"}}作为字符串值赋值给了idempotencyKey,而非执行转换逻辑。另外,我无法像JS查询那样传递数组形式的更新语句,因为Document仅接受JSON对象,不支持数组格式。
请问正确的实现方式是什么?
解决方案
方法1:用Scala MongoDB DSL构建聚合管道式更新
你原来的JS查询用的是聚合管道式更新(数组形式的更新语句),Scala驱动里可以通过Aggregates工具类构建对应的管道阶段,再用Updates.pipeline()包裹这些阶段:
import org.mongodb.scala.model._ // 构建$set聚合阶段,包含$convert转换逻辑 val setIdempotencyKey = Aggregates.set( "idempotencyKey", Aggregates.convert( Field("_id"), ConvertOptions().to(ConvertType.STRING) ) ) // 执行更新:用Updates.pipeline包裹聚合阶段数组 getCollection("my_collection").updateMany( Filters.not(Filters.exists("idempotencyKey")), Updates.pipeline(setIdempotencyKey) ).toFuture() .map(_.getModifiedCount)
方法2:直接用BsonArray传递原始管道数组
如果更习惯写原始JSON,可以用BsonArray来包裹更新管道(而非Document):
import org.bson.BsonArray import org.bson.Document val pipeline = BsonArray.parse(""" [{ $set: { idempotencyKey: { $convert: { input: "$_id", to: "string" } } } }] """) getCollection("my_collection").updateMany( Filters.not(Filters.exists("idempotencyKey")), pipeline ).toFuture() .map(_.getModifiedCount)
原代码失效原因
你之前用Document传递单个$set对象时,MongoDB会将其识别为普通更新文档,而非聚合管道。此时$convert不会被解析为操作符,而是直接作为字符串值存入字段,这就是问题的核心。只有用数组形式的聚合管道,MongoDB才会执行其中的表达式逻辑。
内容的提问来源于stack exchange,提问作者Capacytron
相关产品推荐
相关产品推荐

