Products index v10: is_special missing from sortable-attributes — breaks BFF category browse (400 invalid_search_sort) #9

Closed
opened 2026-09-20 07:00:40 +00:00 by david · 1 comment
Owner

Summary

The fresh products index shoppy-test-v10 (created 2026-09-20, after #3 landed the attribute ranking-rule change) does not declare is_special as a sortable attribute. The Shoppy BFF's default CATEGORY sort is specials_first = ["is_special:desc", "categoryListingRank:asc"], so every real-category browse through the BFF fails with 400 against v10.

This blocks pointing the BFF at v10 (the index that carries the search-relevance fix from #3 / bff#13).

Evidence (2026-09-20, via Kong gateway)

Same request body against both indexes:

{"q":"","filter":["storeId IN ['woolworths']","categories.level1=fruit"],"sort":["is_special:desc","categoryListingRank:asc"], ...}
  • shoppy-test-v9200 (works)
  • shoppy-test-v10400:
{"message":"Index `shoppy-test-v10`: Attribute `is_special` is not sortable. Available sortable attributes are: `categoryListingRank, price.amountAsCents, price.comparativePricing.priceAsCents`.",
 "code":"invalid_search_sort","type":"invalid_request"}

Document-level check: v10 documents do carry the field (is_special: 0 observed on a fetched hit), while v9 documents don't carry it at all (yet v9's settings declared it sortable, so the sort was a no-op primary key there). So between the v9 and v10 runs both the data (field added) and the effective settings (sortable declaration dropped) changed.

BFF impact

  • productList(listType: CATEGORY) for any real category → 400 (Error getting products by category). Default sort is specials_first (to_ms_sort_option, src/graphql/queries/helpers.rs in shoppy/bff); the explicit-sort passthrough can also request it.
  • Virtual categories (specials / half-price-specials) are not affected — they filter on price.tags and sort by price only.
  • SEARCH is not affected (sorts use categoryListingRank / price.amountAsCents, both still sortable).

Fix

  1. Add is_special back to the products index settings' sortableAttributes in this repo's template(s) — wherever v10's settings came from (shoppy-products.json and/or the live template shoppy-indexes/shoppy-test.json consumed by product-sync via MEILISEARCH_INDEX_CONFIG_PRODUCTS). Check why it was dropped between the v9 and v10 runs so it doesn't recur.

  2. Apply to the live shoppy-test-v10 without waiting for a fresh run — the Kong gateway allows the sortable-attributes settings sub-route:

    PUT /indexes/shoppy-test-v10/settings/sortable-attributes
    {"sortableAttributes": ["categoryListingRank", "price.amountAsCents", "price.comparativePricing.priceAsCents", "is_special"]}
    

    (Preserve the existing three entries; confirm final state via GET .../settings/sortable-attributes.)

Validation

  • POST /indexes/shoppy-test-v10/search with sort: ["is_special:desc","categoryListingRank:asc"] and a category filter → 200.
  • BFF against v10: productList(listType: CATEGORY, argument: "fruit") with shoppy-stores: woolworths returns products (specials first).
  • Re-run the bff#13 regression checklist items that touch category browse.

Note on behaviour change

On v9 the is_special sort was inert (no document carried the field — all ties fell through to categoryListingRank). Once v10 sorts by a real is_special value, category browse will genuinely put specials at the top — which is the intended semantics of the specials_first default, but it is a visible UX change worth calling out when the BFF switches indexes.

## Summary The fresh products index `shoppy-test-v10` (created 2026-09-20, after #3 landed the `attribute` ranking-rule change) does **not** declare `is_special` as a sortable attribute. The Shoppy BFF's default CATEGORY sort is `specials_first` = `["is_special:desc", "categoryListingRank:asc"]`, so **every real-category browse through the BFF fails with 400** against v10. This blocks pointing the BFF at v10 (the index that carries the search-relevance fix from #3 / bff#13). ## Evidence (2026-09-20, via Kong gateway) Same request body against both indexes: ```json {"q":"","filter":["storeId IN ['woolworths']","categories.level1=fruit"],"sort":["is_special:desc","categoryListingRank:asc"], ...} ``` - `shoppy-test-v9` → **200** (works) - `shoppy-test-v10` → **400**: ```json {"message":"Index `shoppy-test-v10`: Attribute `is_special` is not sortable. Available sortable attributes are: `categoryListingRank, price.amountAsCents, price.comparativePricing.priceAsCents`.", "code":"invalid_search_sort","type":"invalid_request"} ``` Document-level check: v10 documents **do** carry the field (`is_special: 0` observed on a fetched hit), while v9 documents don't carry it at all (yet v9's settings declared it sortable, so the sort was a no-op primary key there). So between the v9 and v10 runs both the data (field added) and the effective settings (sortable declaration dropped) changed. ## BFF impact - `productList(listType: CATEGORY)` for any real category → 400 (`Error getting products by category`). Default sort is `specials_first` (`to_ms_sort_option`, `src/graphql/queries/helpers.rs` in shoppy/bff); the explicit-sort passthrough can also request it. - Virtual categories (specials / half-price-specials) are **not** affected — they filter on `price.tags` and sort by price only. - SEARCH is not affected (sorts use `categoryListingRank` / `price.amountAsCents`, both still sortable). ## Fix 1. Add `is_special` back to the products index settings' `sortableAttributes` in this repo's template(s) — wherever v10's settings came from (`shoppy-products.json` and/or the live template `shoppy-indexes/shoppy-test.json` consumed by `product-sync` via `MEILISEARCH_INDEX_CONFIG_PRODUCTS`). Check why it was dropped between the v9 and v10 runs so it doesn't recur. 2. Apply to the live `shoppy-test-v10` without waiting for a fresh run — the Kong gateway allows the `sortable-attributes` settings sub-route: ``` PUT /indexes/shoppy-test-v10/settings/sortable-attributes {"sortableAttributes": ["categoryListingRank", "price.amountAsCents", "price.comparativePricing.priceAsCents", "is_special"]} ``` (Preserve the existing three entries; confirm final state via `GET .../settings/sortable-attributes`.) ## Validation - `POST /indexes/shoppy-test-v10/search` with `sort: ["is_special:desc","categoryListingRank:asc"]` and a category filter → 200. - BFF against v10: `productList(listType: CATEGORY, argument: "fruit")` with `shoppy-stores: woolworths` returns products (specials first). - Re-run the bff#13 regression checklist items that touch category browse. ## Note on behaviour change On v9 the `is_special` sort was inert (no document carried the field — all ties fell through to `categoryListingRank`). Once v10 sorts by a real `is_special` value, category browse will genuinely put specials at the top — which is the intended semantics of the `specials_first` default, but it is a visible UX change worth calling out when the BFF switches indexes.
Author
Owner

Closing as resolved — the blocker is gone, and the "apply to v10" step is obsolete rather than outstanding.

Repo side (already done)

shoppy-products.json lists is_special under sortableAttributes (line 22) and the README settings table matches (README.md:35). Landed in 7680977issue-6: add is_special to products sortableAttributes (#7) — which closed #6. Nothing further is required in this repo.

Live instance (verified 2026-09-22 ~11:20Z)

index sortableAttributes sort: ["is_special:desc", ...]
shoppy-test-v9 includes is_special 200
shoppy-test-v10 still missing 400 invalid_search_sort (unchanged, as reported)
shoppy-test-v11 includes is_special 200

The settings fix landed on shoppy-test-v11 as Meilisearch task 1544 (settingsUpdate, succeeded 2026-09-22T11:19:46Z). Confirmed live:

GET /indexes/shoppy-test-v11/settings/sortable-attributes
["categoryListingRank","is_special","price.amountAsCents","price.comparativePricing.priceAsCents"]

Documents carry the field too — a 1000-hit sample of the 48,763 docs had 0 missing is_special (976 × 0, 24 × 1), so the sort is meaningful rather than a no-op tie-break.

Validation

  • POST /indexes/shoppy-test-v11/search with sort: ["is_special:desc","categoryListingRank:asc"] and a category filter → 200.
  • BFF (search__products_index=shoppy-test-v11) productList(listType: CATEGORY, argument: "fruit", pageNumber: 1) with shoppy-stores: woolworths200, totalItems: 50, items returned. The default specials_first sort no longer 400s.
  • The "blocks pointing the BFF at v10" framing is stale: the BFF is now pointed at v11, the index that carries the #3 relevance fix and a working is_special sort.

Not fixed by this closure

  • shoppy-test-v10 still lacks is_special and will keep returning 400 on specials_first. It is superseded and unreferenced — delete it or leave it alone, but do not point anything at it.
  • Recurrence cause is not conclusively identified. The full settings payload applied at v10 creation (task 540) and at v11 creation (task 1543, 2026-09-22T05:29Z) is exactly the canonical shoppy-products.json as of the pre-#7 state (df465b3: attribute rule present, only three sortables). It matches neither ~/Projects/shoppy-indexes/shoppy-test.json nor the retired ~/Projects/shoppy/indexes/shoppy-test.json (both also lack the attribute rule), so a stale settings source (old copy/checkout or binary) produced those runs. v11 was corrected out-of-band rather than by re-creating the index — the next fresh index run could regress the same way unless that source is tracked down.

Follow-ups for the open half are tracked in product-sync: #12 (re-sync so is_special exists on every document), #13 (validate present + sortable), #16 (one-off settings PATCH).

Closing as resolved — the blocker is gone, and the "apply to v10" step is obsolete rather than outstanding. ## Repo side (already done) `shoppy-products.json` lists `is_special` under `sortableAttributes` (line 22) and the README settings table matches (`README.md:35`). Landed in `7680977` — *issue-6: add is_special to products sortableAttributes (#7)* — which closed #6. Nothing further is required in this repo. ## Live instance (verified 2026-09-22 ~11:20Z) | index | `sortableAttributes` | `sort: ["is_special:desc", ...]` | |---|---|---| | `shoppy-test-v9` | includes `is_special` | 200 | | `shoppy-test-v10` | still missing | 400 `invalid_search_sort` (unchanged, as reported) | | `shoppy-test-v11` | **includes `is_special`** | 200 | The settings fix landed on **`shoppy-test-v11`** as Meilisearch task **1544** (`settingsUpdate`, succeeded `2026-09-22T11:19:46Z`). Confirmed live: ``` GET /indexes/shoppy-test-v11/settings/sortable-attributes ["categoryListingRank","is_special","price.amountAsCents","price.comparativePricing.priceAsCents"] ``` Documents carry the field too — a 1000-hit sample of the 48,763 docs had **0 missing** `is_special` (976 × `0`, 24 × `1`), so the sort is meaningful rather than a no-op tie-break. ## Validation - `POST /indexes/shoppy-test-v11/search` with `sort: ["is_special:desc","categoryListingRank:asc"]` and a category filter → 200. - BFF (`search__products_index=shoppy-test-v11`) `productList(listType: CATEGORY, argument: "fruit", pageNumber: 1)` with `shoppy-stores: woolworths` → **200, `totalItems: 50`**, items returned. The default `specials_first` sort no longer 400s. - The "blocks pointing the BFF at v10" framing is stale: the BFF is now pointed at **v11**, the index that carries the #3 relevance fix and a working `is_special` sort. ## Not fixed by this closure - `shoppy-test-v10` still lacks `is_special` and will keep returning 400 on `specials_first`. It is superseded and unreferenced — delete it or leave it alone, but do not point anything at it. - **Recurrence cause is not conclusively identified.** The full settings payload applied at v10 creation (task 540) and at v11 creation (task 1543, `2026-09-22T05:29Z`) is exactly the canonical `shoppy-products.json` **as of the pre-#7 state** (`df465b3`: `attribute` rule present, only three sortables). It matches neither `~/Projects/shoppy-indexes/shoppy-test.json` nor the retired `~/Projects/shoppy/indexes/shoppy-test.json` (both also lack the `attribute` rule), so a stale settings source (old copy/checkout or binary) produced those runs. v11 was corrected out-of-band rather than by re-creating the index — **the next fresh index run could regress the same way** unless that source is tracked down. Follow-ups for the open half are tracked in product-sync: #12 (re-sync so `is_special` exists on every document), #13 (validate present + sortable), #16 (one-off settings PATCH).
david closed this issue 2026-09-22 11:35:38 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
shoppy/shoppy-index-configuration#9
No description provided.