December 13, 2024

How to implement RavenDB suggestions with NestJS

Hey there! Since I will be away for a couple of weeks, I’d like to share with you an easy implementation of RavenDB suggestion capability which can work in conjunction with the search capability about which we talked in the last article.

This article is part 9 of a tutorial series about starting an app with the RavenDB and NestJS stack. You can find them in the RavenNest category. The setup which is used for this tutorial as well for the others in the series can be found in this Github repo, the tutorial9-suggestions branch. You can also find there a RavenDB backup which you can restore in order to follow through this article.

When to use suggestions

It’s important to understand the use-case for this capability. There are 2 situations which would require implementing the suggestions mechanism:

  1. Your search endpoint yielded no results, hence it is required to show something like: “Did you mean to look for suggestion?”
  2. You want to enrich the search results with additional data

No matter what your use-case may be, implementing it should be no issue. So let’s start 😁

Amending the MovieSearchIndex

The nice fact of adding suggestions is that we can do it directly in the MovieSearchIndex class with a one-liner. To understand more about this class please follow through my last article.

That’s it regarding the index. Now the full index will look like:

import { AbstractJavaScriptIndexCreationTask } from 'ravendb';
import { MovieEntity } from '../entities';

export class MovieSearchMap {
  public name: string;
  public year: number;
  public tags: string[];

  public searchTerms: string;
  public movieId: string;
}

export class MovieSearchIndex extends AbstractJavaScriptIndexCreationTask<
  MovieEntity,
    MovieSearchMap
> {
  constructor() {
    super();
    this.map(new MovieEntity().collectionName, (doc) => {
      const searchTerms =
        doc.name + ' ' +
        doc.year + ' ' +
        doc.tags.join(' ');

      return {
        movieId: doc['@metadata']['@id'],
        name: doc.name,
        year: doc.year,
        tags: doc.tags,
        searchTerms,
      };
    });

    this.index('searchTerms', 'Search');
    this.store('searchTerms', 'Yes');
    this.suggestion('searchTerms');
  }
}

Testing in the Studio

Once we run the app after applying the change above, we will be able to get suggestions from the index in RavenDB Studio:

As you can see, suggesting a term inside our movies database for “fycti0n” will return “fiction” in the suggestions array. Please note that this will not return the records themselves, just the suggestions based on the indexing on these terms. Actually, performing a search by “fiction” will for sure yield one result from our majestic movie database as you can notice below.

Let’s see some other scenarios:

What we can notice is that RavenDB is returning quite good suggestions. I would say it’s not really perfect and there is room for customization but this will be the topic of another discussion.

  • holow => hollow
  • graavens => raven

Adding suggestions into repository

Okay, now that we are convinced it’s behaving properly, let’s add this capability in our repository class. The method should be similar to the search one with the only difference that it will not return actual records:

public async suggestTerms(term: string): Promise<SuggestionsResponseObject> {
  const session = this.documentStore.openSession();

  const query = session.query({
    indexName: MovieSearchIndex.name,
  })
  .suggestUsing(b => b.byField('searchTerms', term))

  const results = await query.execute()

  session.dispose();

  return results;
}

Testing the new capability in our app

Good, we came quite far, our last step is to create the suggestion endpoint and test it! It only reduces to creating another API method in our MovieController class:

@Get('suggestTerms')
async suggestTerms(@Query('term') term: string) {
  if (term === null || term === '') {
    throw new BadRequestException();
  }
  return await this.movieRepo.suggestTerms(term);
}

Playing with Postman will get the same results as in the RavenStudio as earlier demoed.

Conclusion

Implementing suggestions is a powerful way to improve user experience inside your app. What I like about RavenDB is that it has this capability builtin and you just need to make the necessary arrangements to make use of it. And there you go, another customer happy! Hope you enjoyed this article and please subscribe to my newsletter, it will help me a lot, thanks!

Thanks for reading, I hope you found this article useful and interesting. If you have any suggestions don’t hesitate to contact me. If you found my content useful please consider a small donation. Any support is greatly appreciated! Cheers  😉

afivan

Enthusiast adventurer, software developer with a high sense of creativity, discipline and achievement. I like to travel, I like music and outdoor sports. Because I have a broken ligament, I prefer safer activities like running or biking. In a couple of years, my ambition is to become a good technical lead with entrepreneurial mindset. From a personal point of view, I’d like to establish my own family, so I’ll have lots of things to do, there’s never time to get bored 😂

View all posts by afivan →