» Node.js: Build a REST API with Express » 2. Development » 2.11 Search

Search

To implement search functionality in a RESTful API, you typically define a route that handles the search query parameters, searches using the prefix or full-text index in the underlying database, and returns the search results.

Search in MySQL

Add keyword parameter into BookManager.getBooks in domain/gateway/book_manager.ts:

@@ -5,5 +5,5 @@ export interface BookManager {
   updateBook(id: number, b: Book): Promise<void>;
   deleteBook(id: number): Promise<void>;
   getBook(id: number): Promise<Book | null>;
-  getBooks(offset: number): Promise<Book[]>;
+  getBooks(offset: number, keyword: string): Promise<Book[]>;
 }

Add search functionality in infrastructure/database/mysql.ts:

@@ -69,10 +69,18 @@ export class MySQLPersistence implements BookManager {
     return rows.length ? (rows[0] as Book) : null;
   }
 
-  async getBooks(offset: number): Promise<Book[]> {
-    const [rows] = await this.db
-      .promise()
-      .query("SELECT * FROM books LIMIT ?, ?", [offset, this.page_size]);
+  async getBooks(offset: number, keyword: string): Promise<Book[]> {
+    let query = "SELECT * FROM books";
+    let params: (string | number)[] = [];
+
+    if (keyword) {
+      query += " WHERE title LIKE ? OR author LIKE ?";
+      params = [`%${keyword}%`, `%${keyword}%`];
+    }
+
+    query += " LIMIT ?, ?";
+    params.push(offset, this.page_size);
+    const [rows] = await this.db.promise().query(query, params);
     return rows as Book[];
   }

Caution:

Don't do LIKE %keyword% in a production database. It's less efficient compared to other search methods, especially when dealing with large datasets. The LIKE %keyword% syntax forces the database to perform a full table scan or index scan, which can be slow, particularly on large tables. It doesn't leverage indexes efficiently, leading to slower query performance.
If your use case allows, consider using a prefix search (e.g., LIKE keyword%). This can leverage indexes more efficiently compared to substring searches, especially if most searches are for prefixes.

Tune application/executor/book_operator.ts:

@@ -23,13 +23,18 @@ export class BookOperator {
     return await this.bookManager.getBook(id);
   }
 
-  async getBooks(offset: number): Promise<Book[]> {
+  async getBooks(offset: number, query: string): Promise<Book[]> {
+    // Search results, don't cache it
+    if (query) {
+      return await this.bookManager.getBooks(offset, query);
+    }
+    // Normal list of results
     const k = `${booksKey}-${offset}`;
     const cache_value = await this.cacheHelper.load(k);
     if (cache_value) {
       return JSON.parse(cache_value);
     }
-    const books = await this.bookManager.getBooks(offset);
+    const books = await this.bookManager.getBooks(offset, "");
     await this.cacheHelper.save(k, JSON.stringify(books));
     return books;
   }

Pass in the query parameter in adapter/router.ts:

@@ -21,7 +21,10 @@ class RestHandler {
       offset = 0;
     }
     try {
-      const books = await this.bookOperator.getBooks(offset);
+      const books = await this.bookOperator.getBooks(
+        offset,
+        req.query.q as string
+      );
       res.status(200).json(books);
     } catch (err) {
       console.error(`Failed to get books: ${err}`);

Save the changes and restart the api server.

Try to search books with curl

Search books

curl -X GET "http://localhost:3000/books?q=and"

Result:

[
  {
    "id": 7,
    "title": "Pride and Prejudice",
    "author": "Jane Austen",
    "published_at": "1813-01-28",
    "description": "A classic novel exploring the themes of love, reputation, and social class in Georgian England.",
    "isbn": "9780486284736",
    "total_pages": 279,
    "created_at": "2024-03-01T21:48:25.000Z",
    "updated_at": "2024-03-01T21:48:25.000Z"
  },
  {
    "id": 13,
    "title": "War and Peace",
    "author": "Leo Tolstoy",
    "published_at": "1869-01-01",
    "description": "A novel depicting the Napoleonic era in Russia, exploring themes of love, war, and historical determinism.",
    "isbn": "9781400079988",
    "total_pages": 1392,
    "created_at": "2024-03-01T21:48:25.000Z",
    "updated_at": "2024-03-01T21:48:25.000Z"
  },
  {
    "id": 14,
    "title": "Alice’s Adventures in Wonderland",
    "author": "Lewis Carroll",
    "published_at": "1865-11-26",
    "description": "A children’s novel featuring a young girl named Alice who falls into a fantastical world populated by peculiar creatures.",
    "isbn": "9780141439761",
    "total_pages": 192,
    "created_at": "2024-03-01T21:48:25.000Z",
    "updated_at": "2024-03-01T21:48:25.000Z"
  }
]

Search books with an offset

curl -X GET "http://localhost:3000/books?q=the&o=4"

Result:

[
  {
    "id": 12,
    "title": "The Adventures of Huckleberry Finn",
    "author": "Mark Twain",
    "published_at": "1884-12-10",
    "description": "A novel depicting the journey of a young boy and an escaped slave along the Mississippi River.",
    "isbn": "9780486280615",
    "total_pages": 366,
    "created_at": "2024-03-01T21:48:25.000Z",
    "updated_at": "2024-03-01T21:48:25.000Z"
  },
  {
    "id": 15,
    "title": "The Odyssey",
    "author": "Homer",
    "published_at": "8th Century BC",
    "description": "An ancient Greek epic poem attributed to Homer, detailing the journey of Odysseus after the Trojan War.",
    "isbn": "9780140268867",
    "total_pages": 541,
    "created_at": "2024-03-01T21:48:25.000Z",
    "updated_at": "2024-03-01T21:48:25.000Z"
  }
]

Search in mongoDB

Add keyword parameter into ReviewManager.getReviewsOfBook in domain/gateway/review_manager.ts:

@@ -5,5 +5,5 @@ export interface ReviewManager {
   updateReview(id: string, r: Review): Promise<void>;
   deleteReview(id: string): Promise<void>;
   getReview(id: string): Promise<Review | null>;
-  getReviewsOfBook(book_id: number): Promise<Review[]>;
+  getReviewsOfBook(book_id: number, keyword: string): Promise<Review[]>;
 }

Add search functionality in infrastructure/database/mongo.ts:

@@ -1,4 +1,11 @@
-import { MongoClient, Db, Collection, ObjectId } from "mongodb";
+import {
+  MongoClient,
+  Db,
+  Collection,
+  ObjectId,
+  Filter,
+  Document,
+} from "mongodb";
 import { Review } from "@/domain/model";
 import { ReviewManager } from "@/domain/gateway";
 
@@ -63,8 +70,15 @@ export class MongoPersistence implements ReviewManager {
     };
   }
 
-  async getReviewsOfBook(book_id: number): Promise<Review[]> {
-    const cursor = this.coll.find({ book_id });
+  async getReviewsOfBook(book_id: number, keyword: string): Promise<Review[]> {
+    const filter: Filter<Document> = { book_id };
+    if (keyword) {
+      filter.$or = [
+        { title: { $regex: keyword, $options: "i" } }, // Case-insensitive regex search for title
+        { content: { $regex: keyword, $options: "i" } }, // Case-insensitive regex search for content
+      ];
+    }
+    const cursor = this.coll.find(filter);
     const reviewDocs = await cursor.toArray();
     return reviewDocs.map((reviewDoc) => ({
       id: reviewDoc._id.toHexString(),

Caution:

Regular expression queries can't fully utilize indexes. While some operations may still benefit from indexes, the use of regex can be less efficient compared to exact matches or prefix searches. If your collection has a large number of documents and the field being searched is not indexed, it could lead to slower query performance.

Tune application/executor/review_operator.ts:

@@ -26,8 +26,8 @@ export class ReviewOperator {
     return await this.reviewManager.getReview(id);
   }
 
-  async getReviewsOfBook(book_id: number): Promise<Review[]> {
-    return await this.reviewManager.getReviewsOfBook(book_id);
+  async getReviewsOfBook(book_id: number, query: string): Promise<Review[]> {
+    return await this.reviewManager.getReviewsOfBook(book_id, query);
   }
 
   async updateReview(id: string, r: Review): Promise<Review> {

Pass in the query parameter in adapter/router.ts:

@@ -100,7 +100,10 @@ class RestHandler {
     }
 
     try {
-      const books = await this.reviewOperator.getReviewsOfBook(bookID);
+      const books = await this.reviewOperator.getReviewsOfBook(
+        bookID,
+        req.query.q as string
+      );
       res.status(200).json(books);
     } catch (err) {
       console.error(`Failed to get reviews of book ${bookID}: ${err}`);

That's all you need to add search functionality into an API server based on mongoDB. Let's try it out.

Try to search book reviews with curl

Ingest some fake reviews first:

curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Alice Johnson", "title": "A Timeless Classic", "content": "The Great Gatsby is a timeless classic that delves deep into the complexities of the human psyche and the pursuit of the American Dream."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Michael Adams", "title": "Brilliantly Written", "content": "Fitzgerald’s prose in The Great Gatsby is simply brilliant. Each sentence is crafted with care, drawing the reader into a world of opulence and tragedy."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Emily Parker", "title": "Mesmerizing", "content": "I was completely mesmerized by The Great Gatsby. Fitzgerald’s ability to create such vivid characters and evoke a sense of longing is unmatched."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Daniel White", "title": "Enthralling Plot", "content": "The plot of The Great Gatsby is enthralling from start to finish. It’s a story of love, loss, and the emptiness of the American Dream."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Sophia Lee", "title": "A Literary Gem", "content": "The Great Gatsby is a literary gem that shines with its insightful commentary on society and human nature. A must-read for all."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Ethan Miller", "title": "Timeless Masterpiece", "content": "The Great Gatsby remains a timeless masterpiece that continues to resonate with readers across generations. Fitzgerald’s prose is as relevant today as it was in the 1920s."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Olivia Wilson", "title": "Immersive Experience", "content": "Reading The Great Gatsby is an immersive experience like no other. Fitzgerald’s vivid descriptions transport you to the roaring twenties, making you feel like you’re part of the story."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Jacob Thompson", "title": "Intriguing Characters", "content": "What sets The Great Gatsby apart are its intriguing characters. Each one is flawed yet fascinating, contributing to the richness of the narrative."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Ava Martinez", "title": "Gripping Narrative", "content": "The Great Gatsby grips you from the first page and doesn’t let go until the very end. It’s a story that lingers in your mind long after you’ve finished reading."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Noah Taylor", "title": "Profound Themes", "content": "Beneath its glitzy surface, The Great Gatsby explores profound themes of love, betrayal, and the corrupting influence of wealth. A true masterpiece."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Isabella Hernandez", "title": "Hauntingly Beautiful", "content": "The Great Gatsby is hauntingly beautiful, painting a picture of an era filled with glamour and excess, yet tinged with sadness and longing."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Lucas Moore", "title": "Compelling Narrative", "content": "Fitzgerald weaves a compelling narrative in The Great Gatsby, drawing readers into the world of Jay Gatsby and Daisy Buchanan with finesse and skill."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Mia Clark", "title": "Unforgettable Characters", "content": "The characters in The Great Gatsby are unforgettable, each with their own desires and flaws that drive the story forward with intensity and emotion."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Elijah Garcia", "title": "Eloquent Prose", "content": "Fitzgerald’s eloquent prose in The Great Gatsby elevates the novel to literary greatness. It’s a book that demands to be savored and appreciated."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Charlotte King", "title": "Riveting Plot", "content": "The plot of The Great Gatsby is riveting, filled with twists and turns that keep you on the edge of your seat until the very end. An absolute page-turner."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "William Brown", "title": "Emotionally Resonant", "content": "The Great Gatsby is emotionally resonant, exploring themes of love and longing in a way that stays with you long after you’ve turned the final page."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Chloe Rodriguez", "title": "Sensory Delight", "content": "Reading The Great Gatsby is a sensory delight, with Fitzgerald’s vivid descriptions bringing the sights, sounds, and smells of the 1920s to life."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "James Lee", "title": "Thought-Provoking", "content": "The Great Gatsby is thought-provoking, challenging readers to reflect on the nature of wealth, ambition, and the pursuit of happiness."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Emma Taylor", "title": "Rich Symbolism", "content": "The Great Gatsby is rich in symbolism, with every detail serving a purpose in conveying deeper themes and messages about society and human nature."}' http://localhost:3000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 4, "author": "Ryan Martinez", "title": "A Literary Masterpiece", "content": "The Great Gatsby is undeniably a literary masterpiece, with its lyrical prose and timeless themes making it a must-read for all lovers of literature."}' http://localhost:3000/reviews

Search reviews by its title or content with keyword: masterpiece

curl -X GET "http://localhost:3000/books/4/reviews?q=masterpiece"

Result:

[
  {
    "id": "65e2d01909ef3f4e2f726f04",
    "book_id": 4,
    "author": "Ethan Miller",
    "title": "Timeless Masterpiece",
    "content": "The Great Gatsby remains a timeless masterpiece that continues to resonate with readers across generations. Fitzgerald’s prose is as relevant today as it was in the 1920s.",
    "created_at": "2024-03-02T07:07:05.291Z",
    "updated_at": "2024-03-02T07:07:05.291Z"
  },
  {
    "id": "65e2d01909ef3f4e2f726f08",
    "book_id": 4,
    "author": "Noah Taylor",
    "title": "Profound Themes",
    "content": "Beneath its glitzy surface, The Great Gatsby explores profound themes of love, betrayal, and the corrupting influence of wealth. A true masterpiece.",
    "created_at": "2024-03-02T07:07:05.358Z",
    "updated_at": "2024-03-02T07:07:05.358Z"
  },
  {
    "id": "65e2d01909ef3f4e2f726f12",
    "book_id": 4,
    "author": "Ryan Martinez",
    "title": "A Literary Masterpiece",
    "content": "The Great Gatsby is undeniably a literary masterpiece, with its lyrical prose and timeless themes making it a must-read for all lovers of literature.",
    "created_at": "2024-03-02T07:07:05.519Z",
    "updated_at": "2024-03-02T07:07:05.519Z"
  }
]

Search reviews by its title or content with keyword: Fitzgerald

curl -X GET "http://localhost:3000/books/4/reviews?q=Fitzgerald"

Result:

[
  {
    "id": "65e2d01909ef3f4e2f726f00",
    "book_id": 4,
    "author": "Michael Adams",
    "title": "Brilliantly Written",
    "content": "Fitzgerald’s prose in The Great Gatsby is simply brilliant. Each sentence is crafted with care, drawing the reader into a world of opulence and tragedy.",
    "created_at": "2024-03-02T07:07:05.226Z",
    "updated_at": "2024-03-02T07:07:05.226Z"
  },
  {
    "id": "65e2d01909ef3f4e2f726f01",
    "book_id": 4,
    "author": "Emily Parker",
    "title": "Mesmerizing",
    "content": "I was completely mesmerized by The Great Gatsby. Fitzgerald’s ability to create such vivid characters and evoke a sense of longing is unmatched.",
    "created_at": "2024-03-02T07:07:05.243Z",
    "updated_at": "2024-03-02T07:07:05.243Z"
  },
  {
    "id": "65e2d01909ef3f4e2f726f04",
    "book_id": 4,
    "author": "Ethan Miller",
    "title": "Timeless Masterpiece",
    "content": "The Great Gatsby remains a timeless masterpiece that continues to resonate with readers across generations. Fitzgerald’s prose is as relevant today as it was in the 1920s.",
    "created_at": "2024-03-02T07:07:05.291Z",
    "updated_at": "2024-03-02T07:07:05.291Z"
  },
  {
    "id": "65e2d01909ef3f4e2f726f05",
    "book_id": 4,
    "author": "Olivia Wilson",
    "title": "Immersive Experience",
    "content": "Reading The Great Gatsby is an immersive experience like no other. Fitzgerald’s vivid descriptions transport you to the roaring twenties, making you feel like you’re part of the story.",
    "created_at": "2024-03-02T07:07:05.307Z",
    "updated_at": "2024-03-02T07:07:05.307Z"
  },
  {
    "id": "65e2d01909ef3f4e2f726f0a",
    "book_id": 4,
    "author": "Lucas Moore",
    "title": "Compelling Narrative",
    "content": "Fitzgerald weaves a compelling narrative in The Great Gatsby, drawing readers into the world of Jay Gatsby and Daisy Buchanan with finesse and skill.",
    "created_at": "2024-03-02T07:07:05.390Z",
    "updated_at": "2024-03-02T07:07:05.390Z"
  },
  {
    "id": "65e2d01909ef3f4e2f726f0c",
    "book_id": 4,
    "author": "Elijah Garcia",
    "title": "Eloquent Prose",
    "content": "Fitzgerald’s eloquent prose in The Great Gatsby elevates the novel to literary greatness. It’s a book that demands to be savored and appreciated.",
    "created_at": "2024-03-02T07:07:05.423Z",
    "updated_at": "2024-03-02T07:07:05.423Z"
  },
  {
    "id": "65e2d01909ef3f4e2f726f0f",
    "book_id": 4,
    "author": "Chloe Rodriguez",
    "title": "Sensory Delight",
    "content": "Reading The Great Gatsby is a sensory delight, with Fitzgerald’s vivid descriptions bringing the sights, sounds, and smells of the 1920s to life.",
    "created_at": "2024-03-02T07:07:05.471Z",
    "updated_at": "2024-03-02T07:07:05.471Z"
  }
]

Nice! Search functionality with MySQL or mongoDB is done!

If you want advanced full-text search support, consider using Elasticsearch or Solr. These solutions are optimized for text search operations and offer features such as indexing, relevance scoring, and language-specific stemming.