【问题标题】:Pymongo: Remove an element from arrayPymongo:从数组中删除一个元素
【发布时间】:2013-10-04 08:57:43
【问题描述】:

我正在尝试从我的架构中的 iPad 中删除最低价格。我知道如何使用 pymongo 找到它,但我不知道如何删除它。 这是我的架构:

{
    "_id": "sjobs",
    "items": [
        {
            "type": "iPod",
            "price": 20.00
        },
        {
            "type": "iPad",
            "price": 399.99
        },
        {
            "type": "iPad",
            "price": 199.99
        },
        {
            "type": "iPhone 5",
            "price": 300.45
        }
    ]
}
{
    "_id": "bgates",
    "items": [
        {
            "type": "MacBook",
            "price": 2900.99
        },
        {
            "type": "iPad",
            "price": 399.99
        },          
        {
            "type": "iPhone 4",
            "price": 100.00
        },
        {
            "type": "iPad",
            "price": 99.99
        }
    ]
}

我有一个 python 循环可以找到 iPad 的最低售价:

cursor = db.sales.find({'items.type': 'iPad'}).sort([('items', pymongo.DESCENDING)])
for doc in cursor:
    cntr = 0
    for item in doc['items']:
        if item['type'] == 'iPad' and resetCntr == 0:
            cntr = 1
            sales.update(doc, {'$pull': {'items': {item['type']}}})

这行不通。我需要怎么做才能移除 iPad 价格最低的商品?

【问题讨论】:

  • 作为一般评论:“不起作用”通常是不够的信息。
  • 对不起。我没有收到错误消息。该程序成功运行,但我的架构没有任何反应。因此我的 stmt:“不起作用”。
  • 您是否有理由在 python 中进行搜索和排序(将所有文档都带过来)而不是使用聚合查询来获取每个文档 _id 中每个 iPad 项目的最低价格,然后为每个发布更新?
  • 还有什么是'resetCntr',你在哪里设置它?此外,您确实意识到您没有返回已排序的数组项,查找排序指令仅适用于顶级文档顺序。

标签: python mongodb pymongo


【解决方案1】:

您的 Python 代码并没有像您认为的那样做(除非有很多您没有包含在内)。您不需要在客户端进行排序和迭代 - 您应该让服务器完成工作。运行这个聚合管道(我给出的是 shell 语法,你当然可以从你的 Python 中调用它):

> r = db.sales.aggregate( {"$match"  : { "items.type":"iPad"} },
                          {"$unwind" :   "$items"}, 
                          {"$match"  : { "items.type":"iPad"} },
                          {"$group"  : { "_id" : "$_id", 
                                         "lowest" : {"$min":"$items.price"}, 
                                         "count":{$sum:1}
                                       }
                          }, 
                          {"$match"  : {count:{$gt:1}}}
);
{
    "result" : [
        {
            "_id" : "bgates",
            "lowest" : 99.99,
            "count" : 2
        },
        {
            "_id" : "sjobs",
            "lowest" : 199.99,
            "count" : 2
        }
    ],
    "ok" : 1
}

现在您可以遍历“r.results”数组并执行更新:

db.sales.update( { "_id" : r.results[0]._id }, 
                 { "$pull" : { "items" : { "type" : "iPad", "price" : r.result[0].lowest}}} );

请注意,我只包括具有多个 iPad 的记录 - 因为否则您最终可能会删除数组中唯一的 iPad 记录。如果您想删除所有“非最高”价格,那么您需要找到最高价格和$pull 所有元素$lt 该价格。

【讨论】:

    【解决方案2】:

    免责声明:以下代码未经测试,因为我没有在本地安装 mongo。但是我确实花时间写了它,所以我非常有信心它接近工作

    def remove_lowest_price(collection):
        cursor = collection.find({}, {'items': 1})
        for doc in cursor:
            items = doc['items']
            id = doc['_id']
            for item in items:
                lowest_price = 100000 # a huge number
                if item['type'] == 'i_pad' and item['price'] < lowest:
                    lowest = item['price']
            # lowest now contains the price of the cheapest ipad
            collection.update(
                {'_id': id},
                {'$pull': {'items': {'price': lowest}}}
            )
    

    当然,如果另一个项目恰好具有完全相同的价格,这里会有问题,但我认为从这里改进会很容易

    【讨论】:

      【解决方案3】:
      {'$pull': {'items': {item['type']}}}
      

      这看起来不像是有效的 json,是吗?

      在您的示例中,“sales.update(...)”不应该是“db.sales.update(...)”吗?

      也许在更新操作中有查询会更好:

      db.sales.update({_id: doc[_id]}, ...)
      

      而不是整个文档。

      最后更新体本身可能是

      {'$pull': {'items': {type: item['type']}}}
      

      【讨论】:

      • 这不是js也不是mongo shell,而是Python,所以这里的sales是他的集合(相当于shell中的db.sales)。问题中的更新与您建议的相同。
      猜你喜欢
      • 1970-01-01
      • 2013-05-27
      • 2018-11-16
      • 2017-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-19
      相关资源
      最近更新 更多