Skip to content

Filtro por _id na busca não funciona na API v2: recomendados, compre-junto, favoritos e kits ficam vazios #1306

Description

@vitorrgg

Resumo

Filtrar por _id na busca não funciona na API v2 (ecomplus.io/v2/search/_els). O mesmo filtro funciona na v1 (apx-search.e-com.plus/api/v1). Como todo componente que busca produtos por ID passa por EcomSearch.setProductIds(), e ele monta {terms: {_id: [...]}}, essas vitrines voltam vazias — e somem sem erro, porque o template tem v-if="items.length".

Nas lojas Cloud Commerce isso está ativo hoje: o vbeta-app do @cloudcommerce/storefront seta window.ECOMCLIENT_API_SEARCH = 'https://ecomplus.io/v2/search/_els/', então o app já fala com a v2.

Reprodução

Mesmo produto (5f3432a4f023684cdbd9c78d, loja 1024), mesma query, endpoints diferentes.

Por sku na v2 — 1 hit:

curl -s -X POST -H "X-Store-ID: 1024" -H "Content-Type: application/json" \
  -d '{"size":3,"query":{"bool":{"filter":[{"terms":{"sku":["PA606"]}}]}}}' \
  "https://ecomplus.io/v2/search/_els/items.json"

Por _id na v2 — total: 0:

curl -s -X POST -H "X-Store-ID: 1024" -H "Content-Type: application/json" \
  -d '{"size":3,"query":{"bool":{"filter":[{"terms":{"_id":["5f3432a4f023684cdbd9c78d"]}}]}}}' \
  "https://ecomplus.io/v2/search/_els/items.json"

O mesmo terms._id na v1 — funciona:

curl -s -X POST -H "X-Store-ID: 1024" -H "Content-Type: application/json" \
  -d '{"size":3,"query":{"bool":{"filter":[{"terms":{"_id":["5f3432a4f023684cdbd9c78d","5d09191d24607a6a42d71361"]}}]}}}' \
  "https://apx-search.e-com.plus/api/v1/items.json"

Rodando a query completa do RecommendedItems (com quantity > 0 e available: true) sobre os IDs que a Graphs API devolve para um produto real: 4 produtos na v1, 0 na v2.

Detalhe útil: a query ids funciona na v2. É só term/terms sobre _id que não.
Correção (editado): a query ids NÃO funciona na v2 — ela é descartada pelo proxy e a busca devolve o catálogo inteiro (meu teste original parecia passar porque os ids pedidos eram, por coincidência, os primeiros da ordenação default). O mapeamento completo do que funciona está no comentário abaixo.

Onde quebra

Tudo que passa por setProductIds, que delega para commonFilter e monta terms._id:

  • src/js/RecommendedItems.js:141 — vitrine de recomendados no carrinho (TheCart.html) e no checkout (EcCheckout.html)
  • src/js/RecommendedItems.js:149 — mesma vitrine com productIds explícito, que é como TheAccount.html:73 renderiza os favoritos
  • src/js/BuyTogether.js:157 — compre junto
  • src/js/TheProduct.js:493 — composição de kit

Nenhum deles loga erro: o fetch resolve com zero itens e o v-if esconde a seção.

Quem é afetado

  • Lojas Cloud Commerce (v3): afetadas, porque o vbeta-app aponta a busca para a v2.
  • Lojas do template v2 legado: não afetadas por isso — continuam na v1, onde o filtro funciona. Em compensação, o índice v1 está defasado. Comparando a mesma loja: v1 devolve "Achocolatado Em Pó 180g" e "Granola Low Carb 180g", v2 devolve "Achocolatado em Pó 180g" e "Granola Low Carb Tia Sônia 180g Baixo Carboidrato Rica em Fibras". São índices diferentes.

Ou seja: apontar o app legado para a v2 corrige a defasagem do catálogo e quebra a busca por ID. Hoje cada família de loja está de um lado desse trade-off.

O que não consegui determinar

Se o comportamento da v2 é bug ou decisão de projeto. O proxy search/_els é da plataforma e não está neste repositório, então não dá para olhar o mapeamento do índice a partir daqui. Essa é a pergunta que destrava o resto.

