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.
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.
For this chapter, we’ll use Elasticsearch, as it’s one of the most robust and scalable solutions.
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
We’ll build a microservices-based application:
An Event Bus (using RabbitMQ) will act as the communication layer between services.
Start RabbitMQ using Docker:
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:management
The Post Service allows users to create and update blog posts. It publishes events to the Event Bus for the Search Service to process.
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();
});
The Search Service listens for events from the Event Bus, updates Elasticsearch indexes, and provides a search API.
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();
});
curl -X POST -H "Content-Type: application/json" -d '{"id": 1, "title": "Hello World", "content": "Express.js is amazing!"}' http://localhost:3000/posts
curl "http://localhost:4000/search?query=amazing"
Expected Output: The search result should display the post details.
id
.
await esClient.delete({ index: "posts", id: postId });
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 !❤️