Documents and collections with Mongita
Gregory S. DeLozier, PhD
A record has named fields and values.
Values can include lists and nested records.
A collection holds related documents.
{
"name": "Casey",
"age": 3,
"vaccinations": ["rabies", "distemper"],
"details": {"color": "brown", "weight_kg": 18}
}Scalar, list, nested document.
Ask for pets older than two.
Query the data inside the document.
| Choice | Example |
|---|---|
| Required value | Every pet needs a name |
| Consistent type | Age is a number |
| Optional field | Weight may be absent |
| Shared units | Weight is recorded in kilograms |
Flexibility leaves us with design decisions.
| Milestone | Idea |
|---|---|
| Lotus Notes, 1989 | Documents and forms for shared work |
| Apache CouchDB, 2008 | Document database becomes an Apache project |
| MongoDB, 2009 | Launch of a new document server |
MongoDB did not invent document databases.
2007: 10gen is founded.
2009: MongoDB launches.
Documents familiar to application programmers.
A server designed for growing applications.
MongoDB’s vocabulary travels:
collections, documents, filters, updates, ObjectIds.
Other products offer compatibility with its interface.
| System | What it provides |
|---|---|
| MongoDB | The database server |
| Amazon DocumentDB | A service with MongoDB API compatibility |
| Mongita | A local Python library with a subset of the API |
| CouchDB | A document database with its own interface |
Check the operations your application uses.
| Test | Glucose | Sodium | Body region | Report |
|---|---|---|---|---|
| Blood chemistry | 92 | 140 | - | - |
| X-ray | - | - | Left wrist | Text |
One column for every possible result?
{"test": "Blood chemistry", "patient_id": "P17",
"results": {"glucose_mg_dl": 92,
"sodium_mmol_l": 140}}
{"test": "X-ray", "patient_id": "P17",
"results": {"body_region": "Left wrist",
"report": "Report text"}}Different fields. Shared meaning for common fields.
| Record | Contains |
|---|---|
| Purchase | Receipt identifier, date |
| Purchase item | Purchase, SKU, quantity, price paid |
| Product | SKU, description, current price |
A receipt normally needs all its line items.
purchase = {
"purchase_id": "R1042",
"items": [
{"sku": "MILK-1L", "quantity": 2,
"unit_price_cents": 249},
{"sku": "BREAD-W", "quantity": 1,
"unit_price_cents": 325},
],
}One document includes the line items.
Purchase-specific line items: embed.
Shared product catalog: reference by SKU.
What changes when the query is total sales for one SKU?
animals.update_one(key, {"$set": {"age": 3}})
print(animals.find_one(key))
animals.delete_one(key)
print(animals.find_one(key)) # None
client.close()The same identifier selects the same document.
| App calls | Layer uses |
|---|---|
create_pet(data) |
insert_one() |
get_pet(id) |
find_one() |
update_pet(id, data) |
update_one() with $set |
delete_pet(id) |
delete_one() |
Keep the application’s database functions.
# Database operation returns an ObjectId.
result = pets_collection.insert_one(pet)
# Application receives its text representation.
return str(result.inserted_id)Integer row ID becomes 24-character hexadecimal text.
def get_pet(id):
object_id = _to_object_id(id, "pet id")
pet = pets_collection.find_one({"_id": object_id})
if pet is None:
return None
return pet_to_dict(pet)String in. ObjectId for the query. String out.
def update_pet(id, data):
object_id, _ = _require_existing_pet(id)
pet = _normalize_pet_data(data)
pets_collection.update_one(
{"_id": object_id}, {"$set": pet})Validate the values, then save the fields.
cd chapter-11-intro-to-mongo/code/pets-app
python -m pip install -r requirements.txt
flask --app app run --debugCreate an owner, then a pet. Follow its Update link.
Find the ObjectId string in the address.
Database operations and identifier conversion.
The app still calls the database layer.
Where would an owner check belong?