You're viewing the readable version of this site. The interactive extras (search, diagrams, read-aloud) need JavaScript and a current browser. Enable JavaScript; if it is already enabled, update your browser.

Learn · The Art of Not Looking

budding

The Back of the Book

An index does not make reading faster. It performs a different computation: turning documents inside out so a query can begin with the word instead of the page.

search, information-retrieval, inverted-index, algorithms, data-structures, learn

Knowledge is of two kinds. We know a subject ourselves, or we know where we can find information upon it.

— Samuel Johnson, in James Boswell's Life of Johnson, 18 April 1775

This is the first chapter in a book about search from first principles. You will build the smallest useful text index by hand, state the relation it transposes, trace its construction algorithm, prove why its postings stay correct, and count the work saved on three queries. You will also reject four tempting substitutes: scanning faster, storing only a vocabulary, sorting whole documents, and beginning with embeddings. The next chapter will expose the contract hidden inside the apparently simple phrase “the same word.”

The bargain at the back

Open a nonfiction book near its final pages. The index does not say:

page 12  fox, field, tracks
page 41  index, pages, words
page 73  evidence, search

That would merely repeat the book's forward direction: page first, words second. The actual index reverses the question:

evidence  73
field     12, 19
fox       12, 19
index     41, 88

Now a reader who starts with fox can jump directly to two pages. The book was not scanned more quickly. A different relation was prepared in advance.

Inverted index — a dictionary from terms to the documents or positions in which they occur. It inverts the forward relation from documents to their terms. The opening chapters of Manning, Raghavan, and Schütze develop the standard form.

Here is the corpus that will travel through the book. It is deliberately small enough to fit on one sheet of paper.

ID Title Body
1 A Field Guide a quick fox crosses the quiet field
2 Fox Tracks a fox leaves tracks across a field
3 The Back of the Book an index maps words to pages
4 Finding a Phrase word order turns a match into a phrase
5 Search for Evidence machines retrieve evidence before answering
6 The Patient Reader a reader searches the index before scanning pages
7 Rare Words rare words can carry more evidence
8 Fast Answers a bound proves which candidates cannot win

For the moment, lowercase body words separated by spaces are the terms. Titles will matter later, but not yet. Under that intentionally crude rule, the corpus contains 54 term occurrences.

Before reading on, predict the index entry for field.

Prediction — turn the collection around. Choose the complete postings list for field before constructing the index.

The transpose

Write the collection as a binary relation

RD×T, R \subseteq D \times T,

where (d,t)R(d,t) \in R means that document dd contains term tt. A forward view fixes a document and asks for its terms:

R(d)={tT:(d,t)R}. R(d) = \{t \in T : (d,t) \in R\}.

An inverted view fixes a term and asks for its documents:

R1(t)={dD:(d,t)R}. R^{-1}(t) = \{d \in D : (d,t) \in R\}.

Nothing has been approximated. The same pairs are grouped by their other coordinate. In a matrix picture, documents are rows, terms are columns, and indexing transposes the useful access direction.

Transpose — exchange the two coordinates of a relation: every pair (d,t)(d,t) becomes (t,d)(t,d). This is the same operation that reflects a matrix across its main diagonal. Learn more.

The physical index has two parts:

  • the dictionary stores each distinct term and finds its list;
  • a postings list stores the increasing document IDs for one term.

Posting — one recorded occurrence in an inverted index. In the basic document-level index it is a document ID associated with a term; later it may also carry frequency, positions, fields, or a score contribution. Learn more.

For part of the corpus, the result is:

Term Postings
evidence 5, 7
field 1, 2
fox 1, 2
index 3, 6
pages 3, 6
rare 7
words 3, 7

The dictionary answers “does this term exist, and where is its list?” The list answers “which documents contain it?” Keeping those responsibilities separate will matter when the dictionary becomes an automaton and the postings become compressed integer sequences.

An inverted index is not a faster way to read every document. It is the transposed relation from terms to locations, prepared once so each later query can begin at the term and avoid documents that cannot match.

Build it once

The smallest construction algorithm emits one pair for every term occurrence, sorts the pairs, and groups equal terms. Let TT be the number of term occurrences in the collection.

Pair-sort construction of a document-level inverted index

BUILD-INVERTED-INDEX(D)
Input:  documents D[1:n], each represented by its analyzed terms
Output: dictionary mapping each term to its sorted postings list

pairs  empty sequence
for each document d in D
    seen  empty set
    for each analyzed term t in d
        if t is not in seen
            append (t, d.id) to pairs
            add t to seen
sort pairs by (term, document ID)
index  empty dictionary
for each maximal run r in pairs with the same term
    index[r.term]  document IDs from r
return index

The per-document seen set prevents repeated words from duplicating a document ID in this basic Boolean index. A later ranking chapter will retain frequency instead of discarding it.

The algorithm's dominant generic step is sorting at most TT pairs, for O(TlogT)O(T \log T) comparisons and O(T)O(T) stored pairs. The final grouping pass is linear. This is a teaching construction, not the last word: large indexers use blocked sorting or single-pass in-memory indexing so the collection need not fit in memory. The relation and the correctness argument remain the same.

Why is each output list correct? Keep this invariant while scanning a sorted run for term tt:

After consuming the first jj pairs of tt's run, the output contains exactly the document IDs from those jj pairs, once each, in increasing order.

It is true before the first pair because both sets are empty. The next pair has the same term, its document ID is not duplicated because construction emitted at most one (t,d)(t,d) pair per document, and sorting makes it no smaller than the previous ID. Appending it preserves exact membership, uniqueness, and order. When the run ends, construction has emitted a pair for every document that contains tt, so the list is exactly R1(t)R^{-1}(t).

This proof is modest, but it buys three later algorithms. Intersection can rely on increasing IDs. Gap encoding can rely on nonnegative differences. A query can rely on absence from the list meaning absence from the document under the same analysis rule.

Count before timing

Suppose a query asks for one exact term. A scan inspects all 54 term occurrences, even when only two documents can match. The inverted lookup reads the dictionary entry and then only that term's postings.

Query Term inspections under a full scan Posting entries reached
index 54 2: documents 3 and 6
fox AND field 54 4 before intersection: two per list
evidence 54 2: documents 5 and 7

These are exact operation counts for this corpus, not wall-clock claims. A hash lookup, binary search, compressed decode, cache miss, and sequential text scan do not cost the same number of nanoseconds. The count establishes the source of the advantage: query work depends on the selected lists rather than the whole collection. Later chapters will time complete paths and report distributions.

For a single-term query tt, write NN for all indexed term occurrences and df(t)df(t) for the number of documents containing tt. The simplified contrast is

Let L(t)L(t) be the cost of locating tt in the dictionary. Then

S(t)=Θ(N) S(t) = \Theta(N)

for the scan, while

I(t)=L(t)+Θ(df(t)) I(t) = L(t) + \Theta(df(t))

for the indexed path.

If nearly every document contains tt, the postings list is long and the gap narrows. If tt is rare, the index rejects most of the collection without touching it. This is why the distribution of language—not merely collection size—governs search cost.

Commit — identify what the index actually saved. The right answer distinguishes a changed computation from a faster implementation of the old one.

Four attractive wrong turns

“Just scan faster”

Vectorized substring search, memory mapping, and parallel readers can make a scan excellent. They do not change its dependence on collection size. Scanning is often the right baseline for a tiny collection, a one-time query, or an update-heavy stream whose index would cost more than it saves. It becomes the wrong architecture when many queries repeatedly ask the inverse question.

“Store the vocabulary”

A set of distinct terms can answer whether evidence exists. It cannot say where. The missing information is precisely the postings relation. A dictionary without postings is useful for validation or completion, but it is not a document retrieval index.

“Sort the documents”

Sorting documents by title or by their first term gives one useful order. A document contains many terms, so no single document order places every term's matches together. The index sorts the pairs (t,d)(t,d), allowing one document to participate in many term groups.

“Begin with meaning”

An embedding can place similar texts near one another even when they share no words. That is valuable for a different question. It does not remove the need for exact names, identifiers, quoted phrases, filters, explanations, or a deterministic fallback. Beginning there also hides the simpler baseline against which extra recall and cost must be measured. This book earns semantic retrieval after exact retrieval is understood.

The lesson is not that these techniques are bad. Each answers another question. The design error is adopting one before naming the relation the query needs.

Where the simple index stops

Our handmade index has already smuggled in several decisions:

  • Index in a title and index in a body are, under the rule stated above, different inputs because titles were ignored.
  • search and searches are different terms.
  • punctuation, accents, compounds, and languages other than English have no rule yet.
  • repeated terms lose their frequency.
  • word order disappears, so phrases cannot be verified.
  • a document ID says nothing about whether the current reader may open it.

None is a cosmetic detail. Index construction and query processing must agree on what a term is, or a query can ask for a key the index could never have written. Authorization has an even harder boundary: inaccessible documents must be removed before ranking, snippets, counts, or timing can reveal them. Those concerns get their own chapters because hiding them inside “tokenize the text” makes a small demo look correct while the real system is not.

Lessons

  • A forward collection maps each document to its terms; an inverted index groups the same relation by term and maps each term to its documents.
  • The dictionary locates a term's postings list. The postings list records the increasing document IDs that contain the term.
  • Pair-sort construction emits document–term pairs, sorts them, and groups equal terms. Its simple form costs O(TlogT)O(T \log T) comparisons for TT term occurrences.
  • The grouping invariant proves exact membership, uniqueness, and increasing order—properties later query and compression algorithms rely on.
  • Exact work counts explain the win before timing does: a query reaches selected postings rather than re-reading the whole collection.
  • Scanning, a vocabulary alone, one global document order, and embeddings all answer nearby questions. None substitutes for naming and materializing the term-to-document relation.

Practice

Retrieval — reconstruct the data structure from memory.
Discrimination — choose the collection that should remain a scan.
Transfer — recognize the same transpose outside text.

The first machine was actually two

We pretended that spaces reveal terms and lowercase letters make them equal. Real text refuses both assumptions. The constructor might store search while the query produces searches; one side might preserve an accent while the other folds it; a language may not use spaces between words at all.

The next chapter splits the search engine into two machines—one that analyzes documents and one that analyzes queries—and gives them a law: equivalent input must become the same term on both sides. The index is only as truthful as that agreement.

References

  1. Boswell. “The Life of Samuel Johnson, LL.D..” 1791. — primary public-domain source for the epigraph and its library context
  2. Manning, Raghavan, Schütze. “Introduction to Information Retrieval.” Cambridge University Press, 2008. — chapters 1, 2, and 4 develop inverted indexes, postings, analysis, and scalable construction
  3. Zobel and Moffat. “Inverted Files for Text Search Engines.” ACM Computing Surveys, 2006. — comprehensive account of core inverted-file representation, construction, query processing, and extensions