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:
- Your search endpoint yielded no results, hence it is required to show something like: “Did you mean to look for suggestion?”
- 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 😉