Caminhos possíveis

  1. Corrigir o proxy _els da v2. Resolve os quatro consumidores de uma vez, sem publicar nada nem bumpar versão em loja nenhuma. Depende de quem mantém o serviço — especificação e critérios de aceite na seção abaixo.
  2. Trocar terms._id por uma query ids no setProductIds. Inviável — a query ids é ignorada pela v2 (ver correção acima e comentário abaixo). O caminho client-side viável é outro: rotear buscas por _id pelo ?q=_id:(...) puro, que funciona nas duas APIs — implementado em fix(fetch): workaround Search API v2 to fix products search by IDs search-engine#324.
  3. Não fazer nada nos componentes legados e tratar caso a caso onde a vitrine importa.

A opção 2 (revisada) foi entregue, mas a 1 continua valendo por três motivos: (a) busca por _id com aggregations — página de coleção com UI de filtros — segue quebrada na v2 mesmo com a PR, que só roteia queries sem aggs; (b) o fail-open do proxy (cláusula desconhecida → catálogo inteiro) é um risco latente para qualquer consumidor, não só estes quatro; (c) com o proxy corrigido, o workaround client-side pode ser revertido no futuro.

Especificação para o conserto do proxy (caminho 1)

Consolidando aqui o mapeamento que estava só em comentário. Todos os testes contra a loja 1024; ids reais usados: 5d09193d24607a6a42d71399 (Tapioca, com estoque) e 5d09191d24607a6a42d71361 (Granola Low Carb, quantity: 184, available: true).

Comportamentos a corrigir, por prioridade:

P1 — term/terms sobre _id no body retorna 0 hits. É o que quebra o caminho principal do setProductIds. Critério de aceite:

curl -s -X POST -H "X-Store-ID: 1024" -H "Content-Type: application/json" \
  -d '{"size":3,"query":{"bool":{"filter":[{"terms":{"_id":["5d09193d24607a6a42d71399"]}}]}}}' \
  "https://ecomplus.io/v2/search/_els/items.json"

Esperado: 1 hit (o doc existe — trocando _id por sku no mesmo filtro, ele volta). Hoje: total: 0.

P2 — cláusulas não reconhecidas são descartadas silenciosamente (fail-open). ids, match, query_string, bool.must e term com {value:} são ignorados e a busca devolve o catálogo inteiro. Para um consumidor com v-if isso é vitrine vazia; para qualquer outro é resultado errado apresentado como certo. Critério de aceite: cláusula não suportada deve ou ser honrada ou retornar 400 — nunca ser removida da query.

curl -s -X POST -H "X-Store-ID: 1024" -H "Content-Type: application/json" \
  -d '{"size":3,"query":{"ids":{"values":["5d09193d24607a6a42d71399"]}}}' \
  "https://ecomplus.io/v2/search/_els/items.json"

Esperado: 1 hit ou 400. Hoje: total: 167 (catálogo inteiro, ordem default).

P3 — composição no q= (GET) é inconsistente. q=_id:("A" "B") sozinho funciona; composto, quebra de duas formas diferentes:

# derruba a Granola, que é available: true → esperado total 2, hoje total 1
curl -s -G -H "X-Store-ID: 1024" \
  --data-urlencode 'q=_id:("5d09193d24607a6a42d71399" "5d09191d24607a6a42d71361") AND available:true' \
  "https://ecomplus.io/v2/search/_els/items.json"

# a forma que a composição de kit emite hoje via fetch(true) → esperado total 2, hoje total 167
curl -s -G -H "X-Store-ID: 1024" \
  --data-urlencode 'q=visible:true AND _id:("5d09193d24607a6a42d71399" "5d09191d24607a6a42d71361")' \
  "https://ecomplus.io/v2/search/_els/items.json"

P4 — ranges e sort no GET não funcionam. q=_id:X AND quantity:>0 e quantity:[1 TO *] retornam 0 mesmo com o doc tendo quantity: 184; &sort=price:desc é ignorado (verificável com q=available:true&sort=price:desc&size=2, que volta preços fora de ordem). Menor prioridade — o workaround client-side já cobre — mas documenta a distância entre o _els e o contrato ES que os clientes assumem.

Nota

Isso não bloqueia ecomplus/cloud-commerce#812, que passa a renderizar recomendação no carrinho e checkout das lojas v3 por fora do cliente legado, buscando com api.get('search/v1?_id='), que funciona. Aquele PR não conserta os quatro componentes acima — só substitui a vitrine do carrinho e do checkout.

Activity

  1. vitorrgg commented on Aug 26, 2026

    @vitorrgg
    MemberAuthor

    Reverifiquei o comportamento da v2 com mais cuidado e o quadro mudou — corrigi o corpo da issue e abri PR com a solução client-side.

    Mapeamento completo do ?q=/DSL na v2 (/v2/search/_els)

    forma resultado
    body terms/term sobre _id 0 hits (aplicado, não casa)
    body ids, match, query_string, bool.must, term com {value:} descartados → devolve o catálogo inteiro
    GET q=_id:("A" "B") sozinho ✅ funciona, hits exatos
    GET q=_id:(...) AND available:true ❌ derruba doc que é available: true
    GET q=visible:true AND _id:(...) (forma que o kit emite hoje via fetch(true)) ❌ catálogo inteiro (total: 167)
    GET q=... quantity:>0 ou [1 TO *] ❌ 0 hits mesmo com quantity: 184 no doc
    GET &sort=price:desc ❌ ignorado

    Ou seja: na v2 a única forma confiável de buscar por id é q=_id:(...) puro — qualquer condição composta é traição. A afirmação anterior de que "a query ids funciona" estava errada (o teste passou por coincidência de ordenação) — e ela seria pior que o bug: renderizaria o catálogo inteiro como "recomendados".

    PR

    ecomplus/search-engine#324 — o fetch() passa a rotear busca com filtro por _id para o q= puro, reaplicando os demais filtros, a ordenação (pela ordem dos ids pedidos) e a paginação no cliente. Testado contra as duas APIs com o build real: vitrine, paginação, kit (fetch(true)) e um controle sem _id — na v2 a vitrine sai de 0 hits para os produtos certos em ordem, e o kit sai de "catálogo inteiro" para os ids exatos; na v1 comportamento preservado.

    Cobre os quatro consumidores (recomendados, compre junto, favoritos, kit) porque todos passam por setProductIds sem aggregations. Fora do escopo: busca por _id com aggregations (página de coleção com UI de filtros) — na v1 segue funcionando, na v2 segue como está.

    O caminho 1 (corrigir o _id no próprio proxy _els) continua sendo o ideal a longo prazo — a PR é o contorno que não depende de mudança na plataforma. Depois do release ainda falta o bump em storefront-components/storefront-app para as lojas herdarem.

  2. leomp12 commented on Sep 25, 2026

    @leomp12
    Member

    Corrigido no proxy (P1 e P3) em ecomplus-core/store-api#1314, publicado no v2.0.0-rc.154, já em produção.

    Causa

    • P1: term/terms sobre _id no body viravam $in com strings, mas o _id é armazenado como ObjectId, então nunca casavam.
    • P3: o q= era lido com um único split(':') sobre a string inteira. Com visible:true AND _id:(...), o primeiro campo (visible) não era reconhecido e nada era filtrado. Com _id:(...) AND available:true, os ids eram cortados errado e o segundo virava inválido.

    Validação em produção (ecomplus.io/v2/search/_els, loja 1024)

    cenário antes agora
    P1: terms._id [5d09193d…] 0 1 hit
    P1: reprodução original terms._id [5f3432a4…] 0 1 hit (o mesmo doc que a busca por sku PA606)
    P3: q=_id:(A B) AND available:true 1 2 hits
    P3: q=visible:true AND _id:(A B) (kit via fetch(true)) 167, o catálogo 2 hits
    Controles: q=sku:"PA606" OR name:"PA606" e busca sem filtro — sem mudança, com aggregations

    Os quatro consumidores (recomendados, compre junto, favoritos e kit) voltam a funcionar nas lojas Cloud Commerce sem publicar nada no search-engine ou no storefront. ecomplus/search-engine#324 fica fechado.

    Continuam em aberto

    • P2: cláusulas não suportadas (ids, match, query_string…) ainda são descartadas e a busca devolve o catálogo inteiro.
    • P4: range e sort dentro do q= no GET ainda não são suportados.

    🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions