如果字段不存在,则更新 MongoDB 文档

Updating a MongoDB document if field doesn't exist

每当我使用新字段更新 insert_one 时,我必须始终删除集合中的旧帖子。我知道有使用 update_many 更新此类字段的手动方法,但我知道它效率低下。

例如:

posts.insert_one({
    "id": random.randint(1,10000)
    "value1": "value1",
    "value2": "value2"
})

我使用下面的代码来检查文档是否存在。 这对一个字段有何作用?

if posts.find({'id': 12312}).count() > 0:

我知道我可以轻松覆盖以前的数据,但我知道人们不会喜欢每隔一个月就擦除一次数据。

有没有办法将字段添加到 Python 中的文档?

How would this work for a field?

您可以使用$exists检查文档中是否存在某个字段。

在你的情况下,你可以将它与 find

结合起来
find({ 'id':1, "fieldToCheck":{$exists:"true"}})

它将 return 文档,如果它存在 id = 1, fieldToCheck is present in doc with id = 1

您可以跳过 id=1,在这种情况下,它将 return 所有 fieldToCheck 存在的文档

Is there a way to add the field to a document in Python?

您可以将 update 与新字段一起使用,如果存在则将更新,否则将插入。

update({"_id":1}, {field:"x"})

如果 field 存在,它将设置为 x 否则它将添加 field:x

提防update options like multi, upsert

是的,您可以使用 mongoDB shell 中的更新命令来做到这一点。检查 here

这是要使用的命令...

db.collection.update({},{$set : {"newfield":1}},false,true)

以上将适用于 mongoDB shell。如果不存在,它将在所有文档中添加新字段。

如果要使用Python,请使用pymongo。

对于 python,以下命令应该有效

db.collection.update({},{"$set" : {"newfield":1}},False, True)

感谢 john 的回答,我制作了一个完整的解决方案,无需 运行 即可自动更新文档,这意味着您无需更新非活动文档。

import datetime
import pymongo

database = pymongo.MongoClient("mongodb://localhost:27017")  # Mongodb connection
db = database.maindb  # Database
posts = db.items  # Collection within a database


# A schema equivalent function that returns the object
def user_details(name, dob):
    return {
        "username": name,  # a username/id
        "dob": dob,  # some data
        "level": 0,  # some other data
        "latest_update": datetime.datetime.fromtimestamp(1615640176)
        # Must be kept to ensure you aren't doing it that often
    }


# The first schema changed for example after adding a new feature
def user_details2(name, dob, cake):
    return {
        "username": name,  # a username/id
        "dob": dob,  # Some data
        "level": 0,  # Some other data
        "cake": cake,  # Some new data that isn't in the document
        "latest_update": datetime.datetime.utcnow()  # Must be kept to ensure you aren't doing it that often
    }


def check_if_update(find, main_document,
                    collection):  # parameters: What you find a document with, the schema dictionary, then the mongodb collection
    if collection.count_documents(find) > 0:  # How many documents match, only proceed if it exists

        fields = {}  # Init a dictionary

        for x in collection.find(find):  # You only want one for this to work
            fields = x

        if "latest_update" in fields:  # Just in case it doesn't exist yet
            last_time = fields["latest_update"]  # Get the time that it was last updated
            time_diff = datetime.datetime.utcnow() - last_time  # Get the time difference between the utc time now and the time it was last updated
            if time_diff.total_seconds() < 3600:  # If the total seconds of the difference is smaller than an hour
                print("return")
                return

        db_schema = main_document  # Better naming
        db_schema["_id"] = 0  # Adds the _id schema_key into the dictionary

        if db_schema.keys() != fields:
            print("in")

            for schema_key, schema_value in db_schema.items():

                if schema_key not in fields.keys():  # Main key for example if cake is added and doesn't exist in db fetched fields
                    collection.update_one(find, {"$set": {schema_key: schema_value}})

                else:  # Everything exists and you want to check for if a dictionary within that dictionary is changed
                    try:
                        sub_dict = dict(schema_value)  # Make the value of it a dictionary

                        # It exists in the schema dictionary but not in the db fetched document
                        for key2, value2 in sub_dict.items():
                            if key2 not in fields[schema_key].keys():
                                new_value = schema_value
                                new_value[
                                    key2] = value2  # Adding the key and value from the schema dictionary that was added
                                collection.update_one(find,
                                                      {"$set": {schema_key: new_value}})

                        # It exists in the db fetched document but not in the schema dictionary
                        for key2, value2 in fields[schema_key].items():
                            if key2 not in sub_dict.keys():

                                new_dict = {}  # Get all values, filter then so that only the schema existent ones are passed back
                                for item in sub_dict:
                                    if item != key2:
                                        new_dict[item] = sub_dict.get(item)
                                collection.update_one(find, {"$set": {schema_key: new_dict}})

                    except:  # Wasn't a dict
                        pass

            # You removed a value from the schema dictionary and want to update it in the db
            for key2, value2 in fields.items():
                if key2 not in db_schema:
                    collection.update_one(find, {"$unset": {key2: 1}})
    else:
        collection.insert_one(main_document)  # Insert it because it doesn't exist yet


print("start")
print(posts.find_one({"username": "john"}))

check_if_update({"username": "john"}, user_details("john", "13/03/2021"), posts)

print("inserted")
print(posts.find_one({"username": "john"}))

check_if_update({"username": "john"}, user_details2("john", "13/03/2021", "Lemon drizzle"), posts)

print("Results:")
print(posts.find_one({"username": "john"}))

它可以作为 gist