Search and relevance

Language groups

Let each store of a multilingual shop find its products by the other stores' text, so an English search on the Estonian store still finds the Estonian product.

What a language group does

Many shops run one storefront per language. A Magento shop with Estonian, English and Russian store views syncs each view into its own index. The products are the same and only the text differs. A shopper on the Estonian store who types "red dress" finds nothing, because the Estonian index only holds "Punane kleit".

A language group connects those indices. Each member keeps a copy of the other members' text for the same product, analyzed in the language it was written in. The copies show up as fields in the member's mapping, and each search profile decides how much they count. With the English title in the Estonian store's profile, "red dress" on the Estonian store finds Punane kleit. The result still shows the Estonian name, price and URL. Facets, filters, sorting and highlighting use the store's own fields, so nothing from the English store shows up on the Estonian one.

Search speed does not change: it is still one request to the search engine.

Create a group

Only organization owners and admins can create, change and delete groups. You need at least two indices in the project, one per store language. API keys and AI agents cannot manage groups, see API.

  1. Open the project and click Language groups in the project menu, then New group.
  2. Enter a Name, for example Storefronts.
  3. Under Indices, pick one index per store. See Indices below.
  4. Under Match products by, pick the field that is the same for one product in every store. Product id fits most shops. See Match products by.
  5. Check the Shared fields: the text the other stores borrow. See Shared fields.
  6. Click Create group.

The cabinet then asks which search profiles should search the borrowed text. See Search the borrowed text.

The table lists each group with its Stores, what it matches by (Match by) and its Status. Edit on a row opens the same form.

The settings in the form:

Indices

indexIdsType list of index idsValues 2 to 10 indices of the project

What it does

Pick one index per store. The list shows each index's language, and two indices with the same language cannot be in one group. An index can be in one group at a time.

Match products by

joinType field nameDefault idValues id, or a keyword field every member can filter on

What it does

The field whose value is the same for one product in every member. Product id matches documents with the same id. This is the right choice for Magento, where a product has the same entity id in every store view. When ids differ between stores, pick another field, such as sku. The list offers only the keyword fields that every picked index maps as filterable.

On save, the field must name one product: in each member that has documents, nearly every product has its own value, and at least one value is also found in another member. A field shared by many products, such as a type or a category, or one whose values differ per store, such as a translated URL key, fails with 400 LANGUAGE_GROUP_JOIN. A product missing from some members is fine, and so is a member with no documents yet. When one store has several documents with the same value, the one with the lowest id lends its text.

Shared fields

sharedFieldsType list of field namesValues 1 to 20 fields

What it does

The text the other members borrow. Each shared field has to be searchable in every member. The form starts with every field the members can all search, minus fields whose name contains "description". Descriptions are long, they make every index bigger, and product names usually decide the match anyway. Add them if your shoppers search by words that only appear in descriptions.

After you save, each member gets the other members' text in the background. The Status column shows Syncing with the number of sync tasks still running, then Ready. Each index overview shows the group it belongs to, with a link to this page.

Search the borrowed text

Saving a group adds one field per shared field and source language to each member's mapping. On the Estonian store of an Estonian, English and Russian group that shares title, the mapping gets zs_alt.english.title and zs_alt.russian.title. A field shared from under attributes keeps that part of its name, as in zs_alt.english.attributes.color.

In Fields & mapping (Mapping and fields) these fields sit in their own section, named after the group. They are always searchable. You can change their text analysis options, and you can make them returnable so search results carry the other stores' text. Those changes work like they do on any other field. Type and language stay fixed, and borrowed text can't be filtered, faceted or sorted. The group owns the fields, so Remove explains that instead of removing one. To drop a field, take it out of the group's shared fields.

Saving the group does not change your search profiles, and a profile searches only the fields it lists. So right after you save, the cabinet opens Next, search the borrowed text and asks which profiles should search the new fields. It lists every member's profiles and how many borrowed fields each one already searches. Profiles that search none yet are ticked.

Each borrowed field gets a share of the weight the profile gives the store's own field. The share is the Weight in the dialog and starts at 0.3, so an Estonian title at weight 5 gets the English title at 1.5, and a product that matches in the store's own language usually ranks first. Set it to 1 to copy the weights as they are. A borrowed field is only added where the profile searches the store's own field. The update button, such as Update 2 profiles, saves the ticked profiles. Later closes the dialog, and Profiles on the group's row opens it again. Open the profile next to each profile opens it in the editor.

You can also do it by hand. In a member's search profile, the borrowed fields have their own list under the store's own fields in Fields and weights, headed From other stores in the language group. Each is labelled with its field and store, such as title · from English (US). Switch on the ones you want and give each its own weight. A field switched on there starts at the same 0.3 share. A new profile includes every searchable field, borrowed ones too.

Ranking rules can change the weight of a borrowed field like any other query field.

Through the API, a profile names a borrowed field by its full name:

{ "queryFields": { "title": 5, "zs_alt.english.title": 1.5, "zs_alt.russian.title": 1 } }

Keeping the text current

You do not have to do anything when products change. When the English store updates or deletes a product, the Estonian and Russian indices get the new English text a moment later, and an upload to the Estonian store keeps the text it borrowed. A full sync, a flush and a reindex of any member update the others the same way.

Changing an index's language while it is in a group is refused. Remove it from the group first.

See where a match came from

In the playground's score breakdown, a borrowed field reads title from English (US). Search explain in the API reports the store's field name and marks the copy with borrowedFrom:

{ "field": "title", "weight": 1.5, "score": 2.1, "borrowedFrom": "english" }

Storage

Document counts do not change. Each member stores the other members' shared text, so an index in a group of three grows by about twice its shared text. This is why descriptions are left out by default.

Delete a group

Delete on the group's row asks you to confirm, and Delete group removes the borrowed fields from every member's mapping and the borrowed text from their documents. Each index keeps its own fields, profiles, rules and synonyms.

A profile or rule that still searches a borrowed field blocks the delete with 409 MAPPING_BREAKS_CONFIG, and details lists each use. The same goes for an edit that would drop a borrowed field, such as removing a member or a shared field. Take the field out of those profiles and rules first. Deleting a member index is never blocked: the other members drop the fields it lent, and those fields are taken out of their profiles and rule field weights. A profile that searched nothing else, or a rule whose only action weighted those fields, is left as it was, and search skips what is no longer mapped until you change it.

API

The cabinet is the only way to create, change or delete a group: signed-in users manage groups. API keys and MCP agents can see which group an index is in (languageGroup on the index) but cannot change groups, because a key limited to one index must not change the others.

POST /v1/projects/:projectId/language-groups
{ "name": "Storefronts", "indexIds": ["…et", "…en", "…ru"], "join": "id", "sharedFields": ["name", "categories"] }

The answer holds the group and the ids of the sync tasks it queued. Leave out sharedFields to share what the form would prefill. See the REST API reference for every route.