Skip to navigation

Index settings

Product Search is in beta with design partners. Endpoints roll out per tenant.

Each index has settings that shape its search. They decide which fields are searched and how much each one counts, which fields can be faceted and sorted, and which synonyms and rules apply to a query.

Field types and facets are set when an index is created. Settings, synonyms and rules change later with the same write key, with no help from Captain. Captain sets two things with you: per-record access control (acl, tenant_field) and what the audit log keeps (audit). A write key that changes one of those gets 403 FORBIDDEN. An Algolia import can also bring settings in.

What you set when you create an index

Create Index takes an optional fields object. Give each field a type, and set facet to true for the fields you want counted in facets. A number can also carry a unit, and an enum its allowed values.

curl -X POST "https://acme.captain.dev/v1/indexes" \
-H "Authorization: Bearer $CAPTAIN_WRITE_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "products",
"fields": {
"title": {
"type": "text"
},
"description": {
"type": "text"
},
"brand": {
"type": "keyword",
"facet": true
},
"category": {
"type": "keyword",
"facet": true
},
"price": {
"type": "number",
"unit": "usd"
},
"rating": {
"type": "number"
},
"image_url": {
"type": "url"
}
}
}'
Example: (201 Created)
{
"name": "products",
"status": "empty",
"staged_documents": 0,
"staged_records": 0,
"record_count": 0,
"build_id": null,
"last_job": null,
"created_at": 1759093140,
"staged_changes_since_build": false,
"pending_changes": false,
"object_search": true,
"last_build_at": null,
"serving": null,
"id_field": "id"
}

Fields you leave out are typed from the first records you add. Records lists the types and what each one supports.

A field accepts only type, facet, unit and values. Anything else answers 400 VALIDATION_ERROR. A write key that sends settings or schema gets 403 FORBIDDEN. Only a write key for every index in the tenant can create indexes. A key limited to some indexes gets 403.

Change settings

Get Settings returns the settings you set, the field table search uses, and up to three example values per field. Update Settings changes only the keys you send, as a JSON merge patch: objects merge key by key, null removes a key, and anything else replaces it.

curl -X PATCH "https://acme.captain.dev/v1/indexes/products/settings" \
-H "Authorization: Bearer $CAPTAIN_WRITE_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"fields": {
"brand": {"type": "keyword", "facet": true, "weight": "low"},
"material": {"type": "keyword", "facet": true}
}
}'
Example: (202 Accepted)
{
"object_search": true,
"settings": {"fields": {"brand": {"type": "keyword", "facet": true, "weight": "low"}, "material": {"type": "keyword", "facet": true}}},
"warnings": [],
"job": {"id": "job_20261002193012_9f8e7d", "type": "index.build", "index": "products", "status": "queued"},
"applies_at": "when job job_20261002193012_9f8e7d succeeds"
}

A field takes type, searchable, weight (primary, secondary or low), facet, filterable, sortable, unit and values. The settings are checked against your records before anything is saved, and a change that doesn’t fit answers 400 VALIDATION_ERROR and changes nothing.

A change rebuilds the index, the same way adding records does. The reply’s job is that rebuild, and Get Job follows it. Search keeps answering with the old settings until the job succeeds, then switches in one step. If a rebuild of the index is already running, job is that one, and your change goes into the rebuild that starts right after it. An index with no records yet answers 200 with no job, and the settings apply to its first build.

Replace Settings (PUT) sends the whole settings object instead. Leave out acl, tenant_field and audit as they are: a write key that changes them gets 403.

Get Synonyms and Replace Synonyms, and Get Rules and Replace Rules, work the same way with the write key. Their changes apply to the next search, with no rebuild.

Searchable fields and weights

Search matches the words in text, keyword, enum and identifier fields, and in numbers and booleans as written. Dates, locations and URLs aren’t searched.

Each text field has a weight. Weights are three tiers, not numbers:

  • Primary is the field that names the record, such as a title. A match here counts most.
  • Secondary is longer text, such as a description. This is the default for long text.
  • Low is everything else that should still match, such as tags or attributes.

Captain picks the tiers from your records. Change a field’s tier with weight, or stop it from being searched with "searchable": false, in Update Settings. Ranking uses one relevance model for every index, so there is no ranking formula to tune per index.

Facets

Enum and bool fields are facets by default. So are keyword fields whose values repeat, such as a brand or a category. Facet fields whose values repeat. A facet on a description lists almost every record once. For short labels such as a color, use the keyword type.

A number facet counts values only while the field has at most 1,000 distinct values, such as years or quantities. A date facet, or a number facet with more values such as prices, gives its minimum and maximum through facet_stats instead of value counts.

A search asks for facets with facet_keys. Search shows the request, and List Filters says which fields can be faceted.

Sort

Number and date fields are sortable. A search sorts by one of them instead of relevance with sort, as in {"field": "price", "order": "asc"}. Each request picks its own order, so an index needs no sorted copies.

Synonyms

A synonym makes a query match words it didn’t contain. There are two kinds:

  • Two-way: any word in the group matches the others. sofa, couch and settee all find each other.
  • One-way: the input adds the synonyms, but not the reverse. tv also matches television, and television doesn’t match tv.
