Como indexar contenido en Algolia/Meilisearch desde WP en PHP en WordPress

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:

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

  1. Indexe solo los campos necesarios para búsqueda.
  2. Use batching (ej. 100-500 registros por request según tamaño).
  3. En WP_Query para reindexaciones use fields => ids y recupere posts por ID para reducir memoria.
  4. 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

  1. Desarrollo: pruebe localmente con Meilisearch o con un índice de prueba en Algolia.
  2. Producción (sitio pequeño): indexación en save_post es suficiente.
  3. 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 🙂



Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *