Publicado em 31 de outubro de 2026 · 2 min de leitura
Uma busca de catálogo que acha “Miu Miu”: FULLTEXT no MariaDB
Como a busca do Resolvi Desapegar usa um índice InnoDB próprio, normalização simétrica e degraus de fallback, e por que filtrar na view custava 1,5 s.
O catálogo do Resolvi Desapegar tem peças de luxo, e boa parte das buscas é por marca: “bolsa chanel”, “brinco vivara”, “bolsa miu miu”. Esse último caso é o difícil. Sem cuidado, “miu” é curto demais para o índice de texto, a busca vira apenas “bolsa” e devolve o catálogo inteiro.
O problema do tamanho mínimo da palavra
A maioria das tabelas do banco é MyISAM, e o FULLTEXT do MyISAM ignora palavras menores que ft_min_word_len (4, no servidor em questão). O InnoDB usa outro parâmetro, innodb_ft_min_token_size, configurado com 3, e portanto indexa “miu”. A solução foi não buscar na tabela de produtos: a busca tem uma tabela própria, em InnoDB, só para isso.
Uma tabela derivada, nunca a fonte da verdade
A tabela produtos_busca guarda, por produto, um texto normalizado com título, marca, categorias e referência, e a descrição à parte. Ela é derivada: se for perdida, é reconstruída a partir dos produtos. O índice é atualizado quando um produto é salvo no painel, por um processo incremental a cada 5 minutos e por uma reconstrução completa de madrugada.
Normalizar dos dois lados do mesmo jeito
O texto indexado e o termo digitado passam pela mesma função: minúsculas, sem acento, plural reduzido ao radical, palavras vazias removidas. Se uma das pontas muda sem a outra, a busca silenciosamente deixa de achar coisas, então qualquer alteração na normalização exige reindexar tudo.
-- Exemplo simplificado da ideia (não é o SQL do projeto)
SELECT produto_id
FROM produtos_busca
WHERE MATCH(doc) AGAINST ('+bolsa +miu' IN BOOLEAN MODE)
LIMIT 40;Degraus: do mais preciso ao mais permissivo
A busca começa exigindo todos os termos no título. Se não houver resultado, exige todos os termos incluindo a descrição, depois aceita qualquer termo e, por último, cai num LIKE. Cada degrau só roda se o anterior voltou vazio, o que mantém a relevância alta sem devolver o catálogo inteiro quando a pessoa digita algo muito específico.
O custo escondido da view
A listagem de produtos usa uma view com GROUP BY. No MariaDB, uma view assim é materializada por completo a cada consulta, cerca de 16 mil linhas, e filtrar nela custava em torno de 1,5 s, mesmo com índice na tabela base. O padrão que resolveu foi em dois passos: filtrar a tabela base, coletar os ids e só então consultar a view com IN (lista literal de ids). A mesma consulta caiu para cerca de 25 ms. Uma subconsulta IN (SELECT ...) não serve, porque o otimizador volta a materializar tudo.
O que fica de lição
- Confirme o motor da tabela antes de assumir transações, chaves estrangeiras ou o comportamento do FULLTEXT.
- Índice de busca é um dado derivado: planeje como reconstruí-lo por inteiro.
- Normalize consulta e índice com a mesma função, e teste as duas pontas juntas.
- Meça a consulta real. Um índice na tabela base não ajuda se o filtro está na view.