34

I have managed to set up a search feature in my mongodb app. See the code below. This works very well however it only returns exact results. How would I change my code to make it accept more "fuzzy" search results? Thanks!

router.get("/", function(req, res){
    if (req.query.search) {
       Jobs.find({"name": req.query.search}, function(err, foundjobs){
       if(err){
           console.log(err);
       } else {
          res.render("jobs/index",{jobs:foundjobs});
       }
    }); 
    }

  Jobs.find({}, function(err, allJobs){
       if(err){
           console.log(err);
       } else {
          res.render("jobs/index",{jobs:allJobs});
       }
    });
});
manniL
  • 7,157
  • 7
  • 46
  • 72
AndrewLeonardi
  • 3,351
  • 9
  • 47
  • 100
  • you can use a mongoose FullText library for most accurate searches https://www.npmjs.com/package/mongoose-fulltext – Cesar Jr Rodriguez Jul 18 '16 at 02:02
  • 1
    in case anybody's interested, i've written an article on how to do fuzzy searching in mongodb with c# [here](https://dev.to/djnitehawk/mongodb-fuzzy-text-search-with-c-the-easy-way-3l8j) – Dĵ ΝιΓΞΗΛψΚ Jan 15 '20 at 00:04

5 Answers5

44

I believe that to do "fuzzy" search you will need to use regex. This should accomplish what you're looking for (escapeRegex function source here):

function escapeRegex(text) {
    return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
};

router.get("/", function(req, res) {
    if (req.query.search) {
       const regex = new RegExp(escapeRegex(req.query.search), 'gi');
       Jobs.find({ "name": regex }, function(err, foundjobs) {
           if(err) {
               console.log(err);
           } else {
              res.render("jobs/index", { jobs: foundjobs });
           }
       }); 
    }
}

That being said, your application can experience performance issues when querying mongo by regex. Using a library like search-index for search could help optimize your application's performance, with the added benefit of searching word stems (like returning "found" from "find").


UPDATE: My original answer included a simple regular exression that would leave your application vulnerable to a regex DDoS attack. I've updated with a "safe" escaped regex.

the holla
  • 816
  • 11
  • 17
21

I know this is an old thread, but I made a plugin which is based on this article.

mongoose-fuzzy-searching

(It uses $text query operator instead of $regex, for faster results)

The below example just searches for events based on title and city

const mongoose_fuzzy_searching = require('mongoose-fuzzy-searching');

const schema = {
    title: {
        type: String,
        trim: true,
        required: true,
    },
    description: {
        type: String,
        trim: true,
    },
    city: {
        type: String,
    },
    address: {
        type: String,
    }
};

const EventsSchema = mongoose.Schema(schema);
EventsSchema.plugin(mongoose_fuzzy_searching, {fields: ['title', 'city']});

const Events = mongoose.model('Events', EventsSchema);

Events.fuzzySearch('Nodejs meetup').then(console.log).catch(console.error);
Vasileios Pallas
  • 4,801
  • 4
  • 33
  • 50
  • @Vassilis Pallas Does this use the partial index or not. I need to remove the documents from the index when the condition does not met. For eg I only want to add in the index when user_status is ACTIVE only – aabiskar Jan 17 '21 at 12:08
  • @VassilisPallas If I delete the ngram from the particular document. Will it get removed from the index as well ? – aabiskar Jan 25 '21 at 10:05
  • To delete the ngrams from documents you can use [this logic](https://www.npmjs.com/package/mongoose-fuzzy-searching#delete-old-ngrams-from-all-documents). Now regarding the indexes, if you removed the plugin from the Collection, you will need also to delete them from the database as well – Vasileios Pallas Jan 25 '21 at 12:25
3

You can use the Mongo DB Atlas feature where you can search your text based on different Analyzers that MongoDB provides. And you can then do a search like this: Without the fuzzy object, it would do a full-text-match search.

$search:{
 {
  index: 'analyzer_name_created_from_atlas_search',
  text: {
    query: 'Russ has denied involvement in the case',
    path: 'sentence',
    fuzzy:{
      maxEdits: 2
    }
  }
 }
}
Shreyesh Desai
  • 569
  • 4
  • 19
1

This is an updated answer that comes on the heels of a new product in MongoDB that was not released at the time of the original post. MongoDB now has a feature where you can deploy Apache Lucene indexes to get robust fuzzy search functionality, and other features like synonyms, highlighting and autocomplete.

Below is the syntax as posted here in another answer I had.

{
  $search: {
    "index": <index name>, // optional, defaults to "default"
    "text": {
      "query": "queryText",
      "path": "<fields-to-search>",
      "fuzzy": {
         "maxEdits": 2
       }
    }
  }
}
Nice-Guy
  • 1,457
  • 11
  • 20
0

if you are using atlas go to the index tab create an index for your database then you can use aggregate for a more powerful fuzzy search.

Jobs.aggregate([
    {
        $search: {
            "index": "default",
            "text": {
                "path": "name",
                "query": req.query.search,
            }
        }
    },
], (err: any, data: any) => {
    if (err) {
        res.status(500).send(err);
    }
    else {
        res.status(200).send(data);
    }
}).limit(20)
raphael
  • 21
  • 2