Medusa Search API Settings
In this guide, you'll learn about the settings and options that Medusa Search supports on indexes, fields, and queries.
Aside from the fields and settings that every index definition declares, Medusa Search supports settings and options of its own.
Index Settings#
Set the settings property of an index definition to configure how Medusa Search treats the whole index.
For example:
1export const productIndex = defineSearchIndex({2 name: "product",3 entity: "product",4 fields: search.define({5 id: search.keyword().filterable(),6 title: search.text().searchable(),7 brand: search.keyword().filterable(),8 }),9 settings: {10 distinct_attribute: "brand",11 provider_options: {12 "search-medusa": {13 distance_metric: "cosine_distance",14 },15 },16 },17 async *seed({ container }) {18 // ...19 },20})
The example declares two settings for the product index to control result deduplication and provider-specific options.
Medusa Search accepts the following index settings:
The example above sets distinct_attribute and provider_options. The section below covers typo_tolerance, which has a few properties of its own.
Typo Tolerance#
Medusa Search matches misspelled terms with a fuzzy index that it builds on every searchable field by default, so an index tolerates typos without declaring any setting. A query is the opposite: it matches terms exactly until you opt in, as explained in Opt a Query Into Typo Tolerance.
Set the typo_tolerance index setting to change the thresholds Medusa Search applies, to exempt a field, or to turn the fuzzy index off:
1export const productIndex = defineSearchIndex({2 name: "product",3 entity: "product",4 fields: search.define({5 title: search.text().searchable(),6 sku: search.keyword().searchable(),7 }),8 settings: {9 typo_tolerance: {10 min_word_size_for_one_typo: 6,11 min_word_size_for_two_typos: 9,12 disabled_on_attributes: ["sku"],13 },14 },15 async *seed({ container }) {16 // ...17 },18})
The example tolerates a typo on a term of six characters or more, two typos from nine characters, and never fuzzy-matches sku. Refer to Index Settings for every property the setting accepts.
Opt a Query Into Typo Tolerance
The setting only prepares the index. Every query matches terms exactly until you pass the typo_tolerance search option:
Medusa Search ranks a fuzzy match below every exact match, so enabling the option adds results rather than reordering the ones you already had.
q filter, or when none of the fields it searches has a fuzzy index.Field Options#
Use a field's providerOptions() modifier to configure how Medusa Search indexes that field.
For example:
Medusa Search reads the following field options:
Set a Field's Language#
Medusa Search matches a searchable field term by term, and it doesn't stem those terms until you ask it to. Set the field's language along with stemming so a query matches the other forms of a word, which matters for a catalog whose titles and descriptions aren't in English.
For example, to analyze a Dutch title and a German one:
1export const productIndex = defineSearchIndex({2 name: "product",3 entity: "product",4 fields: search.define({5 id: search.keyword().filterable(),6 title_nl: search.text().searchable().providerOptions({7 "search-medusa": {8 full_text_search: {9 language: "dutch",10 stemming: true,11 remove_stopwords: true,12 },13 },14 }),15 title_de: search.text().searchable().providerOptions({16 "search-medusa": {17 full_text_search: {18 language: "german",19 stemming: true,20 remove_stopwords: true,21 },22 },23 }),24 }),25 async *seed({ container, catchup, last_key: lastKey }) {26 const batchSize = 20027 let cursor = lastKey28 29 while (true) {30 const { data: products } = await container.query31 .graph({32 entity: "product",33 fields: [34 "id",35 "title",36 "updated_at",37 "deleted_at",38 ],39 filters: {40 ...(catchup41 ? { updated_at: { $gte: catchup.since } }42 : {}),43 ...(cursor ? { id: { $gt: cursor } } : {}),44 },45 pagination: {46 take: batchSize,47 order: { id: "ASC" },48 },49 withDeleted: !!catchup,50 })51 52 if (!products.length) {53 return54 }55 56 const live = products.filter((p) => !p.deleted_at)57 const gone = products.filter((p) => !!p.deleted_at)58 const ids = live.map((p) => p.id)59 60 const [nlTitles, deTitles] = await Promise.all(61 ["nl-NL", "de-DE"].map(async (locale) => {62 const { data } = await container.query.graph({63 entity: "product",64 fields: ["id", "title"],65 filters: { id: ids },66 }, { locale })67 68 return new Map(69 data.map((p) => [p.id, p.title])70 )71 })72 )73 74 yield [75 ...(live.length76 ? [{77 action: "upsert" as const,78 documents: live.map((product) => ({79 id: product.id,80 title_nl:81 nlTitles.get(product.id) ?? product.title,82 title_de:83 deTitles.get(product.id) ?? product.title,84 })),85 }]86 : []),87 ...(gone.length88 ? [{89 action: "delete" as const,90 filters: { id: gone.map((p) => p.id) },91 }]92 : []),93 ]94 95 if (products.length < batchSize) {96 return97 }98 99 cursor = products[products.length - 1].id100 }101 },102})
The seed function reads each page of products once per locale with Query's locale option, then writes each locale's title to its own field. It falls back to the product's stored title when a locale has no translation. It also pages by ID so an interrupted run resumes, and yields a delete write for the products the catch-up pass finds deleted.
A Dutch query for banken now matches a product whose title holds bank, and a German query for stühle matches Stuhl.
Supported Languages in Medusa Search#
Medusa Search stems the following languages:
arabic(no stop-word list)danishdutchenglishfinnishfrenchgermangreek(no stop-word list)hungarianitaliannorwegianportugueseromanian(no stop-word list)russianspanishswedishtamil(no stop-word list)turkish(no stop-word list)
remove_stopwords has no effect on the languages marked above with (no stop-word list). For a language written in Latin script with diacritics, such as French or Spanish, also set ascii_folding so a query without the accents still matches.
A field holds one language, since it holds one string. To search a catalog in more than one market, declare a field per locale as explained in Search Text in Multiple Locales, then pass the attributes_to_search_on search option to search the field of the shopper's locale.
locales search option.Search Query Options#
Pass provider_options to query.search options to change how Medusa Search runs that query.
For example:
Medusa Search reads one query option, consistency, which accepts the following values: