Full-Text Search Integration (e.g., Event Bus)

In modern applications, search functionality is a critical feature, enabling users to retrieve relevant data quickly and efficiently. Full-text search integration enhances the ability to search through large volumes of text data by using specialized indexes and tools optimized for fast querying. This chapter will cover the implementation of full-text search in an Express.js application, using a distributed communication system like an Event Bus to keep search indexes synchronized with the database.

Introduction to Full-Text Search

What is Full-Text Search?

Full-text search allows querying text fields (like titles, descriptions, and content) using keywords, phrases, or patterns. Unlike traditional SQL-based “LIKE” queries, full-text search is faster, more accurate, and supports advanced features like stemming, ranking, and fuzzy matching.

Key Features of Full-Text Search

  1. Tokenization: Breaks text into searchable tokens.
  2. Stemming: Reduces words to their root forms (e.g., “running” → “run”).
  3. Ranking: Orders results by relevance.
  4. Fuzzy Matching: Finds approximate matches (e.g., “neighbr” → “neighbor”).

Tools for Full-Text Search

Popular Full-Text Search Engines

  1. Elasticsearch: A distributed search and analytics engine.
  2. Meilisearch: Lightweight and easy-to-use full-text search engine.
  3. MongoDB: Built-in full-text search capabilities in modern versions.
  4. PostgreSQL: Supports full-text search through extensions.

For this chapter, we’ll use Elasticsearch, as it’s one of the most robust and scalable solutions.

Setting Up the Environment

Prerequisites

  1. Install Node.js and Express.js.
  2. Install Elasticsearch using Docker
				
					docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:8.10.2

				
			

3.Install required NPM libraries:

				
					npm install express body-parser @elastic/elasticsearch amqplib

				
			

Designing the System

We’ll build a microservices-based application:

  1. Post Service: Manages blog posts and publishes events when a post is created or updated.
  2. Search Service: Listens to these events, updates the Elasticsearch index, and provides search functionality.

An Event Bus (using RabbitMQ) will act as the communication layer between services.

Implementing the Event Bus

Connecting RabbitMQ

Start RabbitMQ using Docker:

				
					docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:management

				
			

Building the Post Service

The Post Service allows users to create and update blog posts. It publishes events to the Event Bus for the Search Service to process.

Code

				
					const express = require("express");
const amqp = require("amqplib");

const app = express();
app.use(express.json());

let channel, connection;

async function connectRabbitMQ() {
  connection = await amqp.connect("amqp://localhost");
  channel = await connection.createChannel();
  await channel.assertExchange("event_bus", "fanout", { durable: false });
}

app.post("/posts", async (req, res) => {
  const post = req.body;
  console.log("Post Created:", post);

  // Publish an event to the Event Bus
  channel.publish("event_bus", "", Buffer.from(JSON.stringify({ type: "post_created", post })));
  res.status(201).send("Post created and event published");
});

app.listen(3000, async () => {
  console.log("Post Service running on port 3000");
  await connectRabbitMQ();
});

				
			

Building the Search Service

The Search Service listens for events from the Event Bus, updates Elasticsearch indexes, and provides a search API.

Code

				
					const express = require("express");
const amqp = require("amqplib");
const { Client } = require("@elastic/elasticsearch");

const app = express();
const esClient = new Client({ node: "http://localhost:9200" });

async function connectRabbitMQ() {
  const connection = await amqp.connect("amqp://localhost");
  const channel = await connection.createChannel();
  await channel.assertExchange("event_bus", "fanout", { durable: false });

  const q = await channel.assertQueue("", { exclusive: true });
  channel.bindQueue(q.queue, "event_bus", "");

  console.log("Search Service listening for events...");
  channel.consume(q.queue, async (msg) => {
    const event = JSON.parse(msg.content.toString());
    if (event.type === "post_created") {
      console.log("Processing event:", event);

      // Index the post in Elasticsearch
      await esClient.index({
        index: "posts",
        id: event.post.id,
        body: event.post,
      });
      console.log("Post indexed in Elasticsearch");
    }
  }, { noAck: true });
}

app.get("/search", async (req, res) => {
  const { query } = req.query;
  const results = await esClient.search({
    index: "posts",
    body: {
      query: {
        match: { content: query },
      },
    },
  });

  res.send(results.hits.hits.map((hit) => hit._source));
});

app.listen(4000, async () => {
  console.log("Search Service running on port 4000");
  await connectRabbitMQ();
});

				
			

Testing the System

  • Start RabbitMQ and Elasticsearch.
  • Run the Post and Search Services.
  • Create a post using Postman or CURL
				
					curl -X POST -H "Content-Type: application/json" -d '{"id": 1, "title": "Hello World", "content": "Express.js is amazing!"}' http://localhost:3000/posts

				
			

Search for the post:

				
					curl "http://localhost:4000/search?query=amazing"

				
			

Expected Output: The search result should display the post details.

Advanced Topics

Handling Updates and Deletions

  • For updates, re-index the post using the same id.
  • For deletions, remove the document from Elasticsearch 
				
					await esClient.delete({ index: "posts", id: postId });

				
			

Real-Time Indexing

  • Use RabbitMQ acknowledgments to ensure events are processed reliably.

Scaling with Sharding

  • Distribute the Elasticsearch index across multiple nodes for large datasets.

Multi-Language Search

  • Configure Elasticsearch analyzers to handle different languages.

Integrating full-text search with microservices using an Event Bus creates a robust, scalable architecture for managing and querying textual data. By combining RabbitMQ and Elasticsearch, you can achieve efficient, asynchronous synchronization of search indexes with your primary data source. Happy coding !❤️

Table of Contents

Contact here

Copyright © 2025 Diginode

Made with ❤️ in India