Topic 44

Document Databases

NoSQL

The festival-history problem from the last page — every film's extra information a different shape — is exactly what a document database is built for. Instead of rows in a table with fixed columns, it stores each record as a self-contained document that carries whatever it happens to have. One film's document lists three awards; another's has none and simply leaves the awards out. No wall of empty columns, no fight.

MongoDB is the household name here. The trade — because there is always a trade — is that the work JOINs did for free in the relational world now falls to the application. This page shows the document, the shape of working with it, and exactly where that trade bites.

What a Document Is

A document is written in a format called JSON, and it is friendlier than it sounds. JSON is just name-value pairs: a name like title, then its value like "Night Bus". Values can be simple (a word, a number), or they can nest — a value can itself be a little bundle of more name-value pairs, or a list of them. That's the whole format: pairs, nesting, lists.

Here is Night Bus as a document, festival history and all:

A film as a JSON document — a non-relational specimen, read for shape not syntax
{
  "title": "Night Bus",
  "runtime_min": 98,
  "rating": "PG",
  "awards": [
    { "festival": "Northlight", "year": 2024, "prize": "Audience" },
    { "festival": "Harbor",     "year": 2024, "prize": "Editing" }
  ]
}

Now picture Paper Lanterns stored beside it, with no awards at all — the key simply isn't there. In a table that absence would be a NULL in an awards column that exists for every film. In a document store there is no such column; each film's document has exactly the keys that film needs, and nothing else. That single difference — the shape belongs to the document, not to the whole collection — drives everything else on this page.

Reading and Writing

You still ask questions, just not in SQL. A document store has its own query style — here is MongoDB's flavor of "family-friendly films", shown only for its shape:

MongoDB-style query — one dialect, read for shape, don't memorize
db.films.find({ rating: "PG" })

You name a field and the value you want, and matching documents come back whole. Updates reach inside a document to change one field or push a new award onto the list. It is a different language with the same underlying job — find records, change records — so the mental muscle you built querying tables transfers; only the surface changes.

Where the JOIN Went

Here is the heart of it. In the relational world, a booking pointed at a customer and a screening, and a JOIN followed the pointers. A document store gives you two choices instead, and neither is free.

You can embed: put the screenings right inside the film's document, nested like the awards. Reading a film then hands you its screenings in one grab — fast and whole. But embedded data can only be reached from the outside in: ask "which screenings play on screen 1 across all films?" and you must open and scan every film document, because the screenings have no life of their own. Or you can reference: store screening ids inside the film and keep screenings as their own documents — which is pointing, exactly like foreign keys, and means the application does the join by hand, fetching the second set itself.

If that tension sounds familiar, it should: it's the Chapter 6 normalization debate — store it together or store it once and point — relocated into the document world. You already own the reasoning. What changed is that the database no longer decides for you; you bet on your read patterns up front.

The Night Bus document as a nested tree — the shape belongs to the film
Document
Night Bus — title, runtime_min, rating
Nested list
awards: [ … ]
Each award
festival, year, prize

Where It Shines, Where It Hurts

Document stores shine on catalog-like, read-mostly, shape-varying data — product listings, articles, user profiles, film metadata — where each item is naturally self-contained and you usually want the whole thing at once. That's a large slice of the web, which is why this is the second-most-common database family in the wild.

They hurt exactly where the Marquee's core lives: many-to-many webs. Bookings connect many customers to many screenings, and there is no clean document to put a booking "inside" — it belongs to both sides at once. And the strict all-or-nothing bracket from Chapter 7 classically covers only a single document at a time; modern document databases do offer multi-document transactions, but with real costs, so the honest statement is that their default posture is per-document, where relational databases start from all-of-it. Give up the free join and the easy cross-record guarantee, and you gain the flexible shape. That's the trade.

Common Confusions
  • "A document is just a row with extra steps." A row's shape is declared once for the whole table; a document's shape is its own. That single difference is the source of every strength and every weakness on this page.
  • "Embedding is always faster, so embed everything." Embedded data can't be reached from the other direction — "all screenings across all films" means scanning every document. Embedding is a bet on which way you'll read, not a free win.
  • "MongoDB has no transactions." Modern versions have multi-document transactions, at a real cost. The honest line is that the default posture is per-document, not that the guarantee is missing entirely.
  • "Document stores replaced relational ones." They took a slice — the catalog-shaped, read-mostly slice — and left the many-to-many, transaction-heavy core to relational databases. Both families are thriving because they're good at different jobs.
Why It Matters
  • Document stores are the second-most-common database family you'll meet, and JSON is everywhere in modern work — reading a document on sight is now basic literacy.
  • The embed-versus-reference choice is the document world's version of normalization. You already own the reasoning from Chapter 6; here it just wears different clothes.

Knowledge Check

One film's document has an awards list and another's has no awards key at all. How is that possible?

  • Each document has its own shape, so it can omit keys it doesn't need
  • The second film secretly stores an empty awards value behind the scenes
  • The database added an awards column that is NULL for the second film
  • Documents in the same collection must all have identical keys

You embed a film's screenings inside its document. What becomes hard?

  • Reading a single film together with all its screenings
  • Asking a question about screenings across all films
  • Adding a new screening to a single film's document
  • Storing the film's title and runtime in the first place

Which of the Marquee's data would fit a document database worst?

  • The films, each with their own widely varying festival histories
  • The bookings, which connect many customers to many screenings
  • A single customer's name, email, and join date
  • A film's list of festival awards

The embed-versus-reference choice in a document store is really a familiar debate. Which one?

  • Whether or not to add an index for speed
  • Chapter 6's normalization: store together, or store once and point
  • Chapter 7's transactions: whether changes are all-or-nothing
  • Chapter 9's injection: whether user input is glued or parameterized

You got correct