Fuzzy search is a technique that finds approximate matches for the search string or characters; even when it does not exactly match the entered information.
For example, if a user searches for “Node,” they will get an unmatched result when they type “Nede.” But a fuzzy search returns results containing both “Node” and “Nede” words.
Fuzzy search logic is one of the basics of spell checker and was implemented using various algorithms like Levenshtein distance, Damerau-Levenshtein distance, Bitap algorithm, Smith-Waterman algorithm, and others. It can be used on any search engine algorithms and has already been implemented in several popular search engines (Google, Yahoo, Bing, etc.) and databases.
In this blog, you are going to learn how to achieve fuzzy searches using our Essential JS 2 autocomplete component and Fusejs.
A fuzzy search can be performed with the help of the search method of the Fusejs library and a filtering event of autocomplete component. The filtering event provides an option to filter the existing data source and rebind with the modified data source.
Configure the autocomplete component in Angular:
git clone https://github.com/angular/quickstart.git quickstart cd quickstart npm install
npm install @syncfusion/ej2-ng-dropdowns --save
npm install fuse.js
/** * System configuration for Angular samples. */(function (global) { System.config({ paths: { // paths serve as alias 'npm:': 'node_modules/', 'syncfusion:': 'node_modules/@syncfusion/', }, map: { // our app is in the app folder. app: 'app', // Angular bundles // Syncfusion bundles '@syncfusion/ej2-base': 'syncfusion:ej2-base/dist/ej2-base.umd.min.js', '@syncfusion/ej2-data': 'syncfusion:ej2-data/dist/ej2-data.umd.min.js', '@syncfusion/ej2-inputs': 'syncfusion:ej2-inputs/dist/ej2-inputs.umd.min.js', '@syncfusion/ej2-popups': 'syncfusion:ej2-popups/dist/ej2-popups.umd.min.js', '@syncfusion/ej2-lists': 'syncfusion:ej2-lists/dist/ej2-lists.umd.min.js', '@syncfusion/ej2-buttons': 'syncfusion:ej2-buttons/dist/ej2-buttons.umd.min.js', '@syncfusion/ej2-splitbuttons': 'syncfusion:ej2-splitbuttons/dist/ej2-splitbuttons.umd.min.js', '@syncfusion/ej2-dropdowns': 'syncfusion:ej2-dropdowns/dist/ej2-dropdowns.umd.min.js', '@syncfusion/ej2-ng-base': 'syncfusion:ej2-ng-base/dist/ej2-ng-base.umd.min.js', '@syncfusion/ej2-ng-dropdowns': 'syncfusion:ej2-ng-dropdowns/dist/ej2-ng-dropdowns.umd.min.js', 'fuse.js': 'npm:fuse.js/dist/fuse.min.js', } }); })(this);
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AutoCompleteComponent } from '@syncfusion/ej2-ng-dropdowns'; import { AppComponent } from './app.component'; @NgModule({ imports: [ BrowserModule ], //declaration of ej2-ng-dropdowns module in NgModule. declarations: [ AppComponent, AutoCompleteComponent ], bootstrap: [ AppComponent ] }) export class AppModule { }
import { Component, ViewEncapsulation } from '@angular/core'; @Component({ selector: 'my-app', // specifies the template string for the autocomplete component. template: ``, // include the material theme in autocomplete. styleUrls: ['../../node_modules/@syncfusion/ej2-dropdowns/styles/material.css'], encapsulation: ViewEncapsulation.None }) export class AppComponent { }
After initializing, populate data using the dataSource property of autocomplete. Here, we have used the books data as the data source of autocomplete.
import { Component,ViewEncapsulation } from '@angular/core'; import * as Fuse from 'fuse.js'; import { EmitType } from '@syncfusion/ej2-base'; import { FilteringEventArgs } from '@syncfusion/ej2-dropdowns'; @Component({ selector: 'my-app', template: `
`, // include the material theme in autocomplete. styleUrls: [‘../../node_modules/@syncfusion/ej2-dropdowns/styles/material.css’], encapsulation: ViewEncapsulation.None }) export class AppComponent { name = ‘Angular’; public booksData: { [key: string]: Object; }[] = [ { BookName: ‘Support Vector Machines Succinctly’, BookID: ‘BOOK1’ }, { BookName: ‘Scala Succinctly’, BookID: ‘BOOK2’ }, { BookName: ‘Application Security in .NET Succinctly’, BookID: ‘BOOK3’ },{ BookName: ‘Node.js Succinctly’, BookID: ‘BOOK36’ } ]; // maps the appropriate column to the fields property. public fields: Object = { value: ‘BookName’ }; // set placeholder for autocomplete input element. public watermark: string = ‘e.g. Node.js Succinctly’; }
Define the filtering event to rebind the data source with the result of the Fuse JS search method. You can use your own libraries to filter the data and update it to autocomplete’s suggestion list through the updateData method.
public onFiltering: EmitType = (e: FilteringEventArgs) => { // pass the filter data source to updateData method. e.updateData([], null); }
Configure the Fuse JS library with autocomplete:
import * as Fuse from 'fuse.js';
public onFiltering: EmitType = (e: FilteringEventArgs) => { let options: Object = { keys: ['BookName'], includeMatches: true, findAllMatches: true }; // create object from Fuse constructor. let fuse: Fuse = new Fuse(this.booksData, options); // store the search result data based on typed characters. let result: any = fuse.search(e.text) }
let data: { [key: string]: Object; }[] = []; for (let i: number = 0; i < result.length; i++) { data.push(result[i].item as any); } // pass the filter data source to updateData method. e.updateData(data, null); let popupElement: HTMLElement = document.getElementById('books_popup'); let lists: Element[] = <nodelistof & Element[]>popupElement.querySelectorAll('.e-list-item'); // For highlight the typed characters, pass the result data and list items to highlightSearch method. this.highlightSearch(lists, result);</nodelistof
public highlightSearch(listItems: Element[], result: any): void {
if (result.length > 0) {
for (let i: number = 0; i < listItems.length; i++) {
let innerHTML: string = listItems[i].innerHTML;
for (let j: number = result[i].matches[0].indices.length - 1; j >= 0; j--) {
let indexes: number[] = result[i].matches[0].indices[j];
innerHTML = innerHTML.substring(0, indexes[0]) + '' +
innerHTML.substring(indexes[0], (indexes[1] + 1)) + '' + innerHTML.substring(indexes[1] + 1);
listItems[i].innerHTML = innerHTML;
}
}
}
}
Note: The Fuse JS library provides appropriate indexes with the matched characters on search.
You have learned how to achieve fuzzy searches using our Essential JS 2 Angular autocomplete component. You also learned how to integrate third-party libraries like FuseJS with the autocomplete component. For more, check out how fuzzy search works in our sample from GitHub, which is ready to run.
The autocomplete component is also available for the JavaScript, JavaScript (ES5), React, Vue, ASP.NET MVC, and ASP.NET platforms.
To try our autocomplete component’s advanced features, you can explore our documentation. You can download the free trial or play with our online demo. You can even check out the package on GitHub at https://github.com/syncfusion/ej2-angular-ui-components/tree/master/components/dropdowns. If you have any questions or feedback, please let us know in the comments below. You can also contact us through our support forum or Direct-Trac.
If you like this blog post, we think you’ll also like the following free e-books: