【问题标题】:Doctrine ODM(mongo) - upsert embeded document if not existDoctrine ODM(mongodb) - 如果不存在则插入嵌入文档
【发布时间】:2016-08-04 04:53:07
【问题描述】:

存在一个链接用户和汽车的 mongo 文档。它包含以下字段:

  • User.userId
  • User.cars[]
  • User.updated
  • User.created

User.cars 是一个嵌入文档的数组。如果查询中不包含 ID 为 $carId 的 Car,则需要编写查询以仅将 Car 插入该字段

以下查询每次都会为 $userId 和 $carId 创建一条新记录。它应该做的是要么不插入新记录,要么更新驱动的值。

    $qb
        ->findAndUpdate()
        ->upsert(true)
        ->field('userId')->equals($userId)
        ->field('cars.id')->notEqual($carId)
        ->field('cars')->addToSet([
                'id' => $carId,
                'driven' => new \DateTime(),
        ])
        ->field('updated')->set(new \DateTime())
        ->field('created')->setOnInsert(new \DateTime())
        ->limit(1)
    ;
    return $qb->getQuery()->execute();

从查询中删除 notEqual($carId) 将始终将记录插入到 User.cars 中,这也是不希望的。

最终结果应该是 User.cars 每个 carId 只包含一条记录。

【问题讨论】:

  • 如果元素匹配,您是否还想更新数组中的"driven"?使用$addToSet 有点多余,如果事实上你有一部分“唯一”(“id”)和另一部分变量(“驱动”值),因为“驱动”值的任何变化都意味着一个新的入口。还应该清楚的是,数组中的$ne 检查不能与“upsert”同时使用,因为任何缺失的元素都会导致新的“文档”而不是新的数组元素。这个过程有现有的答案。你找过他们吗?
  • 是的,大多数答案建议将其作为两个查询进行。第一个更新到driven,其中userIdcars.id 匹配。如果没有发生更新,则执行第二个查询,即上面没有notEqual($carId) 的查询,但是我想修改查询以防止第二个查询运行两次并将汽车两次插入集合中的可能性。如果集合中已经存在$carId,则可以使查询失败。
  • 这并不是“建议”的情况,而是显然不可能这样做。我问是因为您的$ne 检查表明您已经看到了至少可以实现意图的东西。事实上,当与“文档更新插入”混合时,它是 三个 更新。没有其他办法了。你所读的就是你必须做的。
  • 这个答案stackoverflow.com/a/26321684/1283381 有` $ne : MERCHANTID `,用于避免并发问题。这就是我试图用notEqual($carId) 在我的查询中复制的——从你所说的来看这是不可能的?抱歉,我没有遵循您所说的三个更新的意思 - 您是说 mongo 为我的查询运行三个更新?

标签: mongodb doctrine-orm doctrine-odm


【解决方案1】:

如果您以后不需要搜索cars 的数组,您可以将其存储为对象并使用$carId 作为键:

$qb
    ->findAndUpdate()
    ->upsert(true)
    ->field('userId')->equals($userId)
    ->field("cars.$carId")->set([
            'id' => $carId,
            'driven' => new \DateTime(),
    ])
    ->field('updated')->set(new \DateTime())
    ->field('created')->setOnInsert(new \DateTime())
    ->limit(1)
;
return $qb->getQuery()->execute();

如果cars 在文档中映射为EmbedMany,您可能需要将collection's strategy 更改为setatomicSet(我建议后者)否则ODM 将重新索引您的数组保存时间。

再一次,这个解决方案有它的缺点,根据你想如何使用你的数据,可能会被认为是严重的,但它解决了相关问题。

顺便说一句,->limit(1) 是多余的,因为 ->findAndUpdate() 正在执行查询运行 findAndModify 命令,该命令本质上对单个文档进行操作。

【讨论】:

    猜你喜欢
    • 2012-12-20
    • 2012-06-11
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多