Saturday, February 25, 2023

Autocomplete with Redis Search

Summary

In this post, I'm going demonstrate a real-world usage scenario of one of the features of Redis Search: Suggestion (aka autocomplete).  This particular autocomplete scenario is around addresses, similar to what you see in Google Maps.  I pull real address data from a Canadian government statistics site and populate Redis suggestion dictionaries for autocomplete of either full address (with a street number) or just street name.  The subsequent address chosen by the user is then put into a full Redis search for an exact match.

Architecture


Application

I wrote this app completely in Javascript:  front and back end.  

Front End

The front end is a static web page with a single text input.  The expected input is either a street name or house number + street name.  The page leverages this Javascript autocomplete input module.  The module generates REST calls to the back end.  Screenshot below:



Back End

The back end consists of two Nodejs files:  dataLoader.js and app.js.  The dataLoader module handles fetching data from the Canadian gov site and loading it into Redis as JSON objects.  Additionally, it sets up two suggestion dictionaries: one that includes the street number with the address and another that does not.  Snippet below of the Redis client actions.
  1. async #insert(client, doc) {
  2. await client.json.set(`account:${doc.id}`, '.', doc);
  3.  
  4. const addr = doc.address;
  5. if (addr) {
  6. await client.ft.sugAdd(`fAdd`, addr, 1);
  7. await client.ft.sugAdd(`pAdd`, addr.substr(addr.indexOf(' ') + 1), 1);
  8. }
  9. }
App.js is an ExpressJS-based REST API server.  It exposes a couple GET endpoints: one for address suggestions and the other for a full-text search of an address.  A snippet of the address suggest endpoint below.

  1. app.get('/address/suggest', async (req, res) => {
  2. const address = decodeURI(req.query.address);
  3. console.log(`app - GET /address/suggest ${address}`);
  4. try {
  5. let addrs;
  6. if (address.match(/^\d/)) {
  7. addrs = await client.ft.sugGet(`fAdd`, address);
  8. }
  9. else {
  10. addrs = await client.ft.sugGet(`pAdd`, address);
  11. }
  12. let suggestions = []
  13. for (const addr of addrs) {
  14. suggestions.push({address: addr})
  15. }
  16. res.status(200).json(suggestions);
  17. }
  18. catch (err) {
  19. console.error(`app - GET /address/suggest ${req.query.address} - ${err.message}`)
  20. res.status(400).json({ 'error': err.message });
  21. }
  22. });

Source


Copyright ©1993-2024 Joey E Whelan, All rights reserved.