Um recurso de pesquisa de mercado geralmente começa como uma caixa de texto conectada a Array.prototype.filter(). Essa abordagem funciona para um protótipo pequeno, mas os usuários expõem rapidamente suas limitações.
Eles podem pesquisar “impressão de parede azul” quando uma listagem for intitulada “Noite Costeira”. Eles podem inserir palavras em uma ordem diferente, usar letras maiúsculas ou pesquisar um assunto que apareça apenas nas tags do produto. Uma comparação estrita de títulos perderá resultados claramente relevantes.
Antes de adicionar um serviço de pesquisa dedicado, os desenvolvedores podem criar uma primeira versão mais útil com metadados estruturados, texto normalizado, campos ponderados, filtros e classificação previsível. Este tutorial explica como criar essa camada de pesquisa com JavaScript vanilla.
Comece com dados de listagem estruturada
A qualidade da pesquisa depende muito das informações disponíveis para o aplicativo. Se todos os detalhes forem armazenados em uma descrição longa, o sistema terá pouco controle sobre a correspondência e a classificação.
Use uma estrutura consistente para cada listagem de mercado:
const listings = (
{
id: "art-101",
title: "Coastal Evening",
description: "An abstract sunset above a quiet shoreline.",
category: "abstract",
colors: ("blue", "orange"),
tags: ("coastal", "sunset", "wall art"),
price: 42,
createdAt: "2026-08-10",
available: true
},
{
id: "art-102",
title: "Quiet Forest",
description: "A minimal green woodland illustration.",
category: "nature",
colors: ("green", "cream"),
tags: ("forest", "minimal", "botanical"),
price: 35,
createdAt: "2026-09-02",
available: true
},
{
id: "art-103",
title: "City Geometry",
description: "Geometric architecture in black and gold.",
category: "architecture",
colors: ("black", "gold"),
tags: ("city", "geometric", "modern"),
price: 55,
createdAt: "2026-07-21",
available: false
}
);
Campos dedicados facilitam a validação, filtragem e indexação dos dados. Eles também permitem que a interface ofereça controles de categoria, cor, disponibilidade e preço sem tentar extrair esses valores das descrições.
Um banco de dados de produção deve impor um esquema para que as listagens não alternem entre valores como wall-art, wall arte Wall Art.
Normalize listagens e consultas
Os usuários não inserem pesquisas perfeitamente formatadas. Eles adicionam espaços extras, misturam letras maiúsculas e minúsculas e às vezes usam caracteres acentuados.
Crie uma função de normalização antes de comparar valores:
function normalizeText(value) {
return String(value)
.normalize("NFKD")
.replace(/\p{Diacritic}/gu, "")
.toLowerCase()
.replace(/(^\p{Letter}\p{Number}\s)/gu, " ")
.replace(/\s+/g, " ")
.trim();
}
A função converte valores em strings, remove diferenças diacríticas comuns, altera o texto para minúsculas, substitui pontuação por espaços e recolhe espaços em branco repetidos.
Crie uma string pesquisável para cada listagem:
function createSearchDocument(listing) {
return {
...listing,
searchText: normalizeText((
listing.title,
listing.description,
listing.category,
...listing.colors,
...listing.tags
).join(" "))
};
}
const searchableListings = listings.map(
createSearchDocument
);
Pré-computação searchText evita normalizar repetidamente cada listagem após cada pressionamento de tecla. É uma pequena otimização, mas também torna a função de pesquisa mais fácil de entender.
Combine todos os termos de consulta
Um único includes() check funciona quando toda a consulta aparece na mesma ordem dentro da listagem. Seu desempenho é ruim quando um usuário pesquisa vários conceitos.
Divida a consulta em termos e exija que cada termo apareça:
function matchesQuery(listing, query) {
const terms = normalizeText(query)
.split(" ")
.filter(Boolean);
if (terms.length === 0) {
return true;
}
return terms.every(term =>
listing.searchText.includes(term)
);
}
Uma consulta como “impressão costeira azul” agora verifica cada palavra de forma independente. As palavras não precisam aparecer lado a lado ou no mesmo campo.
Introdução do SitePoint ao filtragem em JavaScript funcional fornece informações adicionais sobre a construção de conjuntos de dados menores com métodos de array.
Esta abordagem ainda é deliberadamente simples. Ele não entende automaticamente sinônimos, erros ortográficos ou semelhanças semânticas. No entanto, fornece um comportamento previsível que os desenvolvedores podem inspecionar e testar.
Dê mais peso aos campos importantes
Nem todas as correspondências devem ter o mesmo valor. Uma palavra que aparece no título geralmente fornece um sinal mais forte do que a mesma palavra que aparece perto do final de uma descrição.
Crie uma função de pontuação:
function calculateScore(listing, query) {
const terms = normalizeText(query)
.split(" ")
.filter(Boolean);
const title = normalizeText(listing.title);
const category = normalizeText(listing.category);
const tags = normalizeText(listing.tags.join(" "));
const description = normalizeText(
listing.description
);
return terms.reduce((score, term) => {
if (title.includes(term)) score += 5;
if (category.includes(term)) score += 3;
if (tags.includes(term)) score += 2;
if (description.includes(term)) score += 1;
return score;
}, 0);
}
Os números não são regras universais. São decisões de produto que devem ser ajustadas por meio de pesquisas reais e comportamento do usuário.
Não esconda a lógica de classificação dentro de um grande bloco condicional. Pesos nomeados ou um objeto de configuração facilitam os experimentos e permitem que os desenvolvedores documentem por que um campo influencia mais os resultados do que outro.
A descoberta é especialmente importante para criadores independentes que não podem confiar inteiramente em promoções pagas. Esta visão geral de como artistas são encontrados sem pagar por anúncios ilustra por que títulos, informações descritivas de listagem e descobertas orgânicas são importantes em mercados lotados.
A lição técnica é direta: a plataforma só pode classificar os sinais que coleta de forma consistente.
Combine pesquisa com filtros explícitos
Uma consulta de texto responde a uma parte da intenção do usuário. Os filtros tratam de requisitos que não devem depender da correspondência de palavras-chave.
Por exemplo, um cliente pode querer uma arte abstrata abaixo de US$ 50 que esteja disponível atualmente:
function searchListings(
listings,
{
query = "",
category = "",
maxPrice = Infinity,
availableOnly = false
} = {}
) {
return listings
.filter(listing =>
matchesQuery(listing, query)
)
.filter(listing =>
!category ||
listing.category === category
)
.filter(listing =>
listing.price <= maxPrice
)
.filter(listing =>
!availableOnly ||
listing.available
)
.map(listing => ({
...listing,
score: calculateScore(listing, query)
}))
.sort((a, b) => {
if (b.score !== a.score) {
return b.score - a.score;
}
return new Date(b.createdAt) -
new Date(a.createdAt);
});
}
A função primeiro remove as listagens que falham na consulta ou nos filtros. Em seguida, calcula as pontuações de relevância e classifica os itens correspondentes.
Quando duas listagens têm a mesma pontuação, a mais recente aparece primeiro. Em vez disso, um mercado poderia usar popularidade, classificação, nível de estoque ou uma combinação cuidadosamente controlada de sinais.
Evite tornar as prioridades comerciais invisíveis. Se as listagens patrocinadas ou promovidas afetarem a ordem, rotule-as claramente, em vez de apresentar o pagamento como relevância orgânica.
Debounce a entrada
A execução de um pequeno filtro na memória após cada pressionamento de tecla pode ser aceitável para algumas centenas de registros. A pesquisa apoiada pelo servidor não deve enviar uma solicitação para cada evento de entrada.
Uma função de debounce reutilizável atrasa o trabalho até que o usuário faça uma pausa:
function debounce(callback, delay = 250) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
callback(...args);
}, delay);
};
}
const searchInput =
document.querySelector("#marketplace-search");
searchInput.addEventListener(
"input",
debounce(event => {
const results = searchListings(
searchableListings,
{
query: event.target.value,
availableOnly: true
}
);
renderListings(results);
})
);
A eliminação reduz o trabalho repetido, mas o atraso deve permanecer curto o suficiente para que a interface pareça responsiva.
Se a pesquisa chamar uma API, uma solicitação mais antiga ainda poderá terminar após uma mais recente. Guia do SitePoint para evitando respostas obsoletas da API com AbortController explica como cancelar solicitações desatualizadas e evitar que substituam os resultados atuais.
Projete um estado vazio útil
“Sem resultados” é tecnicamente preciso, mas raramente útil. A interface deve mostrar aos usuários como se recuperar.
Quando nenhuma listagem corresponde:
-
Exiba a consulta atual e os filtros ativos.
-
Ofereça um botão para limpar todos os filtros.
-
Sugira a remoção de um filtro restritivo.
-
Mostre categorias relacionadas quando apropriado.
-
Preservar a consulta do usuário enquanto os filtros mudam.
-
Evite substituir automaticamente a pesquisa por produtos não relacionados.
Distinga também entre um resultado vazio e uma solicitação com falha. Os usuários não deverão ver “Nenhum produto encontrado” quando a API realmente expirou.
Mensagens de status acessíveis devem anunciar a contagem de resultados sem mover o foco do teclado:
<p
id="search-status"
role="status"
aria-live="polite"
>p>
Atualize o texto com uma mensagem como “12 listagens encontradas” após renderizar os resultados.
Saiba quando o navegador não é mais suficiente
A pesquisa JavaScript na memória é apropriada para protótipos, pequenos catálogos, interfaces offline e ferramentas internas. Torna-se menos adequado à medida que o catálogo cresce ou os requisitos de classificação se tornam mais complexos.
Considere um índice do lado do servidor quando precisar:
Mesmo depois de mudar para um serviço de pesquisa, mantenha explícitos o modelo de dados e as regras de normalização. Um mecanismo dedicado não pode compensar categorias ausentes, tags inconsistentes ou títulos vagos de produtos.
Considerações finais
A pesquisa útil no mercado não começa com um mecanismo de pesquisa complicado. Tudo começa com dados de listagem estruturados e regras de correspondência claramente definidas.
Normalize o texto, pesquise em campos relevantes, pondere sinais fortes, aplique filtros separadamente, rejeite a entrada do usuário e crie estados vazios úteis. Essas etapas fornecem uma base transparente que os desenvolvedores podem medir e melhorar.
Quando o catálogo eventualmente exigir infraestrutura mais avançada, o mercado já terá os metadados limpos e as decisões de produto necessárias para fazer essa transição com sucesso.




