1

I'm using python's library for Mongo (pymongo) and my document looks like this:

{"vendor_id": 12, "title": "xyz", "price": 1499.0, "price_history": [{"date": "2019-12-01", "price": 1890.0}]}

I would like to push new price object to the "price_history" array if document with id=12 exists. If it doesn't I would create a new document that look the same as the pasted code.

It seems simple but I've checked multiple stackoverflow topics and mongodb docs and can't get it :/

I've come up with the code:

db.holidays.update_one(
            {"vendor_id": t["vendor_id"]},
            {"$push": {"price_history": t["price_history"][0]}},
            upsert=True
        )

but when document isn't found it inserts only vendor_id instead of entire document.

Any tips? Thank you for spending your time on my problem.

3
  • When you say ...look the same as the pasted code above... does that mean the new document would have the same value for the vendor_id, title, price, and other fields? Commented Dec 2, 2019 at 21:57
  • yes, I meant it would just insert an entire document into collection. I'm reading about $setOnInsert - maybe that's the way I should go? Commented Dec 2, 2019 at 22:07
  • $setOnInsert is exactly the way to go. Commented Dec 2, 2019 at 22:15

2 Answers 2

2

$setOnInsert to the rescue:

db.holidays.update(
   { "vendor_id": t["vendor_id" },   // Query parameter
   ,{                     // Update document
      "$push": {"price_history": t["price_history"][0]},
      "$setOnInsert": { everything else you want insert besides the push and the vendor_id
   }
   ,{ upsert: true }      // Options
)
Sign up to request clarification or add additional context in comments.

Comments

1

Fetch the record out into a dict and use standard python the manipulate. If you use find_one() and there's no match it will return None

from pymongo import MongoClient
from bson.json_util import dumps

db = MongoClient()["testdatabase"]

# Data setup
db.testcollection.delete_many({})
template = {"vendor_id": 12, "title": "xyz", "price": 1499.0, "price_history": []}
data_setup = {"vendor_id": 12, "title": "xyz", "price": 1499.0,
              "price_history": [{"date": "2019-12-01", "price": 1890.0}]}
new_price = {"date": "2019-12-02", "price": 2000.0}

# Comment the next line out to see what happens if the record isn't present
db.testcollection.insert_one(data_setup)

record = db.testcollection.find_one({"vendor_id": 12})
if record is None:
    record = template

record['price_history'].append(new_price)
db.testcollection.replace_one({"vendor_id": 12}, record, upsert=True)

# Pretty up the record output
print(dumps(db.testcollection.find_one({}, {'_id': 0}), indent=4))

gives:

{
    "vendor_id": 12,
    "title": "xyz",
    "price": 1499.0,
    "price_history": [
        {
            "date": "2019-12-01",
            "price": 1890.0
        },
        {
            "date": "2019-12-02",
            "price": 2000.0
        }
    ]
}

2 Comments

Thanks for the solution, I upvoted it however I think it's a bit too many requests to the db, wouldn't want to pay for this :D
It's only 2 calls once you strip out the setup. Make sure you add an index to vendor_id and it will be plenty fast unless you're making millions of updates.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.