AACsearch
API de Búsqueda

Búsqueda Múltiple y Consultas

Ejecute múltiples consultas de búsqueda en una sola solicitud para autocompletado, búsqueda federada y escenarios de múltiples índices.

La búsqueda múltiple ejecuta múltiples consultas de búsqueda en un solo viaje de ida y vuelta HTTP. Esto es esencial para el autocompletado (ejecutar una consulta de resultados + una consulta de sugerencias simultáneamente) y la búsqueda federada (buscar en múltiples índices a la vez).

Endpoint de búsqueda múltiple

Endpoint: POST /api/search/multi

Autenticación: Authorization: Bearer ss_search_your_key

{
	searches: Array<{
		indexSlug: string;
		q: string;
		queryBy?: string;
		filterBy?: string;
		facetBy?: string;
		sortBy?: string;
		page?: number;
		perPage?: number;
		highlightFields?: string;
	}>;
}

Respuesta:

{
	results: Array<SearchResponse>; // un resultado por búsqueda en el mismo orden
}

Patrón de autocompletado

El caso de uso más común de búsqueda múltiple: ejecutar una búsqueda completa más una consulta de sugerencias rápidas simultáneamente.

const [mainResults, suggestions] = await client.multiSearch([
	{
		indexSlug: "products",
		q: "auriculares inalám",
		queryBy: "title,brand,description",
		perPage: 20,
		facetBy: "brand,categories",
	},
	{
		indexSlug: "products",
		q: "auriculares inalám",
		queryBy: "title",
		perPage: 5,
		includeFields: "id,title",
		highlightFields: "title",
	},
]);

Esto envía una sola solicitud HTTP y obtiene ambos conjuntos de resultados simultáneamente.

Búsqueda federada en índices

Busque en múltiples índices y combine los resultados en el cliente:

const [productResults, articleResults] = await client.multiSearch([
	{
		indexSlug: "products",
		q: "sostenibilidad",
		queryBy: "title,description,tags",
		perPage: 10,
	},
	{
		indexSlug: "knowledge",
		q: "sostenibilidad",
		queryBy: "content,title",
		perPage: 5,
	},
]);

Nota: Para resultados entre índices, combine y reclasifique en el cliente.

Parámetros de consulta en profundidad

q — consulta de búsqueda

La cadena de búsqueda. Use "*" para una exploración comodín (sin coincidencia de texto completo, útil para exploración filtrada).

{ "q": "auriculares inalámbricos" }    // búsqueda de texto completo
{ "q": "*" }                           // explorar todo (combinar con filterBy)

queryBy — qué campos buscar

Lista separada por comas de campos. El orden importa — los campos anteriores obtienen mayor peso por defecto.

{ "queryBy": "title,sku,brand,description" }

Para cada campo puede especificar un peso por campo:

{ "queryBy": "title,brand,description", "queryByWeights": "10,5,2" }

filterBy — expresión de filtro

Expresión booleana usando la sintaxis de filtro de AACSearch:

Igualdad:        field:=value
                 field:=[value1, value2]
Rango:           field:>100
                 field:[50..200]
Cadena exacta:   field:=value
Negación:        field:!=value
AND lógico:      cond1 && cond2
OR lógico:       cond1 || cond2
Agrupación:      (cond1 || cond2) && cond3

Ejemplos:

"filterBy": "availability:=in_stock"
"filterBy": "price:[50..200] && brand:=[Sony, Bose]"
"filterBy": "categories:=Electrónica && availability:!=out_of_stock"

facetBy — calcular recuentos de facetas

Devuelve distribuciones de valores para los campos especificados. Los campos de faceta deben estar indexados como facet: true en el esquema de la colección.

{ "facetBy": "brand,categories,availability" }

La respuesta incluirá facetCounts con pares valor → recuento para cada campo de faceta.

sortBy — orden de clasificación

Ordenación por un solo campo o múltiples campos:

{ "sortBy": "price:asc" }
{ "sortBy": "_text_match:desc,price:asc" }
{ "sortBy": "created_at:desc" }

Campos de ordenación disponibles: price, sale_price, created_at, _text_match (puntuación de relevancia).

Tolerancia a errores tipográficos

