RedisSearch! An option worth considering for a search engine

RedisSearch! An option worth considering for a search engine

Kaitou
Kaitou

Problem

Your system needs to help users find the data they want. For example, a movie website allows users to search for movies by name, author, actor, etc.

The first thing that comes to mind is using SQL's LIKE. It is a good and fast approach to problem solving. But overall it's not really good. We want more than that. Such as full-text search support, search query suggestions, etc., it is time to implement a search engine system.

There are many famous engine support: ElasticSearch, Solr, ... And there's a new guy who's also quite strong.

That is RedisSearch. even though it just appeared recently but he is good choice to consider by those things:

  • simple syntax, easy to approach.
  • does not require a heavy infrastructure to maintain (like ElasticSearch for example)
  • the search speed is quite fast
  • especially if you are using Redis for other features like Cache, Queue, ... Then implementing more search using Redis is great

Let's start exploring RedisSearch

RedisSearch ?

For ease of understanding, RedisSearch is an extension module of Redis. It's support full-text search, suggest keywords, so on.

verbose info

Flow implement RedisSearch

img

Above is simple schema of RedisSearch integration. There are a few points to note:

  • It is related to the fields you need to search in the future. As the name of the video, actors,...
  • Need to build pusher and querier:
    pusher: supports pushing data from the current database to Redis.
    querier: current API support that interacts with RedisSearch

Practice

For example, there is a movie website. And need to integrate the search feature.

  • Setup RedisSearch
    For quickly, we will use docker to build environment

    docker run -p 6377:6379 redislabs/redisearch:latest
    
  • Create index document

    // createIndexDocument.ts
    const createVideoIndexDocument = async () => {
      const redis = await useRedisClient();
      await redis.ft.create(movieIndex, {
        name: {
          type: SchemaFieldTypes.TEXT,
          SORTABLE: true,
        },
        description: {
          type: SchemaFieldTypes.TEXT,
          SORTABLE: true,
        },
        duration: {
          type: SchemaFieldTypes.NUMERIC,
          SORTABLE: true,
        },
      });
    };
    createVideoIndexDocument();
    

    ts-node createIndexDocument.ts # the script will generate video index document

  • Import sample data
    To make your life easier, I have created a json file which contains over 300 movies

    [ { "name": "[Star Talk] Which Penang hill is a must visit for Singapore leading male idol Li Nanxing?", "description": "[star talk] which penang hill is a must visit for singapore leading male idol li nanxing?", "duration": 716 }, { "name": "[Lu Lu Land] Jackson Wang Sends JJ Lin Birthday Greetings for His Birthday", "description": "[lu lu land] jackson wang sends jj lin birthday greetings for his birthday", "duration": 983 }, { "name": "[Test Me If You Can] Great Gadgets for Mopping and Sweeping! | EP 2/8", "description": "[test me if you can] great gadgets for mopping and sweeping! | ep 2/8", "duration": 202 }, { "name": "[Ah Boys No Limit] Challenge starts! Team PVP mode on! | EP 41/45", "description": "[ah boys no limit] challenge starts! team pvp mode on! | ep 41/45", "duration": 296 }, { "name": "[Ah Boys No Limit] ABNL Cheerleading captain is here! The prophecy of Hafiz came true? | EP 42/45", "description": "[ah boys no limit] abnl cheerleading captain is here! the prophecy of hafiz came true? | ep 42/45", "duration": 676 }, ... ]

    Run the script to import above info info into Redis

    // pusher.ts
    import { createClient } from "redis";
    import fs from "fs";
    import path from "path";
    
    const videos = JSON.parse(
      fs.readFileSync(path.resolve(__dirname, "./data/videos.json"), "utf-8")
    );
    
    const importMoviesData = async function () {
      const client = createClient({
        url: "redis://localhost:6380",
      });
      console.info("connecting redis");
      await client.connect();
      console.info("inserted item");
    
      let idx = 0;
      for (const item of videos) {
        await client.hSet("video:" + idx, item);
        idx++;
      }
    };
    importMoviesData();
    

    ts-node pusher.ts

  • Implement RedisSearch

    // search.ts
    const fullTextSearch = async function () {
      const client = await redisClient();
    
      const readline = rl.createInterface({
        input: process.stdin,
        output: process.stdout,
      });
      while (true) {
        const obtainAnswerResult = () => {
          return new Promise((res) => {
            readline.question("input your text: ", (t) => {
              res(t);
            });
          });
        };
    
        const text = await obtainAnswerResult();
        console.info("searching for", text);
        const rs = await client.ft.search(movieIndex, `*${text}*`);
        console.info(rs.documents);
      }
    };
    fullTextSearch();
    

    ts-node search.ts

    Result

image-01-2

Advanced Features

There are still many features on RedisSearch. You can explore here

Thank for reading the post.