Búsqueda semántica en WooCommerce con XEYE
Para añadir búsqueda semántica a WooCommerce basta un plugin pequeño: guarda tu clave API en wp-config.php, expone una ruta en la API REST de WordPress que llama a XEYE y pinta un buscador con un shortcode. Antes hay que exportar los productos a JSON, importarlos en XEYE y entrenarlos.
Por Joan Martorell
¿Qué necesito antes de empezar?
- Una cuenta de XEYE y una clave API, creada en la página Claves API de la consola.
- Acceso a los archivos de tu WordPress para crear un plugin y editar
wp-config.php. - WP-CLI para exportar los productos. Si no lo tienes, puedes usar la API REST de WooCommerce.
El código de esta página sigue la documentación oficial de WordPress y de WooCommerce y pasa la comprobación de sintaxis de PHP, pero no lo hemos ejecutado en una tienda real. Pruébalo primero en un entorno de pruebas.
Paso 1: ¿cómo exporto los productos?
XEYE importa un JSON con una lista de objetos: un text obligatorio, y opcionalmente una description y unos params. Guarda este archivo como xeye-export.php y ejecútalo con WP-CLI:
<?php
// wp eval-file xeye-export.php > products.json
$out = array();
foreach ( wc_get_products( array( 'status' => 'publish', 'limit' => -1 ) ) as $product ) {
$out[] = array(
'text' => $product->get_name(),
'description' => wp_strip_all_tags( $product->get_short_description() ?: $product->get_description() ),
'params' => array(
'id' => $product->get_id(),
'sku' => $product->get_sku(),
'price' => $product->get_price(),
'url' => $product->get_permalink(),
'image' => wp_get_attachment_url( $product->get_image_id() ),
),
);
}
echo wp_json_encode( $out, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
wp eval-file xeye-export.php > products.jsonEl id que se guarda en params es el identificador del producto en WooCommerce. Lo usa el paso opcional del final para ordenar los resultados nativos.
Paso 2: ¿cómo los cargo en XEYE?
- En la consola, crea una lista llamada
productsy márcala como pública. - Ábrela y usa «Importar desde archivo» con el
products.jsondel paso anterior. - En la pestaña Entrenamientos, elige un modelo y lanza el entrenamiento.
- Cuando termine, prueba unas búsquedas en el playground. Es gratis.
Paso 3: ¿cómo llamo a XEYE desde WordPress?
Primero, guarda la clave donde no se publique. Añade esta línea a wp-config.php:
define( 'XEYE_API_KEY', 'tu-clave-api' );Después crea el plugin. Guarda este archivo como wp-content/plugins/xeye-search/xeye-search.php y actívalo en el panel:
<?php
/**
* Plugin Name: XEYE Search
* Description: Semantic product search through the XEYE API.
*/
defined( 'ABSPATH' ) || exit;
// In wp-config.php: define( 'XEYE_API_KEY', 'your-api-key' );
add_action( 'rest_api_init', function () {
register_rest_route( 'xeye/v1', '/search', array(
'methods' => 'POST',
'permission_callback' => '__return_true',
'callback' => function ( WP_REST_Request $request ) {
$results = xeye_search( $request['q'], $request['limit'] );
return is_wp_error( $results )
? $results
: rest_ensure_response( array( 'results' => $results ) );
},
'args' => array(
'q' => array(
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => function ( $value ) {
return is_string( $value ) && strlen( trim( $value ) ) >= 2 && strlen( $value ) <= 200;
},
),
'limit' => array( 'default' => 5, 'sanitize_callback' => 'absint' ),
),
) );
} );
function xeye_search( $term, $limit = 5 ) {
if ( ! defined( 'XEYE_API_KEY' ) ) {
return new WP_Error( 'xeye_not_configured', 'Search is not configured.', array( 'status' => 500 ) );
}
// Cache each query for five minutes: repeated searches spend no credit.
$limit = max( 1, min( 20, (int) $limit ) );
$cache_key = 'xeye_' . md5( strtolower( $term ) . '|' . $limit );
$cached = get_transient( $cache_key );
if ( false !== $cached ) {
return $cached;
}
$response = wp_remote_post( 'https://search.xeye.es/api/v1/search', array(
'timeout' => 5,
'headers' => array( 'Content-Type' => 'application/json', 'X-API-Key' => XEYE_API_KEY ),
'body' => wp_json_encode( array(
'list_name' => 'products',
'search_term' => $term,
'limit' => $limit,
) ),
) );
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
return new WP_Error( 'xeye_unavailable', 'Search is unavailable.', array( 'status' => 502 ) );
}
$data = json_decode( wp_remote_retrieve_body( $response ), true );
$results = isset( $data['results'] ) && is_array( $data['results'] ) ? $data['results'] : array();
set_transient( $cache_key, $results, 5 * MINUTE_IN_SECONDS );
return $results;
}
El plugin registra la ruta /wp-json/xeye/v1/search. Valida la consulta, llama a XEYE con la clave y guarda cada respuesta durante cinco minutos, de modo que las búsquedas repetidas no consumen crédito.
Paso 4: ¿cómo pinto el buscador?
Con un shortcode. Añade este bloque al mismo plugin y escribe [xeye_search] en la página donde quieras el buscador:
// Add to the plugin. Use it in any page with the shortcode [xeye_search].
add_shortcode( 'xeye_search', function () {
ob_start();
?>
<input type="search" id="xeye-q" placeholder="Search products" autocomplete="off">
<ul id="xeye-results"></ul>
<script>
(() => {
const ENDPOINT = <?php echo wp_json_encode( rest_url( 'xeye/v1/search' ) ); ?>;
const input = document.getElementById('xeye-q');
const list = document.getElementById('xeye-results');
let timer;
input.addEventListener('input', () => {
clearTimeout(timer);
const q = input.value.trim();
if (q.length < 2) { list.replaceChildren(); return; }
// Wait until the user stops typing: one search, not one per keystroke.
timer = setTimeout(async () => {
const response = await fetch(ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ q, limit: 5 }),
});
const { results = [] } = await response.json();
list.replaceChildren(...results.map((result) => {
const item = document.createElement('li');
const link = document.createElement('a');
link.href = result.params?.url || '#';
link.textContent = result.item;
item.append(link);
return item;
}));
}, 300);
});
})();
</script>
<?php
return ob_get_clean();
} );
Espera 300 milisegundos desde la última tecla antes de buscar, y enlaza cada resultado con la URL guardada en params. Los estilos corren de tu cuenta.
Opcional: ¿puedo usar XEYE en la búsqueda nativa de productos?
Sí. Este bloque intercepta la búsqueda de productos de WooCommerce, pide los resultados a XEYE y hace que WordPress muestre esos productos en el mismo orden. Si XEYE no responde, se usa la búsqueda nativa:
// Optional: make the native product search (?s=…&post_type=product) use XEYE.
add_action( 'pre_get_posts', function ( $query ) {
if ( is_admin() || ! $query->is_main_query() || ! $query->is_search()
|| 'product' !== $query->get( 'post_type' ) ) {
return;
}
$results = xeye_search( $query->get( 's' ), 20 );
if ( is_wp_error( $results ) ) {
return; // fall back to the native search
}
$ids = array_values( array_filter( array_map( function ( $result ) {
return absint( $result['params']['id'] ?? 0 );
}, $results ) ) );
// An empty post__in would return every product.
$query->set( 'post__in', $ids ? $ids : array( 0 ) );
$query->set( 'orderby', 'post__in' ); // keep XEYE's order
$query->set( 'xeye', true );
}, 20 ); // after WooCommerce's own pre_get_posts
// Drop WordPress's LIKE '%term%' clause, which would discard semantic matches.
add_filter( 'posts_search', function ( $search, $query ) {
return $query->get( 'xeye' ) ? '' : $search;
}, 10, 2 );
- Los resultados se limitan a los 20 productos que devuelve XEYE.
- Si el cliente elige otro orden, como precio, WooCommerce reordena los resultados.
- En temas de bloques, el bloque de colección de productos puede no pasar por la consulta principal. Compruébalo en el tuyo.
¿Cómo mantengo el catálogo al día?
No hay sincronización automática. Cuando añadas productos, expórtalos, importa solo los nuevos y lanza un entrenamiento. Importar añade elementos y no reemplaza los que ya existen, así que si vuelves a importar el catálogo entero tendrás duplicados. Los cambios en productos existentes se editan en la consola.
Preguntas frecuentes
¿Por qué no llamo a XEYE directamente desde JavaScript?
- Porque la clave API quedaría a la vista en el navegador. La ruta REST del plugin hace de intermediario: el navegador habla con tu WordPress y solo tu servidor conoce la clave.
¿La ruta REST queda abierta a cualquiera?
- Sí, como cualquier buscador público. La caché de cinco minutos reduce el consumo, y conviene añadir limitación de peticiones en tu servidor o en tu CDN y una alerta de saldo en XEYE.
¿Funciona con productos variables?
- El script exporta un elemento por producto, con el precio que devuelve WooCommerce para el producto principal. Si quieres buscar por variación, exporta un elemento por variación.
¿Tengo que desactivar la búsqueda de WooCommerce?
- No. El shortcode añade un buscador independiente. El bloque opcional es el único que cambia la búsqueda nativa, y vuelve a ella si XEYE no responde.
Pruébalo con tus propios datos
Crea una cuenta, sube una lista y haz tu primera búsqueda en unos cinco minutos. Empiezas con 5 € de crédito, sin tarjeta.
Sigue leyendo
- Buscador semántico para tu tienda online: cómo montarloCómo añadir a una tienda online un buscador que entiende sinónimos, intención y erratas: qué datos necesita, los pasos para montarlo y lo que no hace.
- Búsqueda semántica en Shopify con XEYEAñade a tu tienda Shopify un buscador que entiende sinónimos y erratas: exporta los productos, entrénalos en XEYE y conecta el tema con un pequeño proxy.
- Precios de XEYEXEYE cuesta 0,001 € por búsqueda por API y 0,30 € por entrenamiento, más 0,0053 € por descripción con IA. Sin cuotas y con 5 € de crédito inicial.