La tolerancia a errores tipográficos de AACSearch se aplica automáticamente. Por defecto:

  • Se permite 1 error tipográfico para consultas de 5+ caracteres
  • Se permiten 2 errores tipográficos para consultas de 8+ caracteres

Esto maneja errores de escritura comunes como "auriculares" → coincide con "auriculares".

Resaltado

Los términos coincidentes se resaltan con etiquetas configurables:

{
	"highlightFields": "title,description",
	"highlightStartTag": "<em class='highlight'>",
	"highlightEndTag": "</em>"
}

Resaltados en la respuesta:

{
	"highlights": [
		{
			"field": "title",
			"snippet": "Sony <em class='highlight'>Auriculares</em> <em class='highlight'>Inalámbricos</em>"
		}
	]
}

Paginación

Los resultados se paginan usando page (indexado desde 1) y perPage:

{ "page": 2, "perPage": 20 }

La respuesta incluye found (recuento total coincidente) para calcular los totales de páginas:

const totalPages = Math.ceil(results.found / perPage);

Máximo perPage: 100. Para exportaciones que requieren más resultados, implemente iteración basada en cursor usando valores crecientes de page.

Recomendaciones de rendimiento

  • Mantenga perPage ≤ 20 para búsqueda mientras se escribe (autocompletado)
  • Use includeFields para devolver solo los campos que renderiza — reduce el tamaño de la respuesta
  • Evite ejecutar facetas en cada pulsación de tecla — elimine rebotes a 150–300 ms
  • Para conjuntos de resultados grandes con muchas facetas, considere separar la consulta de facetas de la consulta principal usando búsqueda múltiple

Limits

LimitValue
Max searches per multi-search request20 — enforced in public-handler.ts.
Max perPage100
Max page1000
Max numTypos3
Max rangeFacets per search20

Exceeding the multi-search batch limit returns 400 invalid_input with the Zod path searches. Use two consecutive requests rather than batching past 20.

Partial failures

A multi-search request is atomic at the HTTP level — sub-searches can still fail individually inside a 200. Each result envelope carries its own status: success carries hits, found, page; failure carries error and code. Iterate the results array in order; treat any entry with error as a sub-search failure. A single sub-search failure does not roll back the others.

Common error reasons: collection_not_found, invalid_filter, not_authorized (scoped token mismatch), rate_limit_exceeded, internal_error. Engine errors are normalised before reaching the client (Invariant 6 — never echoed raw).

Shared authentication and quota

Authentication is per request, not per sub-search. The same Authorization: Bearer ss_search_* (or ss_scoped_*) header validates the entire batch. A scoped token's scopedFilter is AND-combined into every sub-search (Invariant 4).

Quota / rate limit treats the batch as a single HTTP unit but charges per sub-search. A 20-batch is 20× the unit cost of a single search.

Tenant isolation (Invariant 5) holds at the sub-search level — every sub-search has tenantId AND-combined automatically.

Analytics grouping

Each multi-search response carries a parent queryId (the whole batch) and a per-sub-result queryId. Click events posted via POST /events/track should carry the child queryId so per-search CTR computes correctly. Conversion events follow the same rule. See Analytics overview.

A common bug is to record clicks against the first sub-search's queryId regardless of which list the click came from — that inflates the suggestions-list CTR and deflates the main-results CTR. Always pass through the specific child queryId.

Caching and debouncing

Recommended values (matching the bundled widget): keystroke debounce 200 ms (range 150–300 ms), min characters 2, client-side LRU 30 entries scoped to indexSlug, popular-queries TTL 10 min.

Do not extend the server-side response cache without a rate-limit-aware invalidation path — stale suggestions can keep linking to products that have been removed by a curation change.

Federated suggestions example

Combining a primary product search with a categories suggestion and an articles suggestion in one round-trip:

const [products, categories, articles] = await client.multiSearch([
  { indexSlug: "products",  q: "wireless head", queryBy: "title,brand,description", perPage: 20, facetBy: "brand,categories" },
  { indexSlug: "categories", q: "wireless head", queryBy: "name,aliases",            perPage: 3,  includeFields: "id,name,slug" },
  { indexSlug: "articles",   q: "wireless head", queryBy: "title,excerpt",           perPage: 3,  includeFields: "id,title,url,published_at" },
]);

Three sub-searches still count as three units against the rate-limit bucket — budget accordingly.

On this page