Inside an Elasticsearch index — and the rest of ELK

After this lesson you will say what survives when a node of an Elasticsearch cluster fails, write the complete mapping of an index from an ER entity as the exam asks for it, predict which terms an analyzer makes of a text, and name what Beats, Logstash and Kibana do around the index.

Page 597 sends one request to an index of New York restaurant inspections: GET /nyc-restaurants/_search with a match on "BORO": "Queens". The first hit is NEW YORK SHAKE & BURGER, borough Queens, with "_score" : 1.4879451.

Page 598 sends the same request with one letter changed, "BORO": "queens", and gets "hits" : [ ]: not a single document, "max_score" : null. Same index, same field, same borough — and match is the query that is supposed to be lenient with text.

Nothing is wrong with the data, and the query is not to blame: the outcome was decided before anybody typed a query. Keep both responses in mind. By the end of the lesson you will know what decided them, and why the person who set it up may still have made the right choice.

Who does what: the ELK stack

Page 569 stacks four programs as coloured bars: Kibana, "Visualize and Manage"; Elasticsearch, "Store, Search and Analyze"; Logstash + Beats, "Ingest". The initials of the first three name the ELK stack; p. 571 calls it the Elastic Stack.

Page 570 shows who talks to whom (Fig. 1). Beats are small programs on the machines where data is produced, each collecting one kind of data and shipping it, through their shared library libbeat, straight into Elasticsearch or to Logstash, which parses and enriches it first. Kibana reads what Elasticsearch holds and draws it.

Beats Filebeat Metricbeat Packetbeat Heartbeat Auditbeat Winlogbeat libbeat Elasticsearch Kibana Logstash direct parse & enhance visualize
Fig. 1. The interactions of p. 570, redrawn: through libbeat the Beats ship either directly into Elasticsearch or into Logstash, which sends the data on after "Parse & Enhance"; Kibana's arrow is "Visualize". Blue arrows are data on the move.

The arrows carry a design decision: in this picture no data is born in Elasticsearch. It arrives from elsewhere — a web server's log, a database's rows — and is indexed to be searched, while the system it came from keeps the original. That is why two of the four programs exist only to bring data in.

Elasticsearch itself is the "Core of the Elastic Stack", a "Search & Analytic engine" with four properties (p. 571, again on p. 575):

Documents, indices, shards and replicas

Start from one record: six fields of the inspection that p. 590 fetches from the index nyc-restaurants.

{
  "DBA" : "BENTO HOUSE",
  "BORO" : "Queens",
  "CUISINE DESCRIPTION" : "Chinese",
  "INSPECTION DATE" : "02/17/2021",
  "ZIPCODE" : 11354,
  "VIOLATION CODE" : "22G"
}

That is a document: "Elasticsearch stores data structures in JSON documents" (p. 576), JSON objects like MongoDB's (lesson 13), nesting included. "When stored, new documents are indexed and made fully searchable", and they "can be accessed from any ES node when in a cluster".

The documents of one kind form an index: a "Data organization mechanism", "Partitioned in shards", whose structure is defined "via mappings"; and "Elasticsearch is based on the concept of Inverted Index" (p. 577). Page 586 lines the words up with the relational ones:

Relational words and Elasticsearch words (p. 586), with the restaurant index as the example.
RDBMSElasticsearchIn the restaurant index
TableIndexnyc-restaurants
RowDocumentone inspection, such as BENTO HOUSE
ColumnFieldBORO, DBA, ZIPCODE
SchemaMappingwhat GET /nyc-restaurants/_mapping returns

The analogy holds for words, not mechanisms: a document nests, an index is cut into pieces, and "index" means the whole collection, not SQL's helper structure on a column.

Shards and replicas

An index is split into primary shards — "Each document in an index belongs to one Primary Shard" (p. 1621) — spread over the nodes of a cluster: the partitioning of lesson 3. A replica is a copy of a primary shard, with one condition: "Replicas: copies of shards stored on a different node" (p. 578). The same slide gives the traffic rules:

Why one writer? If every copy accepted writes, copies could disagree; one primary per shard orders the writes and the replicas follow. Reads change nothing, so any copy will do — more replicas, more reads at once. But every write is done once per copy, so replicas never speed up writing. Per index this is two numbers, number_of_shards and number_of_replicas (per primary), plus a name and a location per node (p. 579).

Page 579 draws the result, and is easy to misread: its five boxes labelled "Node" cannot be one cluster — a primary cannot exist twice. Read them as one index, three primaries with one replica each, laid out on two different clusters (Fig. 2), with \(3 \times (1 + 1) = 6\) copies in each. In general \(P\) primaries with \(R\) replicas make \(P\,(1 + R)\) shard copies.

Node 1 Node 2 P1 P2 P3 R1 R2 R3

Two nodes: the top row of p. 579

Node 1 Node 2 Node 3 P2 P3 R1 R2 P1 R3

Three nodes: the bottom row of p. 579

Fig. 2. The five "Node" boxes of p. 579, read as two separate clusters holding the same index — 3 primaries (solid) with 1 replica each (dashed), 6 shard copies in both. In both layouts no replica shares a node with its primary, so either cluster survives the loss of any one node.

Shards and replicas: what survives a failure

An index with some primary shards (P, solid) and replicas (R, dashed) on the nodes of a cluster. The widget places each replica on a different node from its primary, as Elasticsearch does; a copy with no possible node waits under "unassigned". Choose a node that fails, and step through a write and two reads of a document of shard 1. The widget shows the moment right after the failure; after a short delay Elasticsearch rebuilds missing replicas on the surviving nodes when there is room (background).

  1. Set the index of p. 579: 3 primaries, 1 replica, 2 nodes. Predict the number of shard copies, then predict what is still served when Node 1 fails.
    What you should see

    \(3 \times (1 + 1) = 6\) copies, all placed. The layout is not the slide's top row — the widget spreads the primaries — but it obeys the same rule. When Node 1 fails, R1 and R3 on Node 2 are promoted to primaries: every shard is still served. The health turns yellow, because the lost replicas have no third node to be rebuilt on.

  2. Now 3 nodes, 3 primaries and replicas at 0. Before failing Node 2, say what part of the index survives.
    What you should see

    Shard 2 lived only on Node 2: it is lost, and the health is red. A third of the index is gone even though the data was "distributed". This is the one insight of the widget: shards spread the index, only replicas let it survive a failure. Put the replicas back to 1 and fail Node 2 again: R2 on Node 3 becomes the primary and nothing is lost.

  3. One node, one replica. Where do the replicas go? Then add a second node.
    What you should see

    Nowhere: all three replicas are unassigned, because a replica may not sit on the node of its primary, and there is no other node. With two nodes every replica finds a place. In general an index with R replicas needs at least R + 1 nodes to place them all.

  4. Step through the write and the reads with the default index. Then fail the node that holds P1 and step again: which copy receives the write now, and how many copies of shard 1 remain?
    What you should see

    The write goes to the primary, which copies it to the replica; a read can be answered by either copy. After the failure the promoted replica takes the write, and shard 1 exists once — one more failure and it is gone, which is why the health is yellow, not green, until Elasticsearch rebuilds a replica of shard 1 on Node 3.

Shards are not free: "The more shards you have, the more the management overheads", and "The larger the shard size, the more it takes to move them (if needed)" (p. 1622). The number of primaries is fixed when the index is created; the number of replicas can change at any time (background).

Talking to Elasticsearch: one REST request at a time

"Interactions with Elasticsearch happen through requests to REST endpoints", and "Actions depend on HTTP verbs" (p. 582): GET to read, POST and PUT "often used to create new documents, indices, etc.", and DELETE. You send them from a command line, from Postman, or from the "Developer Tools built in Kibana", the console of the exercise sessions. A request is a verb, an endpoint and, often, a JSON body. The shortest creates an index (p. 587):

# create an empty index
PUT /my_index

The answer, "acknowledged" : true, says that the index exists. Now add a document (p. 584):

# add a document; Elasticsearch picks its _id
POST /my_index/_doc
{
  "author":"Andrea",
  "title":"Set up Elasticsearch and Kibana in 15 minutes!",
  "date":"Tue, 23 Feb 2021 11:40:00 +0000",
  "categories": ["elasticsearch","tutorial",
                 "data science","bigdata"],
  "lang":"en-US"
}

Read it aloud: verb POST; endpoint /my_index/_doc, the documents of my_index; body, the document. No identifier is given, so "Elasticsearch takes care of creating and assigning IDs to documents" (p. 583): the answer carries "_id": "jYpU_XQBkGeBYJKltRh8" and "result": "created" (p. 585). With PUT you choose the identifier:

# create or replace document my_id
PUT /my_index/_doc/my_id
{
  "author":"Andrea",
  "lang":"en-US"
}

Hence the slide's warning (p. 583): "POST doesn't require the ID of the resource → duplicates", "PUT requires the ID of the resource → create or update the same one". Send the POST twice and the post is stored twice, under two ids; send the PUT twice and one document has _version 2. A PUT to a known address is idempotent; a POST to a collection is not.

A GET on a document's address reads it back (p. 590):

# one document by its _id
GET /nyc-restaurants/_doc/pZpozncBDaS7kSRanan2

The answer wraps the document in metadata — _index, _id, _version, _seq_no, _primary_term, "found" : true — and returns under _source the JSON exactly as it was sent. Searches look in the inverted index; _source is what they hand back. The last verb removes:

# delete one document; DELETE /my_index drops the whole index
DELETE /my_index/_doc/my_id

The mapping: what each field becomes

Ask the restaurant index for its structure:

# the mapping this index is using
GET /nyc-restaurants/_mapping

