Use Fuzzy Search in MongoDb

Table of Contents

Special thanks to Mayanwolfe for the walkthrough!

Configure MongoDb to enable indexing

  1. Go to MongoDb > Target Collectio > Search Indexes > Create Index. Choose JSON Editor.

  2. Select the correct database and collection. Paste the following JSON. Be sure that the correct field is used in the JSON. Also, make sure you input a descriptive index name.

    {
    	"mappings": {
    		"dynamic": false,
    		"fields": {
    			"field-name": [
    				{
    					"foldDiacritics": false,
    					"maxGrams": 7,
    					"minGrams": 3,
    					"tokenization": "edgeGram",
    					"type": "autocomplete"
    				}
    			]
    		}
    	}
    }
    

Configure MongoDB to also load Object ID

Use destructuring to use both MongoClient and ObjectID when loading mongodb.

const { MongoClient, ObjectId } = require('mongodb');

The use of this search request is to bring back autocomplete matches (like the dropdown that appears when typing).

// Get autocomplete results
app.get('/search', async (req, res) => {
	try {
		let result = await collection
			.aggregate([
				{
					$search: {
						index: 'index-name',
						autocomplete: {
							query: `${req.query.query}`,
							path: 'field-name',
							fuzzy: {
								maxEdits: 2, // num of characters allowed to be wrong
								prefixLength: 2, // minimum num of characters to allow autocomplete
							},
						},
					},
				},
			])
			.toArray();
		res.send(result);
	} catch (err) {
		res.status(500).send({ message: err.message });
	}
});

Setup a GET request for getting item information

This is used when a selection is made from fuzzy results.

app.get('/item/:id', async (req, res) => {
	try {
		const result = await collection.findOne({ _id: ObjectId(req.params.id) });
		res.json(result);
	} catch {
		res.status(500).send({ message: err.message });
	}
});
<div>
	<input id="search" type="text" />
</div>

Setup the client side JS using JQuery

Load JQuery in the HTML head.

<link
	rel="stylesheet"
	href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"
/>
<script src="//code.jquery.com/jquery-1.12.4.js"></script>
<script src="//code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

On your JS file:

$(document).ready(function () {
	$('#input-name').autocomplete({
		// Define the source of the autocomplete results
		source: async function (req, res) {
			let data = await (await fetch(`/search?query=${req.term}`)).json();
			let results = data.map((result) => {
				return {
					label: result['field-name'],
					value: result['field-name'],
					id: result._id,
				};
			});
			res(results);
		},
		// Minimum Length
		minLength: 2,
		// Define the action when a result is selected
		select: function (_, ui) {
			// Somehow, async/await does not work here.
			const id = ui.item.id;
			fetch(`/item/${id}`)
				.then((result) => result.json())
				.then((result) => {
					// Insert here what your want to do with the result
					console.log(result);
				});
		},
	});
});