inquisitive_one
inquisitive_one

Reputation: 1495

Pymongo: Remove an element from array

I'm trying to remove lowest price from the iPad's in my schema. I know how to find it using pymongo, but I don't how to remove it. Here's my schema:

{
    "_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
        }
    ]
}

I've got a python loop that finds the lowest sale price for 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']}}})

That doesn't work. What do I need to do to remove lowest iPad price item?

Upvotes: 2

Views: 10059

Answers (3)

anonymous
anonymous

Reputation: 11

Disclaimer: The below code is not tested as I do not have mongo installed locally. However I did take my time writing it so im pretty confident its close to working

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}}}
        )

Of course there will be a problem here if another item happens to have exactly the same price but I think it will be easy to improve from here

Upvotes: 1

Asya Kamsky
Asya Kamsky

Reputation: 42362

Your Python code isn't doing what you think it's doing (unless there is a lot of it you didn't include). You don't need to do the sorting and iterating on the client side - you should make the server do the work. Run this aggregation pipeline (I'm giving shell syntax, you can call it from your Python, of course):

> 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
}

Now you can iterate over the "r.results" array and execute your update:

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

Note that I only include records which have more than one iPad - since otherwise you may end up deleting the only iPad record in the array. If you want to delete all "non-highest" prices then you'd want to find the max and $pull all the elements $lt that price.

Upvotes: 2

viktortnk
viktortnk

Reputation: 2757

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

This doesn't look like valid json, does it?

shouldn't be "sales.update(...)" be "db.sales.update(...)" in your example?

maybe it's better to have query in update operation:

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

rather than entire doc.

and finally the update body itself might be

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

Upvotes: 0

Related Questions