Elasticsearch with Node.js — Search Guide

Sanjeev SharmaSanjeev Sharma
3 min read

Advertisement

Elasticsearch is the gold standard for search and log analysis. Perfect for building search features that feel responsive.

Setup

npm install @elastic/elasticsearch

Connection

import { Client } from "@elastic/elasticsearch";

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

// Check connection
const health = await client.cluster.health();
console.log("Cluster health:", health);

Indexing

// Create index with mapping
await client.indices.create({
  index: "products",
  mappings: {
    properties: {
      name: { type: "text" },
      description: { type: "text" },
      price: { type: "number" },
      category: { type: "keyword" },
      createdAt: { type: "date" },
    },
  },
});

// Index document
await client.index({
  index: "products",
  id: "1",
  document: {
    name: "Laptop",
    description: "High-performance laptop",
    price: 1200,
    category: "electronics",
    createdAt: new Date(),
  },
});

// Bulk indexing
await client.bulk({
  operations: [
    { index: { _index: "products", _id: "1" } },
    { name: "Product 1", price: 100 },
    { index: { _index: "products", _id: "2" } },
    { name: "Product 2", price: 200 },
  ],
});

Searching

// Simple search
const results = await client.search({
  index: "products",
  query: {
    match: {
      name: "laptop",
    },
  },
});

// Full-text search
const fulltext = await client.search({
  index: "products",
  query: {
    multi_match: {
      query: "laptop computer",
      fields: ["name", "description"],
    },
  },
});

// Filters (no scoring)
const filtered = await client.search({
  index: "products",
  query: {
    bool: {
      must: [
        { match: { name: "laptop" } },
      ],
      filter: [
        { term: { category: "electronics" } },
        { range: { price: { gte: 500, lte: 2000 } } },
      ],
    },
  },
});

// Aggregations
const aggs = await client.search({
  index: "products",
  aggs: {
    categories: {
      terms: { field: "category" },
    },
    price_ranges: {
      range: {
        field: "price",
        ranges: [
          { to: 500 },
          { from: 500, to: 1000 },
          { from: 1000 },
        ],
      },
    },
  },
});

Analyzers

// Create index with analyzer
await client.indices.create({
  index: "articles",
  settings: {
    analysis: {
      analyzer: {
        custom_analyzer: {
          type: "custom",
          tokenizer: "standard",
          filter: ["lowercase", "stop"],
        },
      },
    },
  },
  mappings: {
    properties: {
      title: {
        type: "text",
        analyzer: "custom_analyzer",
      },
      content: {
        type: "text",
        analyzer: "custom_analyzer",
      },
    },
  },
});
class BlogSearchService {
  constructor(private client: Client) {}

  async indexPost(post: {
    id: string;
    title: string;
    content: string;
    tags: string[];
    createdAt: Date;
  }) {
    await this.client.index({
      index: "blog",
      id: post.id,
      document: post,
    });
  }

  async search(query: string, options = { from: 0, size: 10 }) {
    const results = await this.client.search({
      index: "blog",
      from: options.from,
      size: options.size,
      query: {
        multi_match: {
          query,
          fields: ["title^2", "content"],
        },
      },
      highlight: {
        fields: {
          content: {},
        },
      },
    });

    return results.hits.hits.map((hit: any) => ({
      ...hit._source,
      highlights: hit.highlight,
    }));
  }

  async searchByTag(tag: string) {
    const results = await this.client.search({
      index: "blog",
      query: {
        term: { tags: tag },
      },
    });
    return results.hits.hits.map((hit: any) => hit._source);
  }

  async deletePost(id: string) {
    await this.client.delete({
      index: "blog",
      id,
    });
  }
}

FAQ

Q: Should I use Elasticsearch for everything? A: No, it's for search and log analysis. Use databases for CRUD operations.

Q: How do I keep data in sync? A: Index when creating/updating in database. Use event-driven sync for consistency.

Q: What's the performance? A: Sub-100ms searches on millions of documents. Very fast.


Elasticsearch powers search on billions of documents globally. Master it for search-heavy applications.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro