Schema Builder¶
Build the schema argument for creating an index.
A schema names the fields an index searches and says how each one is scored.
SchemaBuilder assembles that structure a field at a time and hands
back a plain {"fields": {...}} dict rather than a model, so a key this SDK
version does not yet model still reaches the server unchanged.
What belongs in a create schema is narrower than what an index stores. Declare
the fields you search — a dense_vector field, a sparse_vector field,
and string fields with full-text search enabled — and at least one of
those is required. Everything else is metadata: put those values in the
documents you upsert and they are indexed for filtering automatically, with no
declaration at all. A schema may hold at most one dense_vector and one
sparse_vector field.
The builder still has methods for the metadata-only declarations
(SchemaBuilder.add_boolean_field(),
SchemaBuilder.add_float_field(),
SchemaBuilder.add_string_list_field(), and
SchemaBuilder.add_string_field() without full-text search). The server
rejects all of them on create, and one rejected field fails the whole request.
Two field types have no method at all. Text embedded server-side is a
semantic_text field, which a create schema does not accept — reach it
through create_for_model() instead.
integer is response-only: describe and list return it for indexes that
pre-date numeric normalisation, but there is no integer variant to create
and the server answers one with a 422. The builder refuses it wherever a
raw type key could reach a field dict — see
SchemaBuilder.add_custom_field() and the additional_options
parameters — so replaying a described schema into a create request fails
locally with an explanation instead of remotely with a status code.
See also
create() — the method that consumes
the result, and the deprecated dimension=/metric= sugar this
replaces.
- class pinecone.schema_builder.SchemaBuilder[source]¶
Bases:
objectAssembles an index schema field by field.
Construct one directly —
SchemaBuilder()— then chainadd_*calls, each of which returnsself, and finish withbuild(). Adding a field under a name already present replaces the earlier definition rather than raising, so a later call wins.Building the
{"fields": {...}}dict by hand is equally valid and equally supported; the builder exists so the field types, their required keys, and the declarations the server refuses are checked as you write rather than on the create call.Examples
>>> from pinecone.schema_builder import SchemaBuilder >>> schema = ( ... SchemaBuilder() ... .add_dense_vector_field("embedding", dimension=768, metric="cosine") ... .add_string_field("title", full_text_search={"language": "en"}) ... .build() ... ) >>> sorted(schema["fields"]) ['embedding', 'title']
Pass the result straight to the create call:
pc.indexes.create( name="movie-recommendations", schema=schema, deployment={"deployment_type": "managed", "cloud": "aws", "region": "us-east-1"}, )
See also
create()— theschemaargument this builds, and thedeploymentargument that goes with it.- add_dense_vector_field(name, *, dimension, metric, description=None, **additional_options)[source]¶
Add the field that holds an embedding, for vector similarity search.
This is the field most indexes are built around, and a schema may hold at most one of them.
dimensionandmetricare fixed for the life of the field, so they have to match the embedding model you intend to use.- Parameters:
name (str) – Field name, up to 64 bytes; the documents you upsert carry their vector under this key, e.g.
"embedding". Replaces any existing field with the same name.dimension (int) – Length of the vectors this field stores — the output width of your embedding model, e.g.
1536. Must be between 1 and 20000 inclusive; the SDK rejects anything else before the request goes out.metric (str) – How similarity is scored —
"cosine","euclidean", or"dotproduct"."cosine"is what most text embedding models are trained for. SeeMetric.description (str | None) – Human-readable note stored with the field, up to 256 bytes. Optional.
**additional_options (Any) – Extra parameters merged into the field dict last, for forward compatibility with new API features.
- Returns:
selffor method chaining.- Raises:
PineconeValueError – If
dimensionis outside 1–20000, or ifnameordescriptionexceeds its byte limit. Both limits count UTF-8 bytes rather than characters, so a name of non-ASCII text runs out sooner than its length suggests.- Return type:
Examples
>>> from pinecone.schema_builder import SchemaBuilder >>> schema = SchemaBuilder().add_dense_vector_field( ... "embedding", dimension=1536, metric="cosine" ... ).build() >>> schema["fields"]["embedding"] {'type': 'dense_vector', 'dimension': 1536, 'metric': 'cosine'}
- add_sparse_vector_field(name, *, description=None, **additional_options)[source]¶
Add a sparse vector field, for keyword-weighted or learned-sparse search.
Declare one alongside a dense vector field for hybrid search, or on its own for pure sparse retrieval. A schema may hold at most one.
descriptionis the only other key a create schema accepts here. A sparse vector field takes nometric— sparse scoring is not configurable — and nodimension, because sparse vectors are variable-length. Passing either raises rather than putting a key on the wire that configures nothing.- Parameters:
name (str) – Field name, up to 64 bytes, e.g.
"keyword_terms". Replaces any existing field with the same name.description (str | None) – Human-readable note stored with the field, up to 256 bytes. Optional.
**additional_options (Any) – Extra parameters merged into the field dict last, for forward compatibility with new API features.
- Returns:
selffor method chaining.- Raises:
PineconeValueError – If
metricordimensionis passed — a sparse vector field has neither — or ifnameordescriptionexceeds its byte limit.- Return type:
Examples
>>> from pinecone.schema_builder import SchemaBuilder >>> schema = ( ... SchemaBuilder() ... .add_dense_vector_field( ... "embedding", dimension=1536, metric="dotproduct" ... ) ... .add_sparse_vector_field("keyword_terms") ... .build() ... ) >>> schema["fields"]["keyword_terms"] {'type': 'sparse_vector'}
- add_string_field(name, *, full_text_search=None, language=None, stemming=None, stop_words=None, filterable=False, description=None, **additional_options)[source]¶
Add a string field searchable by keyword, using full-text search.
Always pass
full_text_search: enabling it is what makes the field searched, and a string field without it is a metadata-only declaration the server refuses on create. Any offull_text_search=True, afull_text_searchdict, or one of the typed keyword arguments (language,stemming,stop_words) turns it on; where a dict and a keyword argument set the same key, the keyword argument wins.lowercaseandmax_term_lenare managed for you and cannot be set here.- Parameters:
name (str) – Field name, up to 64 bytes; the documents you upsert carry the text under this key, e.g.
"title". Replaces any existing field with the same name.full_text_search (bool | dict[str, Any] | None) –
Trueor{}for full-text search with default analysis, or a dict of the config keys (language,stemming,stop_words,ngram).None, the default, leaves the field unsearched — see the note below before choosing it.language (str | None) – Language whose rules drive tokenisation and analysis. Both short codes and their English names work —
"en"and"english"are the same request, and the SDK sends the short form. It knowsar,da,de,el,en,es,fi,fr,hu,it,nl,no,pt,ro,ru,sv,taandtr, and passes anything else through untouched so a newly-supported language works without an SDK upgrade; the server decides what it accepts.stemming (bool | None) – Match words by their root, so a search for
"running"also finds"run". Required whenstop_words=True.stop_words (bool | None) – Drop the language’s most common words from the index. Requires
stemming=True. Not every language supports stop words, and the server is what rejects an unsupported pairing — the SDK does not pre-check it.filterable (bool) – Make the field filterable instead of searched. Requesting it together with
full_text_searchis the trap: a string field is one or the other, and the server keeps the filter, silently discards the search configuration, and reports no error. Sent on the wire including itsFalsedefault, except whenfull_text_searchis enabled and you did not ask to be filterable.description (str | None) – Human-readable note stored with the field, up to 256 bytes. Optional.
**additional_options (Any) – Extra parameters merged into the field dict last, for forward compatibility with new API features.
- Returns:
selffor method chaining.- Raises:
PineconeValueError – If
stop_words=Trueis requested withoutstemming=True, if anngramconfig is combined withstemming=Trueorstop_words=True, or ifnameordescriptionexceeds its byte limit.- Return type:
Examples
Default analysis is enough for most text:
>>> from pinecone.schema_builder import SchemaBuilder >>> schema = SchemaBuilder().add_string_field( ... "title", full_text_search=True ... ).build() >>> schema["fields"]["title"] {'type': 'string', 'full_text_search': {}}
Naming the language turns on that language’s analysis, and the long form is accepted and normalised:
>>> schema = SchemaBuilder().add_string_field( ... "title", language="english", stemming=True, stop_words=True ... ).build() >>> schema["fields"]["title"]["full_text_search"] {'language': 'en', 'stemming': True, 'stop_words': True}
Character n-grams match substrings rather than whole words, which is what autocomplete needs. They cannot be combined with stemming or stop words:
>>> schema = SchemaBuilder().add_string_field( ... "title", full_text_search={"ngram": {"min_gram": 2, "max_gram": 3}} ... ).build() >>> schema["fields"]["title"]["full_text_search"] {'ngram': {'min_gram': 2, 'max_gram': 3}}
Note
A string field with no
full_text_searchis a metadata-only declaration, and the server rejects those on create — a400saying the schema only accepts fields used for search — whatever deployment you ask for, failing the whole request over the one field. Leave such fields out of the schema and put the values in the documents you upsert; they are indexed for filtering automatically.
- add_string_list_field(name, *, filterable=False, description=None, **additional_options)[source]¶
Declare a list-of-strings field — which no index you can create accepts.
A string-list field holds several strings per record, the shape tag-style metadata takes (
["sci-fi", "mystery"]) when you want to filter on individual elements. Declaring it is what does not work: see the note below, and upsert the list as an ordinary document field instead.- Parameters:
name (str) – Field name, up to 64 bytes, e.g.
"tags". Replaces any existing field with the same name.filterable (bool) – Enable metadata filtering on the field. Always included in the built schema, whether
TrueorFalse.description (str | None) – Human-readable note stored with the field, up to 256 bytes. Optional.
**additional_options (Any) – Extra parameters merged into the field dict last, for forward compatibility with new API features.
- Returns:
selffor method chaining.- Raises:
PineconeValueError – If
nameordescriptionexceeds its byte limit.- Return type:
Examples
>>> from pinecone.schema_builder import SchemaBuilder >>> schema = SchemaBuilder().add_string_list_field( ... "tags", filterable=True ... ).build() >>> schema["fields"]["tags"] {'type': 'string_list', 'filterable': True}
Note
string_listis a metadata-only declaration, and the server rejects those on create — a400saying the schema only accepts fields used for search — whatever deployment you ask for and whateverfilterablesays, failing the whole schema over the one field. Leave the field out and put the values in the documents you upsert; they are indexed for filtering automatically.
- add_boolean_field(name, *, filterable=False, description=None, **additional_options)[source]¶
Declare a boolean field — which no index you can create accepts.
Declaring the field is what does not work; a boolean is filterable once it is in a document. See the note below, and upsert the flag as an ordinary document field instead.
- Parameters:
name (str) – Field name, up to 64 bytes, e.g.
"is_published". Replaces any existing field with the same name.filterable (bool) – Enable metadata filtering on the field. Always included in the built schema, whether
TrueorFalse.description (str | None) – Human-readable note stored with the field, up to 256 bytes. Optional.
**additional_options (Any) – Extra parameters merged into the field dict last, for forward compatibility with new API features.
- Returns:
selffor method chaining.- Raises:
PineconeValueError – If
nameordescriptionexceeds its byte limit.- Return type:
Examples
>>> from pinecone.schema_builder import SchemaBuilder >>> schema = SchemaBuilder().add_boolean_field( ... "is_published", filterable=True ... ).build() >>> schema["fields"]["is_published"] {'type': 'boolean', 'filterable': True}
Note
booleanis a metadata-only declaration, and the server rejects those on create — a400saying the schema only accepts fields used for search — whatever deployment you ask for and whateverfilterablesays, failing the whole schema over the one field. Leave the field out and put the values in the documents you upsert; they are indexed for filtering automatically.
- add_float_field(name, *, filterable=False, description=None, **additional_options)[source]¶
Declare a numeric field — which no index you can create accepts.
This is the only numeric declaration there is: a create schema has no integer type, and whole numbers are stored and filtered as double-precision floats. Declaring the field is what does not work; numbers are filterable once they are in a document. See the note below, and upsert the value as an ordinary document field instead.
Describe and list responses can still return
integerfor indexes that pre-date that normalisation — seeIntegerField.- Parameters:
name (str) – Field name, up to 64 bytes, e.g.
"release_year". Replaces any existing field with the same name.filterable (bool) – Enable metadata filtering on the field. Always included in the built schema, whether
TrueorFalse.description (str | None) – Human-readable note stored with the field, up to 256 bytes. Optional.
**additional_options (Any) – Extra parameters merged into the field dict last, for forward compatibility with new API features.
- Returns:
selffor method chaining.- Raises:
PineconeValueError – If
nameordescriptionexceeds its byte limit.- Return type:
Examples
>>> from pinecone.schema_builder import SchemaBuilder >>> schema = SchemaBuilder().add_float_field( ... "release_year", filterable=True ... ).build() >>> schema["fields"]["release_year"] {'type': 'float', 'filterable': True}
Note
floatis a metadata-only declaration, and the server rejects those on create — a400saying the schema only accepts fields used for search — whatever deployment you ask for and whateverfilterablesays, failing the whole schema over the one field. Leave the field out and put the values in the documents you upsert; they are indexed for filtering automatically.
- add_custom_field(name, field_definition)[source]¶
Store a raw field dict verbatim — the escape hatch.
Two jobs: copying a field definition out of a describe response into a new index’s schema, and declaring a field type a newer API version offers that this SDK does not model yet. The name is checked and the definition’s
typeis rejected if it is response-only; the rest of the definition goes through untouched, so anything else wrong with it surfaces as a server error on create rather than here.- Parameters:
- Returns:
selffor method chaining.- Raises:
PineconeValueError – If the field name is empty or over its byte limit, or if
field_definition["type"]is response-only, which today means"integer".- Return type:
Examples
>>> from pinecone.schema_builder import SchemaBuilder >>> described = {"type": "dense_vector", "dimension": 1536, ... "metric": "cosine"} >>> schema = SchemaBuilder().add_custom_field( ... "embedding", described ... ).build() >>> schema["fields"]["embedding"] {'type': 'dense_vector', 'dimension': 1536, 'metric': 'cosine'}
Note
{"type": "integer"}is refused here rather than on the wire.integercomes back from describe and list for indexes created before numeric values were normalised to float, but there is no integer variant to create and the server answers a create request carrying one with a422. When replaying a described schema into a create request, drop those fields —add_float_field()is no help, because afloatdeclaration is rejected on create too, and numeric metadata needs no declaration.
- build()[source]¶
Return the completed schema dict.
The result is a deep copy of the builder’s state, so writing into it — adding a forward-compatible key the SDK does not yet model, for instance — leaves the builder and every other result of
build()untouched. One builder can be reused across several indexes even when each schema is edited after the fact.Nothing here checks that the schema is complete. The server requires at least one searched field (
dense_vector,sparse_vector, orstringwithfull_text_search), and a schema without one is built and returned all the same, so that partial schemas can be inspected; the create call is where it fails.- Returns:
{"fields": {name: field_dict, ...}}ready to pass as theschemaargument when creating an index.- Return type:
Examples
>>> from pinecone.schema_builder import SchemaBuilder >>> builder = SchemaBuilder().add_dense_vector_field( ... "embedding", dimension=8, metric="cosine" ... ) >>> schema = builder.build() >>> schema["fields"]["embedding"]["future_option"] = True >>> builder.build()["fields"]["embedding"] {'type': 'dense_vector', 'dimension': 8, 'metric': 'cosine'}