Search for Substring in several fields with MongoDB and Mongoose Search for Substring in several fields with MongoDB and Mongoose mongoose mongoose

Search for Substring in several fields with MongoDB and Mongoose


Your use case is a good fit for text search.

Define a text index on your schema over the searchable fields:

trackingSchema.index({    tracking_number: 'text',    zip_code: 'text',    'delivery_info.recipient': 'text',    'delivery_info.street': 'text',    'delivery_info.city': 'text'}, {name: 'search'});

Join your search terms into a single string and execute the search using the $text query operator:

var search = ['15232', 'julian'];Test.find({$text: {$search: search.join(' ')}}, function(err, docs) {...});

Even though this passes all your search values as a single string, this still performs a logical OR search of the values.


Why just dont try

var trackingSchema = mongoose.Schema({  tracking_number: String,  zip_code: String,  courier: String,  user_id: Number,  created: { type: Date, default: Date.now },   international_shipment: { type: Boolean, default: false },   delivery_info: {     recipient: String,     street: String,     city: String  }});var Tracking = mongoose.model('Tracking', trackingSchema );var search = [ "word1", "word2", ...]var results = []for(var i=0; i<search.length; i++){    Tracking.find({$or : [      { tracking_number : search[i]},       {zip_code: search[i]},       {courier: search[i]},       {delivery_info.recipient: search[i]},       {delivery_info.street: search[i]},       {delivery_info.city: search[i]}]     }).map(function(tracking){    //it will push every unique result to variable results    if(results.indexOf(tracking)<0) results.push(tracking);    });


Okay, I came up with this.

My schema now has an extra field search with an array of all my searchable fields:

var trackingSchema = mongoose.Schema({    ...    search: [String]});

With a pre-save hook, I populate this field:

trackingSchema.pre('save', function(next) {  this.search = [ this.tracking_number ];  var searchIfAvailable = [    this.zip_code,    this.delivery_info.recipient,    this.delivery_info.street,    this.delivery_info.city  ];  for (var i = 0; i < searchIfAvailable.length; i++) {    if (!validator.isNull(searchIfAvailable[i])) {      this.search.push(searchIfAvailable[i].toLowerCase());    }  }  next();});

In the hope of improving performance, I also index that field (also the user_id as I limit search results by that):

trackingSchema.index({ search: 1 });trackingSchema.index({ user_id: 1 });

Now, when searching I first list all substrings I want to look for in an array:

var andArray = [];var searchTerms = searchRequest.split(" ");searchTerms.forEach(function(searchTerm) {  andArray.push({    search: { $regex: searchTerm, $options: 'i'    }  });});

I use this array in my find() and chain it with an $and:

  Tracking.    find({ $and: andArray }).      where('user_id').equals(userId).      limit(pageSize).      skip(pageSize * page).      exec(function(err, docs) {         // hooray!      });

This works.