根据条件从数组中删除完整的文档或元素

时间:2018-05-14 08:04:22

标签: mongodb pymongo

我的收藏文件是:

    {
      "_id" : 1,
      "fruits" : [ {"name":"pears"},
                   {"name":"grapes"},
                  {"name":"bananas"} ],
    }
    {
      "_id" : 2,
      "fruits" : [ {"name":"bananas"} ],
    }

当水果只包含“香蕉”时,我需要删除整个文件,或者当水果阵列中有多个水果时,我只需要去除水果“香蕉”。

运行必需查询后的最终收藏应为:

{
  "_id" : 1,
  "fruits" : [ {"name":"pears"},
                   {"name":"grapes"}],
}

我目前正在使用两个查询来完成此任务:

db.collection.remove({'fruits':{$size:1, $elemMatch:{'name': 'bananas'} }}) [this will remove the document when only one fruit present]

db.collection.update({},{$pull:{'fruits':{'name':'bananas'}}},{multi: true}) [this will remove the entry 'bananas' from the array]

有没有办法将这些组合成一个查询

编辑:最后拍摄

- 我猜没有“一个查询”来执行上述任务,因为两个动作的意图非常不同。

- 可以执行的最佳操作是:将操作添加到bulk_write查询中,该查询可以保存在网络I / O上(如Neil的答案所示)。当您有多个此类操作被触发时,这被认为更有益。此外,bulk_write可以提供锁定功能,因为bulk_write的“有序”模式使操作顺序,中断并暂停执行,以防出错。

因此,当执行的操作需要按顺序执行时,bulk_write会更有用。有点像JS中的“链接”。还可以选择执行无序的bulk_writes。

此外,批量写入中指定的操作在collection level as individual actions.

上运行

2 个答案:

答案 0 :(得分:2)

你基本上希望bulk_write()在这里做到这两点。同时使用$exists确保只有一个元素:

from pymongo import UpdateMany, DeleteMany

db.collection.bulk_write(
  [
   UpdateMany(
    { "fruits.1": { "$exists": True }, "fruits.name": "bananas" },
    { "$pull":{ 
      'fruits': { 'name':'bananas' }
    }}
   ),
   DeleteMany(
    { "fruits.1": { "$exists": False }, "fruits.name": "bananas" }
   )
  ],
  ordered=False
)

你真的不需要$elemMatch用于"一个"条件,您应该使用update_many(),在这种情况下UpdateMany()而不是{ "multi": true }。这个选项在" pymongo"中是不同的。无论如何。当然,对于"批量"有delete_many()DeleteMany()。上下文。

批量操作通过一个响应发送一个请求,这比发送多个请求更好。另外"更新"和"删除"是两个不同的东西,但单个请求可以像这样组合。

$size运算符有效,但$exists可以应用于"范围" $size不能的地方,所以它通常更灵活。

,就像$exists范围示例

一样
# Array between 2 and 4 elements
db.collection.update_many(
  { 
    "fruits.1": { "$exists": True },
    "fruits.4": { "$exists": False }, 
    "fruits.name": "bananas"
  },
  { "$pull":{ 
    'fruits': { 'name':'bananas' }
  }}
)

当然,在这里的上下文中,你实际上想知道数组中其他可能的东西和只有"只有"单个"香蕉"。

这里的ordered=False实际上指的是两种不同的方式"批量写"请求可以处理

  • 有序 - True("默认"),然后以"序列顺序"执行操作。因为它们出现在使用"批量写入"发送的操作数组中。如果此处发生任何错误,则批处理将在错误点停止执行并返回异常。

  • UnOrdered - False操作在" parallel"中执行在合理的服务器限制范围内。如果发生任何错误,仍然会引发异常,但这并不会阻止"批量写入"从完成。使用"数组索引"返回任何错误。从提供给命令的列表中导致错误的操作。

此选项可用于"调整"期望的行为,特别是错误报告和延续,并且还允许一定程度的并行性"执行" serial"实际上并不需要操作。由于这两个陈述实际上并不依赖于其中一个,并且实际上会选择不同的文档,因此ordered=False可能是效率方面更好的选择。

答案 1 :(得分:0)

db.users.aggregate(
 [{
        $project: {
           data: {
                $filter: {
                    input: "$fruits",
                    as: "filterData",
                    cond: { $ne: [ "$$filterData.name", 'bananas' ] }
                  }
                }
        }
    },
     {
        $unwind: {
            path : "$data",
            preserveNullAndEmptyArrays : false 
        }
    },
     {
        $group: {
        _id:"$_id",
        data: { $addToSet: "$data" }
        }
    },
])

我认为上面的查询可以给你完美的结果