[
{"objectID": "s1", "type": "synonym", "synonyms": ["sofa", "couch", "settee"]},
{"objectID": "s2", "type": "oneWaySynonym", "input": "tv", "synonyms": ["television"]}
]

Synonyms match whole words after spelling is folded and plurals are reduced, and a phrase matches only as written in order. They widen keyword matching. Meaning-based matching already relates words like these on its own.

Replace Synonyms (PUT /v1/indexes/{index}/synonyms) sets the whole list. Synonyms apply as soon as they are saved. There is no build to wait for.

Rules

A rule changes the results of the queries that match its condition. The condition is a pattern and an anchoring: is, contains, startsWith or endsWith. An empty pattern matches every query.

A rule can do four things:

  • Promote pins a record at a position, counted from 0.
  • Hide removes a record from the results.
  • Filter adds a filter to the query, in Captain’s syntax or as an Algolia filters string.
  • Boost lifts results that match a filter. It reorders results the query already found, and never adds new ones.
{
"objectID": "r1",
"condition": {"pattern": "rain jacket", "anchoring": "contains"},
"consequence": {
"promote": [{"objectID": "sku-20311", "position": 0}],
"hide": [{"objectID": "sku-20402"}]
}
}

Replace Rules (PUT /v1/indexes/{index}/rules) sets the whole list. Rules apply as soon as they are saved, like synonyms.

Repeatable results

With deterministic on, the same query and filters return the same records in the same order, with the same total, for as long as the index receives no writes. That holds across restarts and under load. It isn’t promised across rebuilds, or while records are added, changed or deleted.

An index built with fewer than 100,000 records has deterministic on by default. An index with 100,000 records or more has it off. A setting of true or false always wins over the default. Set it yourself with Update Settings. Captain can also move the 100,000-record limit for your tenant.

A change to field types, weights, facets or sort applies when the index is rebuilt. Update Settings starts that rebuild and returns it as job. Search serves with the previous settings until the rebuild is live. Get Update Status reports where it stands in build.

Synonyms and rules don’t need a build. They apply to the next search.

Settings from Algolia

An Algolia import carries the index’s settings, synonyms and rules, and runs as soon as you send it. Send "object_search": true so the settings become the field settings on this page. An export’s reply lists each setting, synonym and rule as mapped, approximated or not mapped, with the reason. An import that reads from Algolia reports the counts in its job.

Algolia settingIn Captain
objectIDThe record id.
searchableAttributesTiers of text fields. The first entry is primary, the second secondary, the rest low. Fields not listed aren’t searched. ordered() and unordered() aren’t reproduced.
attributesForFacetingFacets. filterOnly(x) becomes filterable but not faceted. searchable(x) and afterDistinct(x) become plain facets. Fields not listed aren’t faceted.
customRankingNumber and date fields become sortable, and each search picks its own sort. Algolia’s tie-break inside equal relevance isn’t reproduced. Other field types can’t be sorted.
rankingNot supported. Captain’s relevance model decides the order.
replicasNot needed. Sort each request with sort.
distinct, attributeForDistinctgroup_by on the field, up to 10 records per group, taken from the 100 most relevant records, or further down the 1,000 when a page needs more groups. The field needs one keyword, identifier or enum value per record.
typoTolerancetrue works as in Algolia: one typo from 4 letters and two from 8. min and strict are approximated. false isn’t supported.
minWordSizefor1Typo, minWordSizefor2TyposFixed at 4 and 8.
disableTypoToleranceOnAttributesIdentifier fields are never corrected. Other fields follow the usual typo rules.
allowTyposOnNumericTokensNumbers and codes are never corrected.
disableTypoToleranceOnWordsNot supported.
ignorePluralsPlurals always match their singular.
removeStopWordsCaptain’s own English stop words.
queryLanguages, indexLanguagesEnglish only.
optionalWords, removeWordsIfNoResultsApproximated. A search never requires every word to match.
decompoundQueryApproximated. Hyphens, and words joined or split, are handled.
attributesToHighlight, attributesToSnippetSet per request with highlight: true.
attributesToRetrieve, hitsPerPage, paginationLimitedTo, maxValuesPerFacet, highlightPreTag, highlightPostTag, responseFieldsSupported.
unretrievableAttributesNot supported. A field is either returned or dropped from the index.
advancedSyntax, relevancyStrictness, separatorsToIndex, camelCaseAttributes, enablePersonalization, renderingContent, userDataNot supported.

Synonyms and rules map this way:

AlgoliaIn Captain
synonym, oneWaySynonymThe same synonyms.
altCorrection1, altCorrection2One-way synonyms. A correction isn’t ranked below exact matches.
placeholderNot supported.
Rule pattern and anchoringThe same condition.
Rule promote, hide and params.filtersThe same consequence.
Rule optionalFiltersA boost.
Rule validityIgnored: the rule always applies. A rule whose windows have all ended is skipped, and so is a disabled rule.
Rule contexts, filter conditions, more than one condition, query edits, automaticFacetFilters, userData, renderingContentNot supported. A rule that has nothing else left isn’t imported.

Next steps

  • Records: field types, and how changes reach search.
  • Search: filters, facets, sort and grouping in a request.
  • Algolia compatibility: keep your Algolia client and query Captain.