Skip to content

search-algolia API

Everything the package exports, by the path you import it from. Open an export for its signature and options.

@evolve-framework/search-algolia

Both adapters, the index naming (defaultIndexName, localeIndexName), and the record mappers (toAlgoliaRecord and toProductDocument, toVariantRecord and fromVariantRecord)

Classes

AlgoliaIndexAdapterWrites documents to Algolia.
import { AlgoliaIndexAdapter } from "@evolve-framework/search-algolia";

Every method throws on failure rather than logging and returning: a swallowed error acks the queue message that triggered the write, losing the update with no trace.

new AlgoliaIndexAdapter(options: AlgoliaIndexAdapterOptions<TRecord>)
OptionTypeDescription
appIdstring

The Algolia application.

writeApiKeystring

A key allowed to write and configure indices. Never give it a browser.

resolveIndexName?IndexNameResolver

Override index naming to match an existing convention. Defaults to defaultIndexName.

toRecord?((document: ProductDocument, scope: IndexScope) => TRecord | TRecord[])

Override how a document becomes stored records. Returning several records fans one document out; the sync engine's deleteBySource still cleans them up. Defaults to toAlgoliaRecord.

More

The scope is passed so a record can be written for the target it is going to — flattening localized values to the index's language, say.

batchSize?number

Algolia accepts up to 1000 objects per batch.

atomicReindex?boolean

Rebuild through a temporary index that is moved into place. Defaults to true, and there is rarely a reason to turn it off — a move to a destination that has replicas is supported and propagates the new data to them.

More

Setting it to false does not make this adapter rebuild in place: it drops beginFullReindex and clears capabilities.fullReindex, leaving the sync engine to write over the live index. That is visible to shoppers while it runs, and it leaves behind documents that no longer exist, because nothing clears the index first.

MethodDescription
init(): Promise<void>One-shot, idempotent initialization (build SDK clients, etc.).
upsertDocuments( scope: IndexScope, documents: ProductDocument[], ): Promise<void>Write documents into the scope's target for each one's type, replacing any stored document with the same id as a whole.
deleteDocuments( scope: IndexScope, ids: string[], documentTypes: string[], ): Promise<void>Remove ids from every declared type's index.
deleteBySource( scope: IndexScope, sourceIds: string[], options: DeleteBySourceOptions, ): Promise<void>Remove every record a product owns, except the ids just written, across every declared type's index.
applySettings(scope: IndexScope, settings: unknown): Promise<void>Push provider index configuration (Algolia settings and replicas, …).
AlgoliaQueryAdapterReads documents back out of Algolia.
import { AlgoliaQueryAdapter } from "@evolve-framework/search-algolia";

Separate from the index adapter so an API deployment holds only a search key.

new AlgoliaQueryAdapter(options: AlgoliaQueryAdapterOptions<TRecord>)
OptionTypeDescription
appIdstring

The Algolia application.

searchApiKeystring

A search-only key: this adapter never writes.

resolveIndexName?IndexNameResolver

Defaults to defaultIndexName.

toDocument?((record: TRecord, locale: string) => ProductDocument)

Inverse of the index adapter's toRecord. Defaults to toProductDocument.

More

The request's locale is passed so a record whose localized values were flattened for its index can be read back under the locale being asked for.

buildQuery?((queries: SearchForHits[], query: ProductSearchQuery) => SearchForHits[])

Adjust the queries before they are sent — rule contexts, optional filters, distinct, analytics tags, personalization. Receives the defaults so it can extend rather than replace them.

distinct?number | boolean

Deduplicate hits by the index's attributeForDistinct.

More

Required by the variant-record model: with a record per variant, a search without this returns every variant of a product as its own row. Set it to true (one per group) or a count. Leave unset when records are per product.

scopeGranularity?"language" | "locale" | "store"

Must match the scopeGranularity of the document source the index is built from, such as CommercetoolsCatalogSource, so both halves resolve the same index name.

More

"language" (the default) and "locale" both read from an index named after the requested locale — which name resolver is configured decides whether the region survives into it. "store" reads from one index holding every language, which also means the query needs a language filter and per-request queryLanguages — see buildQuery.

paginationLimitedTo?number

The index's paginationLimitedTo setting, when it was changed from Algolia's default of 1,000: hits past it cannot be paged to.

clickAnalytics?boolean

Ask Algolia for a query id on every search, which click and conversion events send back so Insights attributes them to the search. Off by default, as Algolia only issues one when asked.

querySuggestions?boolean

Read suggestions from the index's Algolia Query Suggestions index, named by resolveIndexName with suggestions: true. A search that finds nothing then tries the top suggestion instead. Off by default, as the index has to be set up in Algolia first.

MethodDescription
init(): Promise<void>Create the Algolia client from appId and searchApiKey.
search( query: ProductSearchQuery, ): Promise<SearchResponse<ProductDocument>>Run a query and return the documents that matched.
suggest(query: SuggestQuery): Promise<SuggestResponse>Implement when capabilities.suggestions is true.

Functions

createCategoryPathTurn a category path into Algolia's breadcrumb strings.
import { createCategoryPath } from "@evolve-framework/search-algolia";

Each level repeats its ancestors, which is what Algolia's hierarchical menu widget expects: ["clothes", "clothes > men", "clothes > men > pants"].

function createCategoryPath(path: CategoryPath): string[]

Returns string[]

defaultIndexName<what>-<store>[-<language>][-<sort>], e.g.
import { defaultIndexName } from "@evolve-framework/search-algolia";

The segment is the language, which is what Algolia's own language settings are keyed on (indexLanguages takes ISO 639-1). It appears when every locale in the scope shares one language — so a scope of ["nl-NL", "nl-BE"] is products-<store>-nl, and a scope spanning several languages gets no segment at all.

That rule is what keeps the halves in step: a per-language deployment names the language, a per-store one does not, and neither needs a second setting that could disagree with the first. Pair localeIndexName with per-locale scopes when regional variants need indices of their own.

The leading segment names what the index holds, which is also the document type — a second index of bundles is bundles-evolve-nl. The primary index carries no sort segment; only replicas do.

Algolia has no per-request sort — each order is a replica index of its own — so the sort segment is the read side's business. Writes always go to the primary and Algolia propagates them to the replicas.

function defaultIndexName(
scope: { storeKey: string; locales?: string[] | undefined; },
context: IndexNameContext,
): string
OptionTypeDescription
storeKeystring
documentTypestring
locales?string[]
sort?SortSpec

Set on the read path; picks a sort replica.

suggestions?boolean

Set when querying the query-suggestions index.

Returns string

expandTextPut a flattened value back under the locale it is being read for, so the read side sees the same shape whichever granularity wrote the record.
import { expandText } from "@evolve-framework/search-algolia";
function expandText(
value: RecordText | undefined,
locale: string,
): LocalizedText | undefined

Returns LocalizedText | undefined

flattenTextReduce a localized value to the one string a per-language index needs.
import { flattenText } from "@evolve-framework/search-algolia";

Worth doing rather than storing the map: Algolia caps record size (10KB on some plans) and its guidance is to send only what search and display use, and a flattened record also lets facets and searchable attributes be declared as attributes.color instead of one path per locale.

function flattenText(
value: LocalizedText | undefined,
targets: string[] | undefined,
): RecordText | undefined

Returns RecordText | undefined

fromVariantRecordRebuild the document.
import { fromVariantRecord } from "@evolve-framework/search-algolia";

Flattened text goes back under locale, so the read side sees the same shape whichever granularity wrote the record. Pass the locale the query was made for — with per-language indices a nl-BE request reads a record flattened from nl-NL, and it is the request's locale the GraphQL layer looks up.

function fromVariantRecord(
record: AlgoliaVariantRecord,
locale?: string,
): ProductDocument
OptionTypeDescription
objectIDstring
sourceIdstring

The product this variant belongs to; the deduplication key.

typestring
nameRecordText
slugRecordText
categoriesRecordCategoryPath[]
hierarchicalCategoriesProductCategoryHierarchy

Algolia's hierarchical-menu shape, derived from primaryCategory.

categoryPageIdstring[]

Breadcrumb strings, for the hierarchical menu and category facets.

categoryIdsstring[]

Every category the product is in, ancestors included.

More

Ancestors come for free because a document's category paths run root → leaf, and including them is what makes a category page behave like commercetools' categoriesSubTree: a product filed under books_fiction shows on the books page too.

variantIdstring
skustring
imagesstring[]
pricesPriceDocument[]

The representative variant's own prices.

startingPricePriceDocument[]

Lowest price across the group, one entry per currency/country.

More

Stored in its own attribute because it cannot be recovered from the rest of the record: the mapper computes it across every variant a group may span, including variants of related products, so reconstructing it from prices — or from a scan of variants — silently substitutes the representative variant's price for a real group minimum.

attributesRecordAttributeMap
variants{ id: string; sku: string; images: string[]; prices: PriceDocument[]; attributes: RecordAttributeMap; dimensions?: RecordDimension[] | undefined; availability?:…

Every variant in the group, including the representative one.

brand?string
meta?{ title?: RecordText | undefined; description?: RecordText | undefined; }
primaryCategory?RecordCategoryPath
variantDimensions?RecordVariantDimension[]

The full picker matrix; a deduplicated hit still renders every option.

image?string
price?PriceDocument

The group's lowest price, for ranking. Taken from the document's startingPrice rather than the representative variant, whose position in the group says nothing about its price.

rating?number

Average review rating, the attribute a rating-desc replica ranks on.

dimensions?RecordDimension[]
availability?RecordAvailability
ribbon?RecordRibbon
quantityRule?QuantityRuleDocument
localizedAttributes?string[]

Attribute keys whose values were localized before flattening.

More

A flattened { color: "groen" } is indistinguishable from an attribute that was always a plain string, so without this the read side cannot rebuild the map and a consumer reading attributes.color for the request's locale would get nothing.

extraKeys?string[]

Which top-level keys came from document.extra.

More

Consumer fields are spread flat so they are directly filterable, which also makes them indistinguishable from the adapter's own on the way back. Recording the key names is what lets the read side rebuild extra — the same problem localizedAttributes solves for flattened text, and necessary here because this record carries no _doc blob to recover them from.

Returns ProductDocument

localeIndexNameThe full locale rather than its language subtag: products-evolve-nl-be.
import { localeIndexName } from "@evolve-framework/search-algolia";

Necessary when a store serves several variants of one language, where defaultIndexName would resolve them to the same index.

function localeIndexName(
scope: { storeKey: string; locales?: string[] | undefined; },
context: IndexNameContext,
): string
OptionTypeDescription
storeKeystring
documentTypestring
locales?string[]
sort?SortSpec

Set on the read path; picks a sort replica.

suggestions?boolean

Set when querying the query-suggestions index.

Returns string

localeTargetsThe locales to try, in order, when flattening a localized value for a scope — or undefined when the scope spans several languages and the maps have to be kept.
import { localeTargets } from "@evolve-framework/search-algolia";

The bare language comes first, then the scope's locales in order: a Dutch index built from ["nl-NL", "nl-BE"] prefers a nl value and falls back to nl-NL, then nl-BE. Commercetools projects are usually keyed by locale rather than language, so the fallback is what actually supplies the text.

function localeTargets(locales: string[] | undefined): string[] | undefined

Returns string[] | undefined

toAlgoliaRecordMap a document to the stored record.
import { toAlgoliaRecord } from "@evolve-framework/search-algolia";

centAmount comes from the gross amount. commercetools stores one amount per price and a flag saying whether tax is included; with no tax configured the document's net and gross are both that stored amount, so this reproduces what the previous mapper wrote. Once tax is configured, gross is the shopper-facing figure.

function toAlgoliaRecord(document: ProductDocument): AlgoliaProductRecord

Returns AlgoliaProductRecord

toProductDocumentMap a stored record back to a document, for the read path.
import { toProductDocument } from "@evolve-framework/search-algolia";

Lossy by construction: the record was never designed to round-trip. Fields it does not carry (per-locale category names, net prices, variant dimensions) come back absent rather than empty, so the GraphQL layer hydrates them instead of rendering a stub.

function toProductDocument(record: StoredAlgoliaProductRecord): ProductDocument
OptionTypeDescription
imagestring | undefined
objectIDstring
nameLocalizedText
slugLocalizedText
descriptionLocalizedText
variantsAlgoliaProductVariant[]
categoryPageIdstring[]
categoryIdsstring[]

Every category the product is in, ancestors included.

hierarchicalCategoriesProductCategoryHierarchy
startingPriceAlgoliaProductPrice
rating?number

Average review rating, the attribute a rating-desc replica ranks on.

type?string

Document type, for indices holding more than products.

_doc?ProductDocument

The canonical document, stored verbatim.

More

The fields above are the shape this integration has always written, and the storefront's InstantSearch code reads them directly — so they stay. But that shape cannot express everything a document carries (localized category paths, the variant dimension matrix, ribbons), and a field the record drops is a field the read side has to fetch from the commerce backend. Keeping the document itself makes the round trip lossless.

Absent on records written before this existed; those fall back to reconstructing what the legacy fields can express.

Costs record size — roughly double. Algolia caps a record at 10KB on some plans; a catalog with very large products should check.

sourceId?string

Returns ProductDocument

toVariantRecordMap a document to its record.
import { toVariantRecord } from "@evolve-framework/search-algolia";

VariantDocumentMapper produces one document per variant, or per group when given a groupBy. Either way the first variant is the representative one carrying the card's sku, image and price, while the group as a whole is kept in variants.

The scope decides how localized values are stored: a per-language index gets plain strings, an index holding several languages keeps the maps.

function toVariantRecord(
document: ProductDocument,
scope?: Pick<IndexScope, "locales">,
): AlgoliaVariantRecord
OptionTypeDescription
localesstring[]

Locales to index content for.

Returns AlgoliaVariantRecord

Types

AlgoliaAttributeValue
import { type AlgoliaAttributeValue } from "@evolve-framework/search-algolia";
export type AlgoliaAttributeValue = string | boolean | Record<string, string>;
AlgoliaIndexAdapterOptions
import { type AlgoliaIndexAdapterOptions } from "@evolve-framework/search-algolia";
export type AlgoliaIndexAdapterOptions<TRecord extends AlgoliaRecordBase = AlgoliaProductRecord> = {
appId: string;
writeApiKey: string;
resolveIndexName?: IndexNameResolver;
toRecord?: (document: ProductDocument, scope: IndexScope) => TRecord | TRecord[];
batchSize?: number;
atomicReindex?: boolean;
};
AlgoliaProductPrice
import { type AlgoliaProductPrice } from "@evolve-framework/search-algolia";
export type AlgoliaProductPrice = {
currency: string;
centAmount: number;
};
AlgoliaProductRecordThe record shape stored in Algolia.
import { type AlgoliaProductRecord } from "@evolve-framework/search-algolia";

Deliberately unchanged from what @evolve-framework/commercetools/algolia has always written: live indices and the InstantSearch code reading them stay untouched by this refactor. The canonical ProductDocument is the contract between framework and adapter, not the storage format — this module maps between the two in both directions.

export type AlgoliaProductRecord = {
objectID: string;
name: LocalizedText;
slug: LocalizedText;
description: LocalizedText;
variants: AlgoliaProductVariant[];
categoryPageId: string[];
categoryIds: string[];
hierarchicalCategories: ProductCategoryHierarchy;
startingPrice: AlgoliaProductPrice;
rating?: number;
image: string | undefined;
sourceId: string;
type?: string;
_doc?: ProductDocument;
};
AlgoliaProductVariant
import { type AlgoliaProductVariant } from "@evolve-framework/search-algolia";
export type AlgoliaProductVariant = {
id: string;
sku: string;
image: string | undefined;
price: AlgoliaProductPrice;
attributes: Record<string, AlgoliaAttributeValue>;
};
AlgoliaQueryAdapterOptions
import { type AlgoliaQueryAdapterOptions } from "@evolve-framework/search-algolia";
export type AlgoliaQueryAdapterOptions<TRecord = AlgoliaProductRecord> = {
appId: string;
searchApiKey: string;
resolveIndexName?: IndexNameResolver;
toDocument?: (record: TRecord, locale: string) => ProductDocument;
buildQuery?: (queries: SearchForHits[], query: ProductSearchQuery) => SearchForHits[];
distinct?: boolean | number;
scopeGranularity?: "language" | "locale" | "store";
paginationLimitedTo?: number;
clickAnalytics?: boolean;
querySuggestions?: boolean;
};
AlgoliaVariantRecordA record per variant, or per group of variants.
import { type AlgoliaVariantRecord } from "@evolve-framework/search-algolia";

Flat and explicit rather than a copy of the document: Algolia's guidance is to send only what search and display need, since record size is capped (10KB on some plans). Every field here is searched, filtered, or rendered on a card.

Shared attributes sit at attributes.* rather than nested under variants. That is the point of the model: a facet filter applies to the record, so "green AND size 40" cannot match a product that is green in 38 and black in 40 — which is what the product-level model does unless the frontend post-processes the hits.

sourceId is the product, and doubles as attributeForDistinct: with distinct enabled a search returns the best-matching record per product.

export type AlgoliaVariantRecord = {
objectID: string;
sourceId: string;
type: string;
name: RecordText;
slug: RecordText;
brand?: string;
meta?: {
title?: RecordText;
description?: RecordText;
};
categories: RecordCategoryPath[];
primaryCategory?: RecordCategoryPath;
hierarchicalCategories: ProductCategoryHierarchy;
categoryPageId: string[];
categoryIds: string[];
variantDimensions?: RecordVariantDimension[];
variantId: string;
sku: string;
image?: string;
images: string[];
price?: PriceDocument;
prices: PriceDocument[];
startingPrice: PriceDocument[];
rating?: number;
attributes: RecordAttributeMap;
dimensions?: RecordDimension[];
availability?: RecordAvailability;
ribbon?: RecordRibbon;
quantityRule?: QuantityRuleDocument;
// …
IndexNameContext
import { type IndexNameContext } from "@evolve-framework/search-algolia";
export type IndexNameContext = {
documentType: string;
sort?: SortSpec;
suggestions?: boolean;
};
IndexNameResolver
import { type IndexNameResolver } from "@evolve-framework/search-algolia";
export type IndexNameResolver = (scope: {
storeKey: string;
locales?: string[];
}, context: IndexNameContext) => string;
ProductCategoryHierarchy
import { type ProductCategoryHierarchy } from "@evolve-framework/search-algolia";
export type ProductCategoryHierarchy = {
lvl0: string | undefined;
lvl1: string | undefined;
lvl2: string | undefined;
lvl3: string | undefined;
};
RecordAttributeMap
import { type RecordAttributeMap } from "@evolve-framework/search-algolia";
export type RecordAttributeMap = Record<string, RecordAttributeValue>;
RecordAvailability
import { type RecordAvailability } from "@evolve-framework/search-algolia";
export type RecordAvailability = {
inStock: boolean;
quantity?: number;
deliveryTime?: RecordText;
};
RecordCategoryLocalized values are reduced to a single string on a per-language index, and kept as maps when one index holds several languages — so every text field is either.
import { type RecordCategory } from "@evolve-framework/search-algolia";
export type RecordCategory = {
id: string;
key: string;
name: RecordText;
slug: RecordText;
description?: RecordText;
};
RecordCategoryPath
import { type RecordCategoryPath } from "@evolve-framework/search-algolia";
export type RecordCategoryPath = RecordCategory[];
RecordDimension
import { type RecordDimension } from "@evolve-framework/search-algolia";
export type RecordDimension = {
key: string;
label: RecordText;
value: string;
valueLabel: RecordText;
};
RecordRibbon
import { type RecordRibbon } from "@evolve-framework/search-algolia";
export type RecordRibbon = {
kind: string;
label: RecordText;
};
RecordTextA text field in a stored record: a plain string on a per-language index, or the locale map when one index holds several languages.
import { type RecordText } from "@evolve-framework/search-algolia";
export type RecordText = string | LocalizedText;
RecordVariantDimension
import { type RecordVariantDimension } from "@evolve-framework/search-algolia";
export type RecordVariantDimension = {
name: string;
label: RecordText;
type: "text" | "color" | "image";
values: {
value: string;
label: RecordText;
color?: string;
variants: VariantRef[];
}[];
};

@evolve-framework/search-algolia/indexing

AlgoliaIndexAdapter

Classes

AlgoliaIndexAdapterWrites documents to Algolia.
import { AlgoliaIndexAdapter } from "@evolve-framework/search-algolia/indexing";

Every method throws on failure rather than logging and returning: a swallowed error acks the queue message that triggered the write, losing the update with no trace.

new AlgoliaIndexAdapter(options: AlgoliaIndexAdapterOptions<TRecord>)
OptionTypeDescription
appIdstring

The Algolia application.

writeApiKeystring

A key allowed to write and configure indices. Never give it a browser.

resolveIndexName?IndexNameResolver

Override index naming to match an existing convention. Defaults to defaultIndexName.

toRecord?((document: ProductDocument, scope: IndexScope) => TRecord | TRecord[])

Override how a document becomes stored records. Returning several records fans one document out; the sync engine's deleteBySource still cleans them up. Defaults to toAlgoliaRecord.

More

The scope is passed so a record can be written for the target it is going to — flattening localized values to the index's language, say.

batchSize?number

Algolia accepts up to 1000 objects per batch.

atomicReindex?boolean

Rebuild through a temporary index that is moved into place. Defaults to true, and there is rarely a reason to turn it off — a move to a destination that has replicas is supported and propagates the new data to them.

More

Setting it to false does not make this adapter rebuild in place: it drops beginFullReindex and clears capabilities.fullReindex, leaving the sync engine to write over the live index. That is visible to shoppers while it runs, and it leaves behind documents that no longer exist, because nothing clears the index first.

MethodDescription
init(): Promise<void>One-shot, idempotent initialization (build SDK clients, etc.).
upsertDocuments( scope: IndexScope, documents: ProductDocument[], ): Promise<void>Write documents into the scope's target for each one's type, replacing any stored document with the same id as a whole.
deleteDocuments( scope: IndexScope, ids: string[], documentTypes: string[], ): Promise<void>Remove ids from every declared type's index.
deleteBySource( scope: IndexScope, sourceIds: string[], options: DeleteBySourceOptions, ): Promise<void>Remove every record a product owns, except the ids just written, across every declared type's index.
applySettings(scope: IndexScope, settings: unknown): Promise<void>Push provider index configuration (Algolia settings and replicas, …).

Types

AlgoliaIndexAdapterOptions
import { type AlgoliaIndexAdapterOptions } from "@evolve-framework/search-algolia/indexing";
export type AlgoliaIndexAdapterOptions<TRecord extends AlgoliaRecordBase = AlgoliaProductRecord> = {
appId: string;
writeApiKey: string;
resolveIndexName?: IndexNameResolver;
toRecord?: (document: ProductDocument, scope: IndexScope) => TRecord | TRecord[];
batchSize?: number;
atomicReindex?: boolean;
};

@evolve-framework/search-algolia/query

AlgoliaQueryAdapter, plus buildQueries and mapFacets for a query adapter of your own

Classes

AlgoliaQueryAdapterReads documents back out of Algolia.
import { AlgoliaQueryAdapter } from "@evolve-framework/search-algolia/query";

Separate from the index adapter so an API deployment holds only a search key.

new AlgoliaQueryAdapter(options: AlgoliaQueryAdapterOptions<TRecord>)
OptionTypeDescription
appIdstring

The Algolia application.

searchApiKeystring

A search-only key: this adapter never writes.

resolveIndexName?IndexNameResolver

Defaults to defaultIndexName.

toDocument?((record: TRecord, locale: string) => ProductDocument)

Inverse of the index adapter's toRecord. Defaults to toProductDocument.

More

The request's locale is passed so a record whose localized values were flattened for its index can be read back under the locale being asked for.

buildQuery?((queries: SearchForHits[], query: ProductSearchQuery) => SearchForHits[])

Adjust the queries before they are sent — rule contexts, optional filters, distinct, analytics tags, personalization. Receives the defaults so it can extend rather than replace them.

distinct?number | boolean

Deduplicate hits by the index's attributeForDistinct.

More

Required by the variant-record model: with a record per variant, a search without this returns every variant of a product as its own row. Set it to true (one per group) or a count. Leave unset when records are per product.

scopeGranularity?"language" | "locale" | "store"

Must match the scopeGranularity of the document source the index is built from, such as CommercetoolsCatalogSource, so both halves resolve the same index name.

More

"language" (the default) and "locale" both read from an index named after the requested locale — which name resolver is configured decides whether the region survives into it. "store" reads from one index holding every language, which also means the query needs a language filter and per-request queryLanguages — see buildQuery.

paginationLimitedTo?number

The index's paginationLimitedTo setting, when it was changed from Algolia's default of 1,000: hits past it cannot be paged to.

clickAnalytics?boolean

Ask Algolia for a query id on every search, which click and conversion events send back so Insights attributes them to the search. Off by default, as Algolia only issues one when asked.

querySuggestions?boolean

Read suggestions from the index's Algolia Query Suggestions index, named by resolveIndexName with suggestions: true. A search that finds nothing then tries the top suggestion instead. Off by default, as the index has to be set up in Algolia first.

MethodDescription
init(): Promise<void>Create the Algolia client from appId and searchApiKey.
search( query: ProductSearchQuery, ): Promise<SearchResponse<ProductDocument>>Run a query and return the documents that matched.
suggest(query: SuggestQuery): Promise<SuggestResponse>Implement when capabilities.suggestions is true.

Functions

buildQueriesBuild the Algolia queries for one product search.
import { buildQueries } from "@evolve-framework/search-algolia/query";

More than one query is sent: the first returns the hits, and each active facet filter gets a companion query with that filter removed. Without them a facet would only ever report the options still matching the current selection, so a shopper could never widen a choice they had already narrowed.

function buildQueries(
indexName: string,
query: ProductSearchQuery,
): SearchForHits[]

Returns SearchForHits[]

mapFacetsTurn Algolia's facet payload into the normalized results.
import { mapFacets } from "@evolve-framework/search-algolia/query";

Algolia has no notion of a facet kind — every facet comes back as a value histogram, plus a facets_stats entry when the values are numeric. Which of those is meant to render as a range slider is a presentation decision, and Algolia keeps it in the dashboard's renderingContent. So the kind is inferred here rather than being declared anywhere in the document.

renderingContent selects and orders the facets when it is set. When it is not, every facet Algolia returned is surfaced and the kind is inferred from whether the values are numeric — an unconfigured index shows its facets with raw attribute keys for labels, rather than showing none.

function mapFacets(
hits: SearchResponse<unknown> | undefined,
facetResponses: SearchResponse<unknown>[],
filters: FacetFilter[],
currency?: string,
): FacetResult[]

Returns FacetResult[]

Types

AlgoliaQueryAdapterOptions
import { type AlgoliaQueryAdapterOptions } from "@evolve-framework/search-algolia/query";
export type AlgoliaQueryAdapterOptions<TRecord = AlgoliaProductRecord> = {
appId: string;
searchApiKey: string;
resolveIndexName?: IndexNameResolver;
toDocument?: (record: TRecord, locale: string) => ProductDocument;
buildQuery?: (queries: SearchForHits[], query: ProductSearchQuery) => SearchForHits[];
distinct?: boolean | number;
scopeGranularity?: "language" | "locale" | "store";
paginationLimitedTo?: number;
clickAnalytics?: boolean;
querySuggestions?: boolean;
};
FacetStats
import { type FacetStats } from "@evolve-framework/search-algolia/query";
export type FacetStats = {
min?: number;
max?: number;
};