Schema & fields
|
This section documents the current Solr line (10.0; 9.10.x the maintained 9.x branch), written and verified against the Apache Solr Reference Guide. No specific patch version is pinned. Some capabilities (the Solr Operator on Kubernetes, the package-manager ecosystem, Learning To Rank model training, and expert plugin development) are linked, not documented in depth. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Every collection has exactly one schema: the list of fields (or field-name patterns) it accepts,
the type each one is stored as, and which field is the document’s identity. Solr can hold that
schema as a hand-edited schema.xml, or — the default and recommended path — as a managed
schema you change through the Schema API instead of editing a file. This page covers that
distinction, the API itself, schemaless mode and the Schema Designer that build on top of it,
uniqueKey, the three schema element kinds (field, dynamicField, copyField), the flags a
field declares, and how to stop an open-ended schema from growing without bound.
Field types covers the fieldType every field here points at.
schema.xml vs. the managed schema
Classic Solr configsets shipped a hand-edited schema.xml: a plain file under conf/, versioned
with the rest of the configset, changed by editing XML and reloading the core or collection. It
still works, but every field, dynamic field, copy field, and field type in it has to be edited by
hand and kept consistent across every node and replica by whatever deploys the configset.
The managed schema (managed-schema.xml, the default since Solr 5) is functionally the same
document, but Solr treats it as generated state: it is stored in ZooKeeper (SolrCloud) or the core’s
conf/ directory (user-managed mode), and the supported way to change it is the Schema API below,
not a text editor. Hand-editing a managed schema is possible but discouraged — the API validates
each change against the fields already in use and keeps every node in a SolrCloud collection in
sync, which a manually copied file does not. Whether a configset uses the classic or the managed
form is set once, per configset, by the managed-schema update handler config; new configsets
created with bin/solr create are managed by default. See
Schema Elements for
the full XML structure shared by both forms.
The Schema API
The Schema API reads and writes a managed schema over HTTP as JSON, at
/solr/<collection>/schema. Reads are GET requests against sub-paths; writes are a single POST
whose body names one or more commands — add-field, delete-field, replace-field,
add-dynamic-field, add-copy-field, add-field-type, and their delete-/replace- counterparts.
Several commands can be batched into one POST, and each is applied and, in SolrCloud, propagated
to every replica before the call returns.
# Add a new explicit field.
curl -X POST -H 'Content-Type: application/json' \
"http://localhost:8983/solr/books/schema" \
-d '{
"add-field": {
"name": "rating_f",
"type": "pfloat",
"indexed": true,
"stored": true,
"docValues": true
}
}'
# Batch a dynamic field and a copy field in one call.
curl -X POST -H 'Content-Type: application/json' \
"http://localhost:8983/solr/books/schema" \
-d '{
"add-dynamic-field": { "name": "*_txt", "type": "text_general", "indexed": true, "stored": false },
"add-copy-field": { "source": "*_txt", "dest": "text" }
}'
# Read the schema back -- the whole thing, or one section at a time.
curl "http://localhost:8983/solr/books/schema?wt=json"
curl "http://localhost:8983/solr/books/schema/fields?wt=json"
curl "http://localhost:8983/solr/books/schema/uniquekey?wt=json"
# https://solr.apache.org/guide/solr/latest/indexing-guide/schema-api.html
Field types (text_general, pint, pfloat, and the rest) are declared and changed the same
way, with add-field-type / replace-field-type / delete-field-type — see
Field types for what goes inside one. A replace-field or
replace-field-type on a field already holding data is restricted to changes Lucene can apply
without a reindex (for example widening stored); anything that changes how existing values were
already analyzed or encoded needs a full reindex into the corrected field, the same irreversibility
Elasticsearch mappings have — see
Mapping & field types. Full command and
response reference:
Schema API.
Schemaless mode
Schemaless mode is not a separate schema format — it is three ordinary features of a managed
schema wired together: an update request processor chain that runs on every add, in which a
field-guessing processor inspects each unmapped field’s JSON value, infers a type (string,
plong, pdouble, boolean, date), and issues the equivalent of an add-field Schema API call
before the document is indexed. bin/solr start -e schemaless (or bin/solr create -c <name> with
no configset, which defaults to the _default configset in schemaless mode) turns it on out of the
box.
# No "genre_s" field declared yet -- schemaless mode adds it from the first value it sees.
curl "http://localhost:8983/solr/books/update?commit=true" \
-H 'Content-Type: application/json' \
-d '[{"id": "5", "title": "The Dispossessed", "genre_s": "science fiction"}]'
curl "http://localhost:8983/solr/books/schema/fields/genre_s?wt=json"
# https://solr.apache.org/guide/solr/latest/indexing-guide/schemaless-mode.html
It is well suited to prototyping and to the tutorial round-trip on
Getting started, but the same trade-off Elasticsearch’s
dynamic mapping makes applies here: the first document Solr sees for a given key decides that
field’s type for the life of the collection, and an unbounded key space guessed this way is exactly
what leads to the field explosion covered below. Most production schemas disable field guessing
(remove or edit the add-unknown-fields-to-the-schema processor in solrconfig.xml) once the real
field set is known, and declare fields — or the dynamicField patterns below — explicitly instead.
See
Schemaless Mode for
the processor chain in full.
The Schema Designer
The Schema Designer, in the Admin UI under Schema Designer, is an interactive front end for building a new managed schema from sample documents without writing Schema API calls by hand: paste or upload sample JSON/CSV/XML, it guesses fields and types the same way schemaless mode does, and lets you edit each field’s type and flags, preview the analysis chain, and run test queries — all against a disposable temporary collection — before publishing the result as a real configset. It is a design-time tool for a schema that does not exist yet; once a published schema has indexed data, the same reindex restrictions as any other schema change apply. See Schema Designer.
uniqueKey
Every schema declares one field, almost always named id, as the uniqueKey: the field Solr uses
to detect whether an incoming document is a new insert or an update-by-replacement of an existing
one, and the field /update deletes and atomic/partial updates
(Partial updates & concurrency) address by.
It must be a single-valued, indexed, stored field, typically string or one of the numeric types,
and it cannot be changed once documents exist without reindexing the collection.
<!-- inside schema.xml / managed-schema.xml -->
<uniqueKey>id</uniqueKey>
See
Schema Elements for
the uniqueKey element and how it interacts with overwrite on /update.
field, dynamicField, and copyField
The schema declares fields three ways. The XML below is what a managed schema looks like under the hood — illustrative of the model even though the Schema API, not a text editor, is the supported way to write it:
<!-- An explicit field: exact name, always present with this shape. -->
<field name="id" type="string" indexed="true" stored="true" required="true"/>
<field name="title" type="text_general" indexed="true" stored="true"/>
<field name="text" type="text_general" indexed="true" stored="false" multiValued="true"/>
<!-- A dynamic field: a name *pattern*, matched against any field Solr has not seen an
explicit declaration for. Exactly one wildcard, at the start or the end of the name. -->
<dynamicField name="*_i" type="pint" indexed="true" stored="true"/>
<dynamicField name="*_s" type="string" indexed="true" stored="true" docValues="true"/>
<dynamicField name="*_ss" type="string" indexed="true" stored="true" docValues="true" multiValued="true"/>
<dynamicField name="*_dt" type="pdate" indexed="true" stored="true"/>
<!-- A copy field: duplicates a source value into a destination field at index time,
before analysis, so the same text can be analyzed two different ways. -->
<copyField source="title" dest="text"/>
<copyField source="*_txt" dest="text" maxChars="30000"/>
-
fieldis an exact, explicitly named field — whatid,title, andtextare above. It is the most predictable option: no pattern matching, no guessing. -
dynamicFieldmatches by name pattern rather than an exact name —year_i,rating_f, andgenre_ssabove in Core concepts & architecture all resolved through ai/*_f/*_ss-style pattern, not an explicitfielddeclaration. It is the idiomatic way to accept a bounded family of similarly-typed fields (price,*_dt) without declaring each one, and Solr always prefers a longer, more specific match over a shorter one. See Dynamic Fields. -
copyFieldcopies a value into a second field before analysis runs, so the two fields can be analyzed completely differently — the classic use is a catch-alltextfield, analyzed for broad full-text search, fed from several typed source fields the application still queries individually. Copies cannot be chained (A → B → Cdoes not propagate toC), andmaxCharscaps how much of a long value is copied. See Copy Fields.
curl -X POST -H 'Content-Type: application/json' \
"http://localhost:8983/solr/books/schema" \
-d '{ "add-copy-field": { "source": "title", "dest": "text" } }'
// SolrJ: the same add-field call as above, through the Schema API's Java client wrapper.
try (SolrClient client = new Http2SolrClient.Builder("http://localhost:8983/solr").build()) {
SchemaRequest.AddField addField = new SchemaRequest.AddField(Map.of(
"name", "rating_f",
"type", "pfloat",
"stored", true,
"docValues", true));
addField.process(client, "books");
}
Field flags
Both field and dynamicField accept the same set of boolean flags, either declared directly or
inherited from the referenced `fieldType’s own defaults (an explicit flag on the field always wins).
| Flag | Effect |
|---|---|
|
The field is written into the inverted index and can appear in |
|
The original value is kept and returned in the |
|
Also builds the columnar, document-to-value structure sorting, faceting, and
function queries read from — far cheaper for those than reading |
|
The field accepts more than one value per document (an array in the JSON body,
several |
|
|
|
When both |
curl -X POST -H 'Content-Type: application/json' \
"http://localhost:8983/solr/books/schema" \
-d '{
"add-field": {
"name": "internal_note_s",
"type": "string",
"indexed": false,
"stored": true
}
}'
# indexed:false, stored:true -- retrievable, never searchable or filterable.
Full property reference, including the analysis-related flags (omitNorms,
omitTermFreqAndPositions, termVectors) not covered here:
Fields.
Preventing field explosion
An open-ended source of field names — schemaless mode left on in production, or a dynamicField
pattern too broad for what it actually matches — creates the same problem Elasticsearch’s dynamic
mapping does: every distinct key becomes its own field in the schema, cluster state (here,
ZooKeeper) grows without bound, and Schema API calls and node startup both slow down as the field
count climbs into the thousands. Solr has no single total_fields.limit-style setting; the
guardrails are structural instead:
-
Turn field guessing off once the real field set is known — remove or restrict the
add-unknown-fields-to-the-schemaupdate request processor insolrconfig.xmlso an unexpected key is dropped or rejected rather than silently minted into a new field. -
Prefer a handful of
dynamicFieldpatterns (_s,*_i,*_dt) that route many keys onto a small number of *types, over letting schemaless mode create one bespoke field per key. -
Fold sparse, rarely-queried, or high-cardinality-of-key data into a
copyFieldcatch-all or a singlestored, non-indexedfield holding the raw value, instead of indexing every key on its own — the same schema-on-write trade-off Elasticsearch’s own mapping-explosion guardrails make, since both sit on Lucene. -
Audit the live field count periodically with the Schema API’s field list (
GET /schema/fields?wt=json) or the Admin UI’s Schema tab, rather than discovering it from a slow node restart.
# Count fields on a collection -- a quick smell test, not a hard limit.
curl "http://localhost:8983/solr/books/schema/fields?wt=json" | grep -o '"name"' | wc -l
Continue with Field types for what a fieldType itself
configures (analyzers, numeric precision, similarity), or
Indexing & updates for how documents are written
against the schema defined here.