Contents
Introducción
Este artículo explica, con todo lujo de detalles y ejemplos en PHP, cómo indexar contenido de WordPress en motores de búsqueda externos: Algolia y Meilisearch. Cubre desde la instalación de las librerías, diseño del objeto que se enviará al índice, hooks para indexación en tiempo real, procesos por lotes para reindexaciones masivas, ajustes de índice y buenas prácticas sobre seguridad y rendimiento.
Requisitos previos
- WordPress 5.x o superior.
- Acceso al servidor para instalar dependencias mediante Composer (recomendado) o empaquetar librerías en un plugin.
- Cuenta y credenciales de Algolia (APP_ID, ADMIN API KEY) o instancia de Meilisearch (URL y MASTER KEY).
- Conocimientos básicos de PHP y desarrollo de plugins/temas en WordPress.
Elección de la librería y instalación
Recomiendo usar Composer y crear un plugin personalizado o un mu-plugin. Las librerías oficiales:
- Algolia PHP client: Algolia PHP client (GitHub)
- Meilisearch PHP client: Meilisearch PHP client (GitHub)
Instalación vía Composer (ejecutar desde la raíz del plugin):
composer require algolia/algoliasearch-client-php composer require meilisearch/meilisearch-php
Conceptos clave antes de indexar
- objectID / primaryKey: Identificador único del registro en el índice. Debe ser estable (ej. post_123).
- Mapeo de atributos: Decide qué campos serán buscables, filtrables y faceteables (taxonomías, custom fields, post_date, author).
- Indexación en tiempo real vs por lotes: El save_post hook brinda indexación en tiempo real para grandes sitios, usar procesos por lotes y en background.
- Seguridad: Use las API keys correctas (Admin keys sólo en servidor). En front-end, solo públicas/limitadas.
Transformar un post de WordPress en un documento para indexar
Siempre cree una función que convierta un objeto WP_Post en el array/objeto que enviará al índice. Incluya los campos esenciales y filtros para extensibilidad.
function my_transform_post_to_record( WP_Post post ) { // Evitar revisiones y borradores si no queremos indexarlos if ( in_array( post->post_status, array( auto-draft, inherit ) ) ) { return null } post_id = post->ID // Ejemplo de objeto mínimo record = array( objectID => post_ . post_id, // identificador único post_id => post_id, post_type => post->post_type, post_title => get_the_title( post ), post_excerpt => get_the_excerpt( post ), post_content => apply_filters( the_content, post->post_content ), post_date => get_post_time( U, true, post ), post_modified => get_post_modified_time( U, true, post ), permalink => get_permalink( post ), author => get_the_author_meta( display_name, post->post_author ), // Taxonomías principales (ejemplo: categories, tags) terms => wp_get_post_terms( post_id, array( category, post_tag ), array( fields => names ) ), // Campos personalizados (meta) - adaptar según necesidades meta => get_post_meta( post_id ), ) / Filtro para permitir que otros añadan/modifiquen el record / return apply_filters( my_index_record, record, post ) }
Indexación en tiempo real: hook save_post
Uso de save_post para enviar el post a Algolia o Meilisearch después de guardar. Evitar revisiones y autosaves.
add_action( save_post, my_index_post_on_save, 10, 3 ) function my_index_post_on_save( post_id, post, update ) { // Evitar autosaves y revisiones if ( wp_is_post_autosave( post_id ) wp_is_post_revision( post_id ) ) { return } // Ajustar tipos que queremos indexar if ( post !== post->post_type page !== post->post_type ) { return } record = my_transform_post_to_record( post ) if ( ! record ) { return } // Algolia if ( class_exists( AlgoliaAlgoliaSearchSearchClient ) ) { client = AlgoliaAlgoliaSearchSearchClient::create( ALGOLIA_APP_ID, ALGOLIA_ADMIN_API_KEY ) index = client->initIndex( wp_posts ) index->saveObjects( array( record ) ) } // Meilisearch if ( class_exists( MeiliSearchClient ) ) { msClient = new MeiliSearchClient( MEILISEARCH_HOST, MEILISEARCH_MASTER_KEY ) msIndex = msClient->index( wp_posts ) msIndex->addDocuments( array( record ) ) // asegúrate que objectID o primaryKey está definido } }
Eliminar del índice cuando se borra o traspasa a la papelera
add_action( before_delete_post, my_delete_post_from_index ) add_action( trashed_post, my_delete_post_from_index ) // Al enviar a papelera function my_delete_post_from_index( post_id ) { objectID = post_ . post_id // Algolia if ( class_exists( AlgoliaAlgoliaSearchSearchClient ) ) { client = AlgoliaAlgoliaSearchSearchClient::create( ALGOLIA_APP_ID, ALGOLIA_ADMIN_API_KEY ) index = client->initIndex( wp_posts ) index->deleteObject( objectID ) } // Meilisearch if ( class_exists( MeiliSearchClient ) ) { msClient = new MeiliSearchClient( MEILISEARCH_HOST, MEILISEARCH_MASTER_KEY ) msIndex = msClient->index( wp_posts ) msIndex->deleteDocument( objectID ) } }
Reindexación masiva (batches) — ejemplo con WP-CLI
Para sitios grandes, haga reindexación por lotes (chunks) desde WP-CLI para evitar timeouts y usar memoria eficientemente.
if ( defined( WP_CLI ) WP_CLI ) { WP_CLI::add_command( my-index rebuild, function( args, assoc_args ) { posts_per_batch = 200 paged = 1 // Inicializar cliente Algolia o Meilisearch fuera del loop para reutilizar algolia_client = AlgoliaAlgoliaSearchSearchClient::create( ALGOLIA_APP_ID, ALGOLIA_ADMIN_API_KEY ) algolia_index = algolia_client->initIndex( wp_posts ) while ( true ) { query = new WP_Query( array( post_type => array( post, page ), post_status => publish, posts_per_page => posts_per_batch, paged => paged, fields => ids, // ahorrar memoria ) ) if ( empty( query->posts ) ) { break } records = array() foreach ( query->posts as post_id ) { post = get_post( post_id ) rec = my_transform_post_to_record( post ) if ( rec ) { records[] = rec } } if ( ! empty( records ) ) { // Algolia: bulk algolia_index->saveObjects( records ) // Meilisearch: bulk (si fuese necesario) // msIndex->addDocuments(records) } WP_CLI::log( Indexed batch {paged} (.count(records). records) ) paged } WP_CLI::success( Reindex complete. ) } ) }
Ajustes y configuración específicos de Algolia
Después de indexar, configure searchableAttributes, attributesForFaceting y ranking con setSettings:
client = AlgoliaAlgoliaSearchSearchClient::create( ALGOLIA_APP_ID, ALGOLIA_ADMIN_API_KEY ) index = client->initIndex( wp_posts ) index->setSettings( array( searchableAttributes => array( title, unordered(post_title), post_content ), attributesForFaceting => array( filterOnly(post_type), searchable(categories), searchable(terms) ), customRanking => array( desc(post_date) ), attributesToRetrieve => array( objectID, post_title, permalink, post_excerpt ), ) )
Use las reglas de relevancia de Algolia y configure replicas si necesita orden por fecha o popularidad.
Ajustes y configuración específicos de Meilisearch
Meilisearch usa updateSettings / updateFilterableAttributes / updateRankingRules:
msClient = new MeiliSearchClient( MEILISEARCH_HOST, MEILISEARCH_MASTER_KEY ) index = msClient->index( wp_posts ) index->updateSettings( array( searchableAttributes => array( post_title, post_content ), filterableAttributes => array( post_type, terms, author ), rankingRules => array( words, typo, proximity, attribute, sort, exactness ), ) )
Indexar campos personalizados y taxonomías
Para indexar ACF u otros meta fields, añádalos explícitamente en la transformación:
function my_transform_post_to_record( WP_Post post ) { record = array( objectID => post_ . post->ID, post_title => get_the_title( post ), post_content => apply_filters( the_content, post->post_content ), price => get_post_meta( post->ID, price, true ), // ejemplo ACF categories => wp_get_post_terms( post->ID, category, array( fields => names ) ), ) // Clean / normalize values if ( isset( record[price] ) ) { record[price] = floatval( record[price] ) } return apply_filters( my_index_record, record, post ) }
Indexar archivos adjuntos y contenido multimedia
WordPress no extrae texto de PDFs/imagenes por defecto. Para indexar texto de attachments:
- Use un servicio OCR / text-extraction (ej. Tika, AWS Textract) para obtener el texto y añadirlo al record.
- Guarde en meta el texto extraído y márquelo como parte del record para búsquedas.
Manejo de errores y reintentos
- En producción, capture excepciones de las llamadas al cliente y haga reintentos exponenciales o push a una cola para reintentar.
- Use logs (error_log o logger específico) y métricas para detectar fallos en indexación.
- Para cargas masivas, prefiera WP-CLI o background workers en lugar de peticiones HTTP síncronas desde el frontend.
Buenas prácticas de rendimiento
- Indexe solo los campos necesarios para búsqueda.
- Use batching (ej. 100-500 registros por request según tamaño).
- En WP_Query para reindexaciones use fields => ids y recupere posts por ID para reducir memoria.
- Para indexación en tiempo real, desacople con una cola (Action Scheduler, WP Background Processing, o cola externa como Redis).
Comparativa rápida: Algolia vs Meilisearch
Característica | Algolia | Meilisearch |
---|---|---|
Licencia / hosting | Servicio SaaS, cuota según uso | Open-source, puede autopublicarse o usar SaaS |
Facetas y filtros | Completo, atributosForFaceting | FilterableAttributes, muy capaz |
Latencia | Extremadamente baja (SaaS global) | Baja si tiene infraestructura adecuada |
Coste | Pago por operaciones y almacenamiento | Self-host: coste infra Cloud: tarifas |
Seguridad: claves y límites
- Jamás exponga la Admin API Key en el navegador. Use solo Key públicas o rutas de servidor que firmen peticiones si necesita búsquedas restringidas.
- Algolia permite API Keys con filtros y expiración para búsquedas seguras en cliente.
- Para Meilisearch, use una API Key pública con permisos limitados para búsquedas y mantenga la MASTER_KEY en servidor.
Respaldo y verificación
Antes de reindexar masivamente, haga un respaldo del índice si el proveedor lo permite. Verifique un subset de documentos y su visualización en la interfaz (Algolia Dashboard o Meilisearch Studio).
Resumen de flujos recomendados
- Desarrollo: pruebe localmente con Meilisearch o con un índice de prueba en Algolia.
- Producción (sitio pequeño): indexación en save_post es suficiente.
- Producción (sitio grande): save_post que envíe un job a una cola proceso batch con WP-CLI para reindex monitorización de fallos.
Ejemplos y plantillas útiles
Inicializar clientes (ejemplo sencillo):
// Algolia define( ALGOLIA_APP_ID, your_app_id ) define( ALGOLIA_ADMIN_API_KEY, your_admin_api_key ) algolia = AlgoliaAlgoliaSearchSearchClient::create( ALGOLIA_APP_ID, ALGOLIA_ADMIN_API_KEY ) algolia_index = algolia->initIndex( wp_posts ) // Meilisearch define( MEILISEARCH_HOST, http://127.0.0.1:7700 ) define( MEILISEARCH_MASTER_KEY, your_master_key ) msClient = new MeiliSearchClient( MEILISEARCH_HOST, MEILISEARCH_MASTER_KEY ) msIndex = msClient->index( wp_posts )
Ejemplo rápido: reindexar un solo post (función utilitaria):
function my_reindex_single_post( post_id ) { post = get_post( post_id ) if ( ! post ) { return false } record = my_transform_post_to_record( post ) if ( ! record ) { return false } // Algolia client = AlgoliaAlgoliaSearchSearchClient::create( ALGOLIA_APP_ID, ALGOLIA_ADMIN_API_KEY ) index = client->initIndex( wp_posts ) index->saveObjects( array( record ) ) // Meilisearch msClient = new MeiliSearchClient( MEILISEARCH_HOST, MEILISEARCH_MASTER_KEY ) msIndex = msClient->index( wp_posts ) msIndex->addDocuments( array( record ) ) return true }
Notas finales
Indexar WordPress en Algolia o Meilisearch implica decidir qué contenido es valioso para la búsqueda, diseñar un mapping estable y elegir un flujo de indexación compatible con el tamaño del sitio. Implementar reintentos, colas y una reindexación por lotes controlada es la clave para mantener consistencia y rendimiento en producción.
Recuerde proteger las claves y monitorear la integridad del índice tras cambios masivos. Con los ejemplos presentados puede construir integraciones robustas y adaptarlas a campos personalizados, taxonomías y necesidades específicas de búsqueda avanzada.
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |