Skip to content
Part IA Michaelmas Term

TinyDB: A Document Database in Practice

Overview

TinyDB is the document database used for the 2nd Assessed Exercise (tick). It is:

  • In-core — all data held in memory; no disk persistence layer.
  • JSON-based — documents are JSON objects, not XML.
  • Queried using Python — no separate query language; queries are Python expressions.
  • No transaction support — no atomicity, consistency, isolation, or durability guarantees beyond what Python provides.

The absence of transactions makes a distributed/sharded version relatively easy to implement (though this is not done in the course).

Data model: two denormal tables

TinyDB provides two primary collections:

CollectionContents
MoviesMovie documents with embedded actors, directors, producers, writers, genres
PeoplePerson documents with embedded filmography (acted in, directed, produced)

Unstructured text fields include Goofs, Trivia, and Quotes — these are full-text searchable but have no enforced internal structure.

Structure and indexing

Data needs to be indexed on various keys. Keys must still be unique, but uniqueness is not enforced by the database — it is a convention the programmer must maintain.

Some fields are foreign keys (e.g., person_id in a cast list references a person in the People collection). Key integrity is expected but not enforced. The database would not stop you from putting a person object in the Movie collection — the collection names are just names to help the programmer, unlike SQL tables that enforce structure.

Example records

Person:

{
  "person_id": "nm0031976",
  "name": "Judd Apatow",
  "birthYear": "1967",
  "acted_in": [
    { "movie_id": "tt0118715", "title": "The Cable Guy", "role": "Director" }
  ],
  "directed": [
    { "movie_id": "tt0405422", "title": "The 40-Year-Old Virgin", "year": "2005" }
  ]
}

Movie (abbreviated):

{
  "movie_id": "tt0111161",
  "title": "The Shawshank Redemption",
  "year": "1994",
  "genres": ["Drama"],
  "actors": [
    { "person_id": "nm0000209", "name": "Tim Robbins", "role": "Andy Dufresne" }
  ],
  "directors": [
    { "person_id": "nm0001104", "name": "Frank Darabont" }
  ]
}

Querying

Queries are written in Python:

# Find a person by ID
tdb_people.get(Query().person_id == 'nm0000002')

# Find movies by year
tdb_movies.search(Query().year == '1994')

# Compound query
tdb_movies.search((Query().year == '1994') & (Query().genres.any('Drama')))

Considerations for the assessed exercise

When implementing the tick, think about:

  • Query planning: can you avoid scanning the entire collection for every query?
  • Cost of correcting a systematically misspelled actor’s name: in a document database, you must update every movie document that embeds that actor, plus the actor’s own person record. Compare with an RDBMS where a single update suffices.
  • Cost of joins: in TinyDB, joins are done by the application, not the DBMS. What is the performance implication?
  • Insertion checks: what should be validated when inserting new data? Key uniqueness? Referential integrity? Data type conformance?
  • ACID properties: which ones might be relatively easy to implement in TinyDB, and which would be hard?

Summary

  • TinyDB is an in-core, JSON-based, Python-queried document database with no transaction support.
  • Two denormalised collections: Movies and People.
  • No schema enforcement; collection names are conventions.
  • Queries are Python expressions against a Query() builder.
  • The exercise tests understanding of the trade-offs between document and relational models.