Installing & Basic Concepts — Indices, Documents & the REST API

Elasticsearch / OpenSearch

Chapter 3 · Installing & Basic Concepts — Indices, Documents & the REST API

This chapter covers the practical basics both engines share — a real, quiet demonstration of search1-2's own "shared ancestry" point, since installation and the REST API remain largely compatible at this level.

Installing — A Cluster, Even of One

Docker is the common modern way to run either engine locally. search1-1's own "distributed by design" claim becomes concrete immediately: even a single-node local install starts up internally as a cluster, not a standalone server the way mysql1 or postgres1-2 install.

Everything Is JSON Over HTTP

No client library is strictly required — plain curl works:

curl -X GET "localhost:9200"

That returns cluster info as JSON, directly. This is a genuine architectural difference from every other engine already covered: mysql1's own mysql client, postgres1-2's own psql, sqlite1-2's own sqlite3, and mongodb1's own mongosh are all dedicated client programs speaking a specific, database-specific wire protocol. Elasticsearch/OpenSearch instead exposes a REST API directly — any HTTP client at all can talk to it natively, with no database-specific driver required at the most basic level, even though official client libraries exist for convenience.

Indices — Not Quite a Table, Not Quite a Database

curl -X PUT "localhost:9200/products"

An index is the closest analog to a "table," or arguably a whole "database." It's worth naming a real terminology collision immediately: this "index" is not the same concept as a MySQL/Postgres index (the B-tree/GIN structure postgres1-7 covered, speeding up queries on an existing table). Here, "index" is the primary container itself — analogous to a table or a MongoDB collection — not a secondary structure built on top of one. Getting this vocabulary distinction right immediately avoids real confusion later.

Documents — JSON, Like MongoDB, But Indexed Differently

curl -X PUT "localhost:9200/products/_doc/1" -H "Content-Type: application/json" -d '{
  "name": "Wireless Mouse",
  "price": 24.99,
  "in_stock": true
}'

A document is a single JSON object stored inside an index — structurally similar to mongodb1-1's own document model. The key difference: every field of every document is, by default, automatically analyzed and added to the inverted index (previewed here, covered fully in search1-4) at write time — which is exactly why write throughput and query latency trade off differently here than in MongoDB, where storage and indexing remain more separable choices.

No Fixed Schema Required by Default — Dynamic Mapping

By default, both engines automatically infer a "mapping" (their own version of a schema) for a new field the first time they encounter it in a document — genuinely schema-flexible out of the box, in similar spirit to mongodb1-1's own flexible schema. There's a real, important difference worth an honest one-line flag here: once a field's mapping is inferred, it's considerably harder to change afterward than a MongoDB document's own genuinely per-document flexibility.

A Basic Walkthrough

curl -X GET "localhost:9200/products/_doc/1"

curl -X GET "localhost:9200/products/_search?q=mouse"

That final query previews search1-5's own full Query DSL chapter — this is just enough to confirm the round trip works end to end.

Dynamic mapping's own real gotcha
Since a field's type is inferred from its first-ever value, a product SKU that happens to be all-digits in its first indexed document gets permanently mapped as a numeric type — and a later document with a genuinely alphanumeric SKU value for that same field will fail to index correctly, or behave unexpectedly. This is conceptually the same category of problem sqlite1-3's own type affinity gotchas covered — "what happens when the engine has to guess a type" — though the underlying mechanism differs: SQLite stores each value's own actual type flexibly per-row, while here the mapping is inferred once and then effectively locked in for that field.
This is basic-concepts territory — depth comes next
Everything introduced here at a basic level — the inverted index, real querying, mapping in depth — gets its own full treatment in search1-4 and search1-5.

Hands-On Exercises

Exercise 1

Explain why Elasticsearch/OpenSearch's REST-API-first design is a genuine architectural difference from every other database engine already covered on this site, which all require a dedicated client program.

📄 View solution
Exercise 2

Explain the terminology collision between an Elasticsearch/OpenSearch "index" and a MySQL/Postgres "index," and why getting this vocabulary distinction right matters.

📄 View solution
Exercise 3

Using this chapter's own warn-box, explain the dynamic mapping gotcha with a concrete example (a numeric-looking SKU), and explain how this is conceptually similar to, but mechanically different from, sqlite1-3's own type affinity gotchas.

📄 View solution

Chapter 3 Quick Reference

  • No dedicated client program required — plain HTTP/JSON, unlike every other engine on this site
  • Index — the primary container (like a table/database), NOT the same concept as a MySQL/Postgres index structure
  • Document — a JSON object, similar to MongoDB's own model, but every field is analyzed into the inverted index at write time
  • Dynamic mapping — schema inferred automatically on first write, genuinely flexible but much harder to change once set than MongoDB's own per-document flexibility
  • Dynamic mapping's own gotcha (a numeric-looking value locking in a numeric type) echoes sqlite1-3's own type-guessing problem, mechanically different underneath
  • Next chapter: The Inverted Index — How Search Actually Works Underneath