0

I don't have enough reputation to comment and hence I have to ask this question again.

I have tried different ways to delete my dynamically changing date column as mentioned here but nothing worked for me : How to remove a field completely from a MongoDB document?

Environment_Details - OS : Windows10, pymongo : 3.10.1, MongoDB Compass App : 4.4, python: 3.6

I am trying to delete column "2020/08/24"(this date will be dynamic in my case). My data looks like this:

    [{
  "_id": {
    "$oid": "5f4e4dda1031d5b55a3adc70"
  },
  "Site": "ABCD",
  "2020/08/24": "1",
  "2020/08/25": "1.0"
},{
  "_id": {
    "$oid": "5f4e4dda1031d5b55a3adc71"
  },
  "Site": "EFGH",
  "2020/08/24": "1",
  "2020/08/25": "0.0"
}]

Commands which don't throw me any error but also don't delete the column/field "2020/08/24":

col_name = "2020/08/24"
db.collection.update_many({}, {"$unset": {f"{col_name}":1}})
db.collection.update({}, {"$unset": {f"{col_name}":1}}, False, True)
db.collection.update_many({}, query =[{ '$unset': [col_name] }])

I am always running into error while trying to use multi:True with update option.

The exact code that I am using is:

    import pymongo
def connect_mongo(host, port, db):
    conn = pymongo.MongoClient(host, port)
    return conn[db]


def close_mongo(host, port):
    client = pymongo.MongoClient(host, port)
    client.close()


def delete_mongo_field(db, collection, col_name, host, port):
    """Delete column/field from a collection"""
    db = connect_mongo(host, port, db)
    db.collection.update_many({}, {"$unset": {f"{col_name}":1}})
    #db.collection.update_many({}, {'$unset': {f'{col_name}':''}})
    close_mongo(host,port)      

        
col_to_delete = "2020/08/30"
delete_mongo_field(mydb, mycollection, col_to_delete, 'localhost', 27017)
HobbyCoder
  • 45
  • 9

2 Answers2

3

The following code worked with Python 3.8, PyMongo 3.11., and MongoDB v 4.2.8.

col_name = '2020/08/24'
result = collection.update_many( { }, { '$unset': { col_name: '' } } )
print(result.matched_count, result.modified_count)

The two documents in the post were updated and the field with the name "2020/08/24" was removed. NOTE: A MongoDB collection's document can have a field name with / character (See Documents - Field Names).


[EDIT ADD]

The following delete_mongo_field function worked for me updating the documents correctly by removing the supplied field name:

def delete_mongo_field(db, collection, col_name, host, port):
    db = connect_mongo(host, port, db)
    result = db[collection].update_many( { }, { '$unset': { col_name: 1 } } ) # you can also use '' instead of 1
    print(result.modified_count)
prasad_
  • 12,755
  • 2
  • 24
  • 36
  • I just updated the question to mention how exactly I have been using these queries by creating functions. Strangely, if I use them the way both of you suggested, it works but nothing happens if I use them in a function. Can you suggest what am I missing here? – HobbyCoder Sep 04 '20 at 10:53
  • I updated the answer with corrections in your code. It worked for me. – prasad_ Sep 04 '20 at 11:42
  • 1
    Also be careful, you are passing a string `db` parameter and then setting the `db` parameter to a pymongo database object. It's not technically wrong but your going to run into a pile of confusion if you do stuff like that. – Belly Buster Sep 04 '20 at 11:48
2

On a separate note, you might want to consider changing your data model to store the dates as values rather than keys, and also to consider storing them as native date objects, e.g.

import datetime
import pytz

db.testcollection.insert_many([
    {
        "Site": "ABCD",
        "Dates":  [
            {
                "Date": datetime.datetime(2020, 8, 24, 0, 0, tzinfo=pytz.UTC),
                "Value": "1"
            },
            {
                "Date": datetime.datetime(2020, 8, 25, 0, 0, tzinfo=pytz.UTC),
                "Value": "1.0"
            }]
    },
    {
        "Site": "EFGH",
        "Dates": [
            {
                "Date": datetime.datetime(2020, 8, 24, 0, 0, tzinfo=pytz.UTC),
                "Value": "1"
            },
            {
                "Date": datetime.datetime(2020, 8, 25, 0, 0, tzinfo=pytz.UTC),
                "Value": "0.1"
            }]
    }])

But back to you question ... the first example works fine for me. Can you try the sample code below and see if you get different results:

from pymongo import MongoClient
import pprint

db = MongoClient()['testdatabase']

db.testcollection.insert_many([{
    "Site": "ABCD",
    "2020/08/24": "1",
    "2020/08/25": "1.0"
}, {
    "Site": "EFGH",
    "2020/08/24": "1",
    "2020/08/25": "0.0"
}])
pprint.pprint(list(db.testcollection.find({}, {'_id': 0})))

col_name = "2020/08/24"
db.testcollection.update_many({}, {"$unset": {f"{col_name}": 1}})

pprint.pprint(list(db.testcollection.find({}, {'_id': 0})))

Result:

[{'2020/08/24': '1', '2020/08/25': '1.0', 'Site': 'ABCD'},
 {'2020/08/24': '1', '2020/08/25': '0.0', 'Site': 'EFGH'}]
[{'2020/08/25': '1.0', 'Site': 'ABCD'}, {'2020/08/25': '0.0', 'Site': 'EFGH'}]
Belly Buster
  • 8,224
  • 2
  • 7
  • 20
  • Thanks..It worked like a charm. Is there a specific reason for removing close_mongo function from delete_mongo_field function? – HobbyCoder Sep 04 '20 at 12:28
  • You don't need to close connections, pymongo will handle that. In fact it's better not to,as reopening them adds a bunch of overhead. – Belly Buster Sep 04 '20 at 12:42