Nobody typed the answer of p. 581, yet every field has a type: "@timestamp" : { "type" : "date" }, "ACTION" : { "type" : "text" }, "BBL" : { "type" : "long" }… "Is Elasticsearch really schemaless?" the slide asks, with an arrow labelled "Proof" to this answer. No: "Elasticsearch tries to guess the structure of the documents" — dynamic mapping — when it first meets a field, and keeps the guess.

A mapping is the schema of an index: with it "you can tell Elasticsearch the types of your documents' fields", "Decide which fields are searchable" and "Enable full-text search, time and geo-based queries" (p. 580). The type decides how a value is indexed, so it decides which questions can be asked of it. Page 589 teaches four types; the session's table adds more (p. 1635):

The field types in the slides' words (pp. 589, 1635), with fields of the restaurant index that would take them.
TypeThe slide saysRestaurant fields
text"used for full-text search. With analyzer you can specify how the field is pre-processed."DBA, VIOLATION DESCRIPTION
keyword"used for structured content such as email, tags, postcode etc…"BORO, CUISINE DESCRIPTION, ZIPCODE
date"used for dates you can specify the format."INSPECTION DATE, with "format": "MM/dd/yyyy"
long"used for 64-bit integers."BBL, as mapped on p. 581
double, boolean, binary, object, geo_pointthe session's table (p. 1635): a 64-bit floating-point number, "Either true or false", a Base64-encoded binary value, "A JSON object", "Latitude and longitude points"Latitude, location
integernot in either table, but used by the session's own mapping (p. 1663): a 32-bit integerSCORE

The hard choice is text against keyword. A text field is cut into words by an analyzer and searched word by word; a keyword field is one exact value, matched whole, counted and sorted. Decide by the question: "violations that mention mice" wants words, so text; "only Queens" and "how many per cuisine" want whole values, so keyword. Mind the slide's own list: a postcode is keyword even when it arrives as a number, like "ZIPCODE" : 11354 — you match postcodes, you never add them.

The guess is "risky (e.g. dates could be parsed as strings)" (p. 581): "02/22/2021" is not a date format Elasticsearch recognises by default, so it becomes a string — a text field with a keyword sub-field (background) — and sorting by it compares characters, not days.

What may change once documents are in

Adding a field (p. 588):

# add a text field to the mapping
PUT /my_index/_mapping
{
  "properties": {
    "field_name": {
      "type": "text"
    }
  }
}

The exam's question: the complete mapping of the index

Safer than any guess is a mapping written before the first document. The session shows the full form, settings and mapping side by side (p. 1663, "Define the full mapping describing a Person with 10 shards and 3 replicas"):

# an index with settings and mapping
PUT /people
{
  "settings": {
    "number_of_shards": 10,
    "number_of_replicas": 3
  },
  "mappings": {
    "properties": {
      "personal_id": { "type": "keyword" },
      "name": { "type": "text" },
      "surname": { "type": "text" },
      "birth date": { "type": "date", "format": "yyyy-MM-dd" },
      "address": { "type": "text" },
      "eye_color": { "type": "keyword" },
      "height": { "type": "integer" }
    }
  }
}

Read it from the outside in: PUT /people creates the index; settings gives it 10 primaries with 3 replicas each; mappings → properties holds one entry per field with its type and, for a date, its format. Seven of the nine past papers open their Elasticsearch exercise with this request, for 1 point: "Provide the complete mapping of the index for the considered entity (i.e., field name, field type, the structure of the mapping, etc.)" — the three 2024 papers word it without "for the considered entity". mappings is the answer; settings may be added.

Solved on the paper of 30 May 2025 (its header prints "June 30, 2025"), exercise 3. The ER scenario tracks puzzles' production and sales; the entity is Puzzle — Puzzle ID, Name, Collection Name (e.g., Museum Collection), Number of Pieces, Width, Height, Description — and "All sizes are in cm". The next sub-question (2 points) asks for "the total number of puzzles for each collection name". "Consider the Puzzle entity. Provide the complete mapping of the index for the considered entity (…) (1 PT)". List every attribute, inherited ones included (Puzzle has no ISA parent), and type each by the question it will answer:

From the attributes of Puzzle to the fields of the mapping. Field names in lower case with underscores, like the session's personal_id.
AttributeFieldTypeWhy
Puzzle IDpuzzle_idkeywordan identifier, matched whole
Namenametextsearched by its words
Collection Namecollection_namekeywordcounted per value by the next question: "term aggregations only work on keywords" (p. 1658)
Number of Piecesnumber_of_piecesintegera count
Width, Heightwidth, heightdoublecentimetres, may have decimals
Descriptiondescriptiontextfree text
# the answer: mappings → properties
PUT /puzzles
{
  "mappings": {
    "properties": {
      "puzzle_id": { "type": "keyword" },
      "name": { "type": "text" },
      "collection_name": { "type": "keyword" },
      "number_of_pieces": { "type": "integer" },
      "width": { "type": "double" },
      "height": { "type": "double" },
      "description": { "type": "text" }
    }
  }
}

Choose each field's type by the question that will be asked of it: words → text, whole values to match or count → keyword, numbers and dates → their own types.

Analyzers: how a text becomes terms

A text field is "pre-processed" by an analyzer (p. 589), the program that turns a string into the terms of the inverted index. You can run one on any text (p. 594):

# the terms of a text
POST /_analyze
{
  "analyzer": "standard",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

The answer lists tokens with their offsets and positions; the slide shows the first, "token" : "the" at position 0. The table runs the same sentence through every analyzer of pp. 591–593:

The sentence of p. 594 through the built-in analyzers. The slide prints only the first token, the; the rows are computed from each analyzer's definition (Elasticsearch documentation). A middle dot separates the terms.
AnalyzerTerms
standardthe · 2 · quick · brown · foxes · jumped · over · the · lazy · dog's · bone
simplethe · quick · brown · foxes · jumped · over · the · lazy · dog · s · bone
whitespaceThe · 2 · QUICK · Brown-Foxes · jumped · over · the · lazy · dog's · bone.
stopquick · brown · foxes · jumped · over · lazy · dog · s · bone
keywordThe 2 QUICK Brown-Foxes jumped over the lazy dog's bone.
patternthe · 2 · quick · brown · foxes · jumped · over · the · lazy · dog · s · bone
english (a language analyzer)2 · quick · brown · fox · jump · over · lazi · dog · bone

Check the slide's definitions against the rows. The standard analyzer "divides text into terms on word boundaries, as defined by the Unicode Text Segmentation algorithm. It removes most punctuation, lowercases terms, and supports removing stop words" (p. 592). The simple analyzer cuts "whenever it encounters a character which is not a letter" and "lowercases all terms": the 2 vanishes, "dog's" falls apart. The whitespace analyzer "does not lowercase terms": QUICK and Brown-Foxes survive. The stop analyzer "is like the simple analyzer, but also supports removal of stop words"; the keyword analyzer, a "noop", "outputs the exact same text as a single term"; the pattern analyzer splits "according to a custom regexp pattern" (p. 593) — the pattern marks the separators, by default every run of non-word characters, \W+, and the terms are lower-cased (background). A language analyzer "removes stopwords of a given language" and performs "stemming" (p. 591): english makes foxes fox and lazy lazi — a stem need not be a word, only shared by related words.

"The standard analyzer removes stop words." It "supports removing stop words" (p. 592) if you configure it to; by default it removes none — the first token on p. 594 is "the".

The analyzer runs twice: on every document when it is indexed, and on the query text of a match. Both sides must end with the same terms, or "Brown-Foxes" never meets "brown" — the training–serving skew of machine learning, where preprocessing that differs at serving time feeds the model inputs it never learned.

And a keyword field? "Borough (BORO) was mapped as a keyword. A keyword does not have any analyser!" (p. 598). Its value enters the index whole, as written, and so does the query text. The widget replays the hook on three documents from the slides.

"Queens" or "queens": the mapping decides

Three restaurant documents from the slides, indexed with the mapping you choose for BORO. Top: the query text goes through the field's analyzer and becomes query terms (orange). Middle: the terms of BORO in the inverted index (violet) and their postings, the documents that contain them. A query term finds documents only if the index holds exactly that term. Under the readouts, the response as Elasticsearch would print it.

  1. Keep BORO as keyword and query "Queens", then "queens". Predict the number of hits before you switch.
    What you should see

    2 hits, then 0 — the response of p. 598, "max_score" : null, "hits" : [ ]. Without an analyzer the query term is "queens", and the index only knows "Queens": there is no posting list to follow.

  2. Switch to text with the standard analyzer. Predict "queens" and "QUEENS".
    What you should see

    2 hits each. The analyzer lower-cased the stored values when the documents were indexed and lower-cases the query now: both sides meet at the term "queens".

  3. Text again, but with the whitespace analyzer. Predict "queens".
    What you should see

    0 hits: the whitespace analyzer "does not lowercase terms" (p. 592), so the index holds "Queens". Case disappears not because a field is text, but because its analyzer lower-cases.

  4. The one insight: a match compares terms after the field's analyzer. Query "Queens County" — predict keyword, then standard.
    What you should see

    Keyword: 0 hits, because the whole string is one term and "Queens County" is not "Queens". Standard: 2 hits, because the text becomes two terms, "queens" and "county", and a match needs any one of them; "county" simply finds nothing.

The rest of ELK: Beats, Logstash and Kibana

Ingestion takes two programs because it has two jobs: "Beats focus on data collection and shipping", "Logstash focuses on processing and data normalization" (p. 617). Beats, a "Platform for data shippers" — Filebeat, Metricbeat, Packetbeat, Heartbeat — "Collect and ship logs and metrics from hosts or containers" (p. 616), light enough for every machine. Logstash, the "Streaming ETL engine of the ELK stack", does the heavy work once, centrally: "centralized data collection, processing and enrichment on the fly", "Data agnostic", with a "Wide range of integrations and processors" and "Ready-to-use monitoring and administrative panes built in Kibana" (p. 615). ETL is the warehouse word of lesson 23; streaming means event by event.

Logstash also takes data without Beats, "Via TCP, UDP, HTTP protocols" or "Pool-based inputs like JDBC" that fetch it (p. 617), and emits it "to Elasticsearch, to other data stores or via TCP, UDP and HTTP protocols" (p. 619); in between, filters "structure, normalize and enrich data" (p. 618) — the three columns of p. 620.

Events and the pipeline

What flows is an event: the "Primary unit of data in Logstash", "Similar to JSON documents", which "Flow through pipelines" (p. 623). The slide prints one as Logstash's debug output shows it, with => where JSON has a colon:

{
  "@timestamp" => 2021-02-16T01:01:01.000Z,
  "message" => "hello",
  "other_field" => {
    "nested_field" => 5678
  }
}

The pipeline, the "Logical flow of data" (p. 624, Fig. 3), "Supports multiple inputs", has a "Single queue to buffer data", "Either in-memory or persistent", and "Workers ensure server-side scalability".

Input Queue memory or disk Worker Filter Output several workers run at once
Fig. 3. The pipeline of p. 624, redrawn. Every input writes events (blue cells) into one queue, kept in memory or persisted on disk; each worker takes a batch from the queue and runs the filters, then the outputs, on it. The stack of boxes stands for several workers running at the same time.

Why a queue? Inputs and filters run at different speeds: bursts wait in the queue, and a slow Elasticsearch fills it until the inputs wait, without dropping events. Why workers? Parsing and enriching are expensive, so several threads do it at once, each on its own batch — scaling inside one server (background).

Codecs and filters

A codec changes "the data representation of an event" — "Serialization and de-serialization" — "as part of an input or output" (p. 625):

# parse the JSON lines of file.log
input{
  file {
    path => "file.log"
    codec => json
  }
}
# pretty-print events on the console
output{
  stdout {
    codec => rubydebug
  }
}

They are the slide's "Parse JSON data in file.log" (a real file input wants an absolute path, like /var/log/file.log) and "Format console output with Ruby Amazing Print", the => form above. A codec works at the edges, on bytes; a filter in the middle, on fields (pp. 626–627):

Logstash filters in the slides' words (pp. 626–627), each with one typical use.
FilterThe slide saysFor example
mutate"field manipulation filter": "Convert types", "Add, rename, replace, copy fields", "Upper and lowercase transformations", "Join arrays", "Split field into arrays"turn the string "200" into the number 200; remove a sensitive field — the "Exclude sensitive fields" of p. 618
split"divide a single event into multiple events"one event per item of a list
drop"delete an event"throw away debug lines
geoip, dns"enrich IP addresses"the city of an address; its host name
useragent"record information like browser type from web logs"browser and system of each visitor
translate"use local data to translate parts of the events"an error code → its description
elasticsearch"query Elasticsearch"copy fields from an earlier event
jdbc"query databases that support Java"look up a customer in any database with a JDBC driver — JDBC is the Java API Logstash speaks, the database need not "support Java"; the filters are named jdbc_streaming and jdbc_static

One filter almost every pipeline uses is missing: grok, which cuts an unstructured line into named fields with patterns (background) — the "structure" of p. 620, which geoip needs before it can enrich (Fig. 4).

{ "message" => "83.149.9.216 GET / 200" } one string, no fields

Before: as Filebeat ships it

{ "clientip" => "83.149.9.216", "verb" => "GET", "request" => "/", "response" => "200", "geoip" => { "city_name" => "Moscow", "country_name" => "Russia" } } grok geoip

After the filters: grok, then geoip

Fig. 4. One event before and after the filter stage, printed in the => notation of p. 623 (the original message and @timestamp are left out). Before, the whole web-server line is one string. After, grok has cut it into fields and geoip (p. 627) has added where the address is — the address and its city are the example of Elastic's Logstash tutorial. Only now can Elasticsearch count requests per country, and Kibana put them on a map.

The slide's own pipeline is the smallest complete one (p. 628):

# Beats in, Elasticsearch out
input {
  beats { port => 5043 }
}
filter {
  mutate { lowercase => ["message"] }
}
output {
  elasticsearch {}
}

The slide reads it: "Receive data from a host that uses Beats" (port 5043; a Beat sends to 5044 unless told otherwise — background), "Process that input via a filter" (lower-case message), "Output directly to Elasticsearch". Type straight quotes, not the slide's curly ones.

Delivery: at least once, and the dead letter queue

Page 629 promises "“At Least Once” message delivery": "In most conditions messages are delivered exactly once", but "Unclean shutdowns could lead to duplicates". With the persistent queue, an event leaves the queue only after the outputs accept it; if Logstash dies in between, the event is processed again at restart — twice in Elasticsearch, never zero times.

Some events can never be delivered: Elasticsearch rejects them, typically when a field contradicts the mapping. Retrying forever blocks the pipeline; dropping them breaks the at-least-once promise. The slide squeezes this into "Due to “at least once” policy these are undeliverable": the refusal makes them undeliverable, the policy forbids dropping them. The dead letter queue (DLQ) sets them aside on disk for later inspection: it "Avoids losing those events and halting the pipeline, freeing processing resources for subsequent events" (p. 629). It is off by default, and only the Elasticsearch output writes to it (background).

Modules, finally — "Provided by Logstash and Beats", "Available only for specific data types" — package the whole chain: "Automated parsing and enrichment in Logstash", "Custom schema in Elasticsearch", "Default dashboards in Kibana" (p. 621).

Kibana: every panel is a question to Elasticsearch

Kibana "is an open source data visualization dashboard for Elasticsearch" that "provides visualization capabilities on top of the content indexed on an Elasticsearch cluster" (p. 631): it shows what an index holds, nothing else. With it "you can aggregate, organize, filter and represent data" (p. 632) in dashboards of "basic representations, location-based analyses, time series, machine learning and graphs/networks" (p. 633), some paid.

A first dashboard (p. 635) is an editor with four labelled zones: "Dashboard Management", "Time and Fields Filters", "Tools to create a new Graph" and the space where "The Graphs you will create will be displayed". Its "Create Panel" (p. 636) offers Lens, Maps, "Aggregation based" charts, TSVB, a "Custom visualization" in Vega, Text and Controls: "easy to learn … but really hard to master" (p. 637).

What Elasticsearch computes behind each kind of panel on the slides (background: Kibana writes the request for you). Aggregations are the subject of lesson 26.
PanelElasticsearch computesField it needs
the gauge "CPU usage 36.43%" (p. 632)one number from many documents, such as an averagea number
the time series of p. 632one number per time intervala date, usually @timestamp
a pie such as "TOP SOURCES" (p. 633)one bucket per value, with its countkeyword
the "MAP" of p. 633counts per region of the mapa location: geo_point coordinates, or a country code as keyword

Back to Queens

The hook resolves. BORO is a keyword, so "Queens" entered the index as one term, capital Q included. The query "Queens" became the same term; "queens" became a term with no posting list, hence "hits" : [ ]. A text field with the standard analyzer would have lower-cased both sides and found both.

Was keyword a mistake? No. A borough is a category to filter and count by, and "term aggregations only work on keywords" (p. 1658): the Kibana pie of inspections per borough needs this very mapping. Exactness costs spelling the value as stored. To have both, map text with a keyword sub-field, as dynamic mapping does, and query BORO for words, BORO.keyword for exact values. The mapping did not fail: it answered the question it was built for.

Exam-style questions

Two practical sub-questions from past papers — the 1-point mapping that opens the Elasticsearch exercise — and five theory questions in the form of the theory part: open answers of a few sentences, and closed questions where a wrong answer may cost points.

Exam of 11 Feb 2025, Elasticsearch exercise, first sub-question. ER scenario: a data structure for tracking Formula 1 races. Person (Personal_ID, Name, Surname, Birth_Date, Gender) has the children Spectator, Team Owner and Team Member, (T, E); Team Member has the children Engineer, Mechanic and Pilot, (P, E). Pilot - (...), Championships_Won, Career (Textual). The second sub-question will ask for male pilots who won at least 10 championships and whose career must include the words "Champion" and "Car".

Consider the Pilot entity. Provide the complete mapping of the index for the considered entity (i.e., field name, field type, the structure of the mapping, etc.) (1 PT)

Show model answer
PUT /pilots
{
  "mappings": {
    "properties": {
      "personal_id": { "type": "keyword" },
      "name": { "type": "text" },
      "surname": { "type": "text" },
      "birth_date": { "type": "date", "format": "yyyy-MM-dd" },
      "gender": { "type": "keyword" },
      "championships_won": { "type": "integer" },
      "career": { "type": "text" }
    }
  }
}

A pilot is a person and a team member, so its documents carry the inherited attributes as well. The identifier and the gender are keywords (exact match), names and career are full text, the number of championships is an integer and the birth date a date with its format.

The paper prints "Career (Textual), Career (Textual)" — one field is enough. The question tests the structure mappings → properties → one entry per attribute, and types that fit the queries that follow: "male" is an exact value of gender, "at least 10" a range on an integer, "Champion" and "Car" words in a text field. Forgetting the inherited attributes leaves the next query without a gender to filter on. That query is solved in lesson 26.

Exam of 12 Sep 2025, exercise 3, first sub-question. ER scenario: a data structure for tracking farmers and their sales at local markets. People (Personal ID, Name, Surname, Age, Address, State) has the child Farmer, (P, O), with the attributes Biography and Experienced (Yes/No). Farmer (1, 2) — Work — (1, N) Farm; Farmer (1, N) — Cultivate — (1, N) Crop.

Consider the Farmer entity. Provide the complete mapping of the index for the considered entity (i.e., field name, field type, the structure of the mapping, etc.) (1 PT)

Show model answer
PUT /farmers
{
  "mappings": {
    "properties": {
      "personal_id": { "type": "keyword" },
      "name": { "type": "text" },
      "surname": { "type": "text" },
      "age": { "type": "integer" },
      "address": { "type": "text" },
      "state": { "type": "keyword" },
      "biography": { "type": "text" },
      "experienced": { "type": "boolean" }
    }
  }
}

The farmer inherits the six attributes of People. Experienced (Yes/No) is a boolean, the age an integer, the state a keyword because it is a category; the biography is full text.

The next sub-question of the paper (solved in lesson 26) searches the biography for "cactus" and "aloe" and asks for farmers whose name is "Bill", so biography must be text and the inherited name must be present. A Yes/No attribute is the classic place to show that you know boolean.

An index of restaurant inspections answers the request below with "hits" : [ ], although many of its documents contain "BORO" : "Queens". Which explanation is correct?

GET /nyc-restaurants/_search
{ "query": { "match": { "BORO": "queens" } } }
  • A match query is always case-sensitive. On a text field with the standard analyzer both sides are lower-cased, and "queens" finds "Queens".
  • The index has a single shard, so only part of the documents is searched. A search reaches every shard of the index; with one shard, that shard holds every document.
  • A match query cannot be used on fields that hold strings. The same match with "Queens" returns hits (p. 597).
  • BORO is mapped as keyword, which has no analyzer, so the query term "queens" must equal the stored term "Queens" exactly. The words of p. 598: "Borough (BORO) was mapped as a keyword. A keyword does not have any analyser!"
Show model answer

BORO is mapped as keyword. A keyword field has no analyzer: the stored value "Queens" is indexed as one exact term, and the query text "queens" is also used as one exact term. The two terms differ in case, so no document matches. With a text field and the standard analyzer, both would be lower-cased and the query would match.

A closed question built on pp. 597–598. Wrong ticks may be penalised: the tempting wrong answer is the first, which holds only for keyword fields and for analyzers that do not lower-case.

Explain the difference between primary shards and replica shards in Elasticsearch. How are write and read operations routed, and which of the two makes an index resistant to the failure of a node?

Show model answer

An index is partitioned into primary shards: each document belongs to exactly one primary shard, and the shards are distributed over the nodes of the cluster, so the data and the work are spread over several machines. A replica shard is a copy of a primary shard, always stored on a different node from its primary. A write operation is performed on the primary shard and then copied to its replicas, so all copies stay identical; a read operation can be routed to the primary or to any replica, so replicas also increase the capacity to serve reads. Resistance to faults comes from the replicas: when a node fails, a replica of each primary that was on it is promoted to primary and no data is lost, while without replicas the shards of the failed node are lost. Both numbers are set per index with number_of_shards and number_of_replicas.

The slides (p. 578) list resistance to faults under shards; the session (p. 1621) repeats it and counts replicas as one of "two types of shards". An answer that names the mechanism — a copy on another node — is right under every reading and shows that you know why.

True or false: once an index contains documents, the type of one of its existing fields can be changed in place with PUT /index_name/_mapping.

  • True The terms of that field were indexed for the old type. PUT /index_name/_mapping can add new fields, but not retype an existing one.
  • False To change the type you create a new index with the new mapping and reindex the documents into it.
Show model answer

False. The mapping of an existing field cannot be changed once documents are indexed: to change its type, create a new index with the correct mapping and reindex the data. PUT /index_name/_mapping can add new fields, but it cannot retype an existing one.

The statement is false both by the slide's printed sentence (p. 580) and by the precise rule — safe either way. Had it read "No field can be added to an index that has documents", the slide's wording and the facts would part ways (p. 588 adds one).

Which request lets Elasticsearch generate the identifier of the new document, so that sending it twice stores two documents?

  • PUT /blog/_doc/1 It creates or replaces the document with id 1: sent twice, the index still holds one document, with _version 2.
  • GET /blog/_doc/1 GET reads a document; it stores nothing.
  • POST /blog/_doc "POST doesn't require the ID of the resource → duplicates" (p. 583): each call gets a new generated _id.
  • PUT /blog It creates the index blog, not a document.
Show model answer

POST /blog/_doc. Without an id, Elasticsearch creates and assigns one, so repeating the request creates a duplicate. PUT /blog/_doc/1 requires the id and creates or updates that same document.

The pair POST /index/_doc against PUT /index/_doc/id is the whole of p. 583; the other two options test whether you can read a verb and an endpoint.

Describe the logical flow of data inside a Logstash pipeline. What are the queue and the workers for, and which delivery guarantee does Logstash offer?

Show model answer

A Logstash pipeline receives events from one or more inputs, for example Beats, TCP, UDP or HTTP listeners, or a JDBC input that polls a database. All inputs write into a single queue that buffers the events, either in memory or persistently on disk; the queue decouples the inputs from the processing and absorbs bursts. Worker threads take batches of events from the queue and run them through the filters, which structure, transform, normalize and enrich the events (for example mutate or geoip), and then through the outputs, for example Elasticsearch. Several workers run in parallel, which gives server-side scalability. Codecs attached to inputs and outputs deserialize and serialize the data. Logstash offers at-least-once delivery: in most conditions an event is delivered exactly once, but after an unclean shutdown it may be delivered twice; this requires the persistent queue. Events that cannot be processed go to the dead letter queue, so that they are not lost and the pipeline does not halt.

The figure of p. 624 is the skeleton: Input → Queue → Worker (Filter → Output). Naming the two kinds of queue and the condition of at least once shows that you know why the guarantee holds, not only its name.

Check yourself

An index is created with "number_of_shards": 2 and "number_of_replicas": 2. How many shard copies does it have, and how many nodes does it need so that every copy is placed?

  • 4 copies, 2 nodes 2 replicas per primary: each shard has 3 copies, not 2.
  • 6 copies, at least 3 nodes \(2 \times (1 + 2) = 6\); the 3 copies of a shard must sit on 3 different nodes.
  • 6 copies, 2 nodes With 2 nodes one of the three copies of each shard would have to share a node with another copy — not allowed, so it stays unassigned.
  • 2 copies, 1 node That counts only the primaries; replicas are copies too, and they can never sit on their primary's node.

You write the mapping of the restaurant index. Which fields should be keyword? Select all that apply.

  • BORO, e.g. "Queens" A category to filter and count by, matched whole.
  • VIOLATION DESCRIPTION, a sentence Sentences are searched by their words: text.
  • CUISINE DESCRIPTION, e.g. "Chinese" Despite its name it holds a category: "how many per cuisine" needs keyword.
  • ZIPCODE, e.g. 11354 "Keyword is used for structured content such as email, tags, postcode" (p. 589) — even when the value looks like a number.
  • INSPECTION DATE, e.g. "02/17/2021" A date, with a format such as "MM/dd/yyyy", so that ranges compare days.

Decide by the question that will be asked of the field: words → text, whole values → keyword.

You send POST /blog/_doc with the same body three times. What does the index contain?

  • One document with _version 3 That is what three PUTs to the same id would give.
  • One document; the second and third requests fail Nothing fails: POST simply creates a new document each time.
  • Three identical documents with three different generated ids "POST doesn't require the ID of the resource → duplicates" (p. 583).

Dynamic mapping meets the field "RECORD DATE" : "02/22/2021" for the first time. What does it most likely make of it?

  • A text field with a keyword sub-field — a string, not a date The value is not in a date format recognised by default; the slide's warning: "dates could be parsed as strings" (p. 581).
  • A date with the format MM/dd/yyyy Dynamic mapping does not invent formats; it only recognises a few standard ones.
  • A long, after removing the slashes The value is a string; it is never turned into a number.
  • Nothing: the document is rejected until you write a mapping Dynamic mapping exists precisely so that unknown fields are accepted.

Which analyzer keeps "Brown-Foxes" as one term but still cuts "The 2 QUICK Brown-Foxes" into several terms?

  • standard It splits on the hyphen: brown, foxes.
  • simple The hyphen is not a letter, so it splits there too — and it drops the 2.
  • keyword It keeps the hyphen, but makes the whole input a single term.
  • whitespace It cuts only at spaces: The, 2, QUICK, Brown-Foxes — and does not lower-case.

In a Logstash pipeline, where do the filters run?

  • In the input threads, before the events enter the queue Inputs only produce events and write them into the queue.
  • Inside the worker threads, on batches taken from the queue, before the outputs Input → Queue → Worker (Filter → Output), p. 624.
  • Inside Elasticsearch, when the document is indexed Logstash finishes its filters before the output sends anything to Elasticsearch.

A Kibana pie chart shows the number of inspections per borough. Where are those numbers computed?

  • In Elasticsearch, as an aggregation over the keyword field BORO Kibana "provides visualization capabilities on top of the content indexed on an Elasticsearch cluster" (p. 631); it asks, Elasticsearch counts.
  • In Kibana, which reads all the documents and counts them Kibana sends a request and draws the answer; it does not scan the index itself.
  • In Logstash, when the events are ingested Logstash can add fields to each event, but it does not keep counts over the index.

Key take-aways

What to watch and read