Node.js con ElasticSearch

Visitas

14 de Febrero, 2019February 14, 2019

Volver al blogBack to blog

Si alguna vez te has preguntado cómo servicios como Netflix, Uber o GitHub logran búsquedas tan rápidas entre millones de registros, la respuesta probablemente sea ElasticSearch. En este tutorial aprenderás a integrar este poderoso motor de búsquedas con Node.js utilizando el framework Koa.

If you've ever wondered how services like Netflix, Uber, or GitHub achieve such fast searches across millions of records, the answer is probably ElasticSearch. In this tutorial you'll learn how to integrate this powerful search engine with Node.js using the Koa framework.

🔍 ¿Qué es ElasticSearch?🔍 What is ElasticSearch?

ElasticSearch es un motor de búsqueda y análisis distribuido, de código abierto, construido sobre Apache Lucene. Es capaz de manejar grandes volúmenes de datos con tiempos de respuesta en milisegundos, lo que lo convierte en la opción preferida para aplicaciones que requieren búsquedas en tiempo real.

ElasticSearch is a distributed, open-source search and analytics engine built on top of Apache Lucene. It can handle large volumes of data with millisecond response times, making it the preferred choice for applications that require real-time searches.

Aunque está escrito en Java, ElasticSearch expone una API REST que nos permite interactuar con él desde cualquier lenguaje de programación: JavaScript, Python, C#, Go, y más.

Although it's written in Java, ElasticSearch exposes a REST API that allows us to interact with it from any programming language: JavaScript, Python, C#, Go, and more.

✨ Características principales✨ Key Features

📋 Requisitos previos:📋 Prerequisites: Asegúrate de tener instalado Node.js (v14+), npm o Yarn, y ElasticSearch (v7+) corriendo en tu máquina local.Make sure you have Node.js (v14+), npm or Yarn, and ElasticSearch (v7+) installed and running on your local machine.

🚀 Configurando el proyecto con Koa🚀 Setting Up the Project with Koa

Usaremos Koa, un framework minimalista creado por el equipo detrás de Express. Koa aprovecha async/await de forma nativa, eliminando callbacks y haciendo el código más limpio y mantenible.

We'll use Koa, a minimalist framework created by the team behind Express. Koa leverages async/await natively, eliminating callbacks and making the code cleaner and more maintainable.

# Instalar el generador de Koa globalmente
$ npm install -g koa-generator

# Crear nuevo proyecto
$ koa serverElastic --hbs

# Entrar al directorio e instalar dependencias
$ cd serverElastic && npm install

📦 Instalando el cliente oficial📦 Installing the Official Client

# Cliente oficial de Elasticsearch para Node.js
$ npm install @elastic/elasticsearch

🔌 Configurando la conexión🔌 Configuring the Connection

Primero, creamos un módulo para gestionar la conexión con ElasticSearch. Es buena práctica verificar la conexión al iniciar la aplicación:

First, we create a module to manage the connection to ElasticSearch. It's good practice to verify the connection when starting the application:

const { Client } = require('@elastic/elasticsearch');

const client = new Client({
  node: 'http://localhost:9200',
  auth: {
    username: 'elastic',
    password: 'changeme'
  }
});

// Verificar conexión
async function checkConnection() {
  try {
    const health = await client.cluster.health();
    console.log('ElasticSearch conectado:', health.status);
  } catch (error) {
    console.error('Error de conexión:', error);
  }
}

checkConnection();

Creando un índiceCreating an Index

async function createIndex(indexName) {
  try {
    await client.indices.create({
      index: indexName,
      body: {
        mappings: {
          properties: {
            title: { type: 'text' },
            content: { type: 'text' },
            author: { type: 'keyword' },
            created_at: { type: 'date' }
          }
        }
      }
    });
    console.log(`Índice ${indexName} creado`);
  } catch (error) {
    console.error('Error creando índice:', error);
  }
}

createIndex('articles');

Insertando documentosInserting Documents

async function indexDocument(index, document) {
  try {
    const result = await client.index({
      index: index,
      body: document
    });
    console.log('Documento indexado:', result._id);
    return result;
  } catch (error) {
    console.error('Error indexando:', error);
  }
}

// Ejemplo de uso
indexDocument('articles', {
  title: 'Introducción a ElasticSearch',
  content: 'ElasticSearch es un motor de búsqueda distribuido...',
  author: 'Oscar',
  created_at: new Date()
});

🔎 Implementando búsquedas🔎 Implementing Searches

El poder real de ElasticSearch está en sus capacidades de búsqueda. Utilizamos multi_match para buscar en múltiples campos simultáneamente:

The real power of ElasticSearch lies in its search capabilities. We use multi_match to search across multiple fields simultaneously:

async function search(index, query) {
  try {
    const result = await client.search({
      index: index,
      body: {
        query: {
          multi_match: {
            query: query,
            fields: ['title^2', 'content'], // title tiene peso x2
            fuzziness: 'AUTO' // Tolerancia a errores tipográficos
          }
        },
        highlight: {
          fields: { title: {}, content: {} }
        }
      }
    });
    return result.hits.hits;
  } catch (error) {
    console.error('Error en búsqueda:', error);
  }
}

// Ejemplo de uso
const results = await search('articles', 'elasticsearch');
console.log(results);

🌐 API REST con Koa🌐 REST API with Koa

Finalmente, exponemos nuestra funcionalidad de búsqueda a través de una API REST limpia y eficiente:

Finally, we expose our search functionality through a clean and efficient REST API:

const Koa = require('koa');
const Router = require('koa-router');
const { Client } = require('@elastic/elasticsearch');

const app = new Koa();
const router = new Router();
const client = new Client({ node: 'http://localhost:9200' });

// Middleware para manejo de errores
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    ctx.status = err.status || 500;
    ctx.body = { error: err.message };
  }
});

// Endpoint de búsqueda
router.get('/api/search', async (ctx) => {
  const { q, page = 1, limit = 10 } = ctx.query;
  
  if (!q) {
    ctx.status = 400;
    ctx.body = { error: 'Query parameter "q" is required' };
    return;
  }
  
  const result = await client.search({
    index: 'articles',
    body: {
      from: (page - 1) * limit,
      size: limit,
      query: {
        multi_match: {
          query: q,
          fields: ['title^2', 'content']
        }
      }
    }
  });
  
  ctx.body = {
    total: result.hits.total.value,
    page: parseInt(page),
    results: result.hits.hits.map(hit => ({
      id: hit._id,
      score: hit._score,
      ...hit._source
    }))
  };
});

app.use(router.routes());
app.listen(3000, () => {
  console.log('🚀 Server running on http://localhost:3000');
});

🎯 Conclusión🎯 Conclusion

Has aprendido a configurar ElasticSearch con Node.js y Koa, crear índices, indexar documentos y realizar búsquedas avanzadas. Esta combinación es ideal para:

You've learned how to set up ElasticSearch with Node.js and Koa, create indices, index documents, and perform advanced searches. This combination is ideal for:

El rendimiento de ElasticSearch es impresionante: puede manejar millones de documentos con tiempos de respuesta de milisegundos. ¡Experimenta y lleva tus aplicaciones al siguiente nivel!

ElasticSearch's performance is impressive: it can handle millions of documents with millisecond response times. Experiment and take your applications to the next level!