AACsearch
Connectoren & Widget

Connector Field Mapping Reference

Per-entity mapping rules: products, categories, variants, prices, stock, images and locales. The contract every CMS module follows when shipping data into AACsearch.

This page is the definitive reference for what a CMS module must send to AACsearch. The endpoint shapes live in Connector API Lifecycle; this page covers the semantics — which CMS-side field maps to which AACsearch field and why.

Every connector ships SyncProductInput documents. The shape is intentionally narrow (it's a product index, not a generic content store) but flexible enough to model articles, recipes, or any other entity through attributes.

Product fields

FieldTypeRequiredNotes
external_idstringyesStable platform primary key. Never a slug, never a SKU.
titlestringyesPlain text in the configured locale. Strip HTML and shortcodes.
descriptionstringnoPlain text. Strip HTML, render rich-text JSON / markdown to text.
skustringnoPrimary or default-variant SKU. Send the displayed SKU, not internal IDs.
brandstringnoManufacturer or vendor name. Empty string → drop the field.
categoriesstring[]noSee Categories.
category_idsstring[]noPlatform category IDs for filters. Stable across renames.
tagsstring[]noFlat list of free-form labels.
pricenumbernoMajor units. See Prices.
sale_pricenumbernoMajor units. See Prices.
currencystringnoISO 4217, 3 letters. Lowercase rejected by the validator.
image_urlstring (URL)noPrimary image. See Images.
product_urlstring (URL)noStorefront URL used by the widget for click-through.
availabilityenumno"in_stock", "out_of_stock", "preorder". See Stock.
stock_quantitynumbernoTotal available units across locations.
attributesRecord<string,unknown>noFree-form facet/display fields. See Attributes.
localeenumnoen, de, es, fr, ru. One document per locale.
created_atstring (ISO 8601)noUsed for "Recently added" sort.
updated_atstring (ISO 8601)noUsed for change detection in the buffer; recommended.

external_id rules

Use the CMS primary key. Specifically:

  • WooCommerce / WordPress: post_id (numeric)
  • PrestaShop: id_product
  • Bitrix: iblock_element_id
  • Shopify: numeric product ID, not the handle
  • Magento: entity_id, not SKU

If your platform models locale-specific entities as separate rows, append the locale: prod-123:en. Never reuse an external_id across product types or locales — searches will collide.

Categories

Two fields, used together:

  • categories — full breadcrumb path, top → leaf, human-readable strings. ["Shoes", "Running", "Trail"], not ["Trail"]. The widget renders the path for filters.
  • category_ids — stable platform IDs for the same path, top → leaf. Used for filter queries that survive category renames.

If a product lives under multiple categories, flatten them: send each path as a separate entry in categories. The order of the array does not matter for ranking; only string identity for facet counts.

CMScategories sourcecategory_ids source
PrestaShopps_category_lang.name along the pathps_category.id_category along the path
Bitrixiblock section namesiblock section IDs
WordPresscategory taxonomy termsterm IDs
Shopifyproduct_type + collection titlescollection IDs
Strapireference field, walked recursivelydocument IDs
Sanityreferences() walked recursively_id of category docs

Variants

The default model is product-level documents. Variant data — size, color, material, variant SKUs, per-variant stock — flattens into attributes. A single product becomes a single document.

This is the right choice for most e-commerce stores: shoppers search "blue running shoes," not "blue running shoes size 9.5 narrow." The widget shows the parent product card; variant selection happens on the product detail page.

When to ship variant-level documents

Build a custom mapper that emits one document per variant when:

  • Search must surface individual SKUs (B2B catalogs, parts stores).
  • Variants have wildly different titles or images (kit vs single unit).
  • Variants live in different categories.

In that case:

  • external_id is the variant ID, suffixed by parent: prod-123:variant-7.
  • title includes the variant attributes: "Blue Running Shoes — Size 9.5 Narrow".
  • image_url is the variant's image, not the parent's.
  • Add attributes.parent_external_id so a deduped "by-product" search can collapse results.

The Shopify reference connector uses product-level documents by default; see Custom Connector to build the variant-level variant.

Prices

Wire format

price and sale_price are major units, decimal numbers. 99.99 USD is sent as 99.99, not 9999. The Connector API converts to BigInt minor units (kopecks-style) inside the ingest pipeline. Sending 9999 will index the price as $9,999.00.

Two-price model

  • price — the list price the customer would pay without any active promotion.
  • sale_price — the promotional price, if active. When sale_price is set, the widget renders both with strikethrough on price.

When there are many prices

CMS systems with customer groups, B2B tiers, or dynamic pricing have one product but many prices. The simplest convention:

{
  "price": 99.99,
  "sale_price": 79.99,
  "currency": "USD",
  "attributes": {
    "price_by_group": {
      "retail": 99.99,
      "wholesale": 79.99,
      "vip": 69.99
    }
  }
}

price is the default storefront price. Other tiers go in attributes.price_by_group. The storefront filters or sorts on the right group at query time.

Multi-currency catalogs

Send the customer-facing currency as currency, plus alternatives in attributes.price_by_currency:

{
  "currency": "USD",
  "price": 99.99,
  "attributes": {
    "price_by_currency": { "EUR": 92.5, "GBP": 79.99 }
  }
}

AACsearch indexes the alternative prices as numeric attributes so the storefront can switch currency without re-indexing.

Stock

Two fields that the widget uses to label availability:

  • availability"in_stock" | "out_of_stock" | "preorder". Drives the badge on the result card.
  • stock_quantity — total units across all locations. Drives "Only 3 left" hints.

Mapping rules

CMS / platformavailability derivation
PrestaShopps_stock_available.quantity > 0 AND out_of_stock policy → in_stock
BitrixOnPriceUpdate event handler; CATALOG_AVAILABLE = 'Y'in_stock
WooCommerce_stock_status post meta: instock / outofstock / onbackorder (→ preorder)
ShopifySum of inventory_quantity across whitelisted locations
StrapiCustom field on the content type, or default to in_stock

A product with multiple locations is in stock if any whitelisted location has quantity > 0. Use connector config to whitelist locations.

Backorders and pre-orders

If your platform supports backorders or pre-orders, map them to preorder. The widget renders a distinct label. The product remains in search results — useful for "available soon" flows.

Images

image_url is the primary display image, full URL with scheme. The widget renders it on the result card.

For products with multiple images, send the full list in attributes.images:

{
  "image_url": "https://cdn.example.com/products/brs-42/main.jpg",
  "attributes": {
    "images": [
      { "url": "https://cdn.example.com/products/brs-42/main.jpg", "alt": "Front" },
      { "url": "https://cdn.example.com/products/brs-42/side.jpg", "alt": "Side" }
    ]
  }
}

Image rules

  • Use full URLs (https://...). Relative URLs break when the storefront renders the widget on a CDN edge.
  • Use the storefront-sized image, not the original. AACsearch does not resize.
  • If the platform serves WebP / AVIF, send the URL of the format the storefront actually shows.
  • Empty string is invalid — drop the field if you have no image.

Image fallbacks

The widget renders a placeholder when image_url is missing. If you have product-type-specific placeholders, set them through the widget theme rather than synthesizing a placeholder URL on the connector side.

Locales

AACsearch accepts five locales out of the box: en, de, es, fr, ru. If your CMS has a locale outside this set, send the closest neighbor and store the original code in attributes.language for display:

{
  "locale": "en",
  "attributes": {
    "language": "pt-BR",
    "language_label": "Português (Brasil)"
  }
}

Document split

For platforms with localized catalogs (Polylang, WPML, Shopify Markets, PrestaShop multistore, Strapi i18n):

  • One document per locale per entity.
  • external_id suffixed with the locale: prod-123:en, prod-123:de.
  • Title and description in the locale's language.
  • Categories in the locale's language too — never the default-locale strings.

Updates to one locale do not affect documents for other locales. The event handler should emit only the locale that changed.

Storefront locale resolution

The widget receives the locale from the storefront — usually from the URL prefix (/en/, /de/) or the lang HTML attribute. The widget sends locale=<code> on every search query, and AACsearch filters documents by the locale field. Documents without a locale value match every locale.

Attributes

Any field that is filterable, facetable, or display-only goes into attributes. Keys are free-form strings; values are primitives or arrays of primitives.

Type inference

AACsearch infers the index type from the first batch ingested under a fresh index:

Field shapeIndexed as
"red" (string)facet, exact match
42 (number)numeric, sort + range
42.5 (number)float, sort + range
true (boolean)filter only
["red", "blue"] (array)multi-facet
[1, 2, 3] (number array)not supported — flatten or split

If you change the type of an attribute mid-flight (e.g. send "42" after sending 42), AACsearch rejects the document. Pick a type per attribute and stick to it.

Naming conventions

  • Lowercase. Underscores for word separation. option_color, not OptionColor.
  • Prefix variant options with option_: option_color, option_size. The widget renders them under a "Variants" section automatically.
  • Prefix derived fields with a domain: price_by_currency, price_by_group, stock_by_location.
  • Reserve top-level field names (the ones listed above). Don't put title into attributes.

Per-platform reference

PlatformMapper file
PrestaShopmodules/prestashop/aacsearch/src/Exporter/
Bitrixmodules/bitrix/aac.search/lib/ProductExporter.php
WordPressmodules/wordpress/aacsearch-search/src/Mapper/
Shopifypackages/shopify-connector/src/product-mapper.ts
Strapipackages/strapi-plugin/src/server/services/aacsearch.ts

Read these as canonical examples. New connectors should follow the same field semantics — the inverse mapping (CMS → SyncProductInput) is platform-specific, but the wire format is invariant.

On this page