2
votes

I have a search function as part of a backbone app that I'm writing. I'd like some help expanding it out. Ideally I'd like it to be able to

  1. search more than one category in my model and
  2. not have to be such an exact match (perhaps using RegEx?)

Here's my code:

search: function(e) {
           var search = this.$('.search').val();  //pulling in from the search bar
           results = namesCollection.filter(function(item) { return item.get('First_Name') == (search);}); //returns a filtered collection that matches (exactly) the search results
           filteredCollection.reset(results); //this just resets the current view
}

The code works, but right now it only returns results for an exact match on First_Name. Is there any way to have it match both First_Name and Last_Name? Also I'm trying to find a way to have it return for incomplete searches. So that entering 'Joh' in the search bar, would return a First_Name of John and Johanna.

Here's what my data looks like (simplified, of course):

var data = [
            {
             "First_Name": "John",
             "Last_Name": Smith
            },
            {
             "First_Name": "Johanna",
             "Last_Name": "Greene"
            }
          ];

And namesCollection refers to a backbone collection of all the names in my data set. Thanks!

3
The filter function should return a true value for each item you want to include. For multiple conditions, make the function more than one line with if statements, or use the || operator. For example, return item.get('First_Name') == search || item.get('Last_Name') == searchquietmint

3 Answers

1
votes

As for the searching first and last names why not just run another filter on your filtered results? You could split the search by spaces.

results = namesCollection.filter(function(item) { return item.get('First_Name') == (search.split(' ')[0]);});
if(search.split(' ').length > 1){
    results = namesCollection.filter(function(item) { return item.get('Last_Name') == (search.split(' ')[1]);});
}

As for finding a similar string that might work with mispelled words you are probably going to need something like a Levenshtein function which allows you to calculate the similarity between to strings. You could then determine based on your own threshold whether or not to consider it a match. Here is a js library for it

If you want to do partial matches as well assuming they are correctly spelled you could first search for exact matches and if one is found use it (let's say for the first name which means there is only one element after split). If an exact match is not found then try a partial match with an indexOf(). If you get a match on the first name and there is also a last name (meaning there are more than one elements in the split) then attempt an exact match of the last name. If that last name match fails then do the partial match search.

1
votes

I would leverage lodash/underscore. Makes life easy. You can probably deconstruct to native js, but I find this easier.

#!/usr/bin/env node

_ = require('lodash');

var search = 'john';

var data = [
    {
        "First_Name": "John",
        "Last_Name": "Smith"
    },
    {
        "First_Name": "Jessica",
        "Last_Name": "Greene"
    },
    {
        "First_Name": "Sam",
        "Last_Name": "Johnson"
    }
];

var re = new RegExp(search, 'i');

data = data.filter(function(model){
    var matched = false;

    _.each(_.values(model), function(val){  
        if ( re.test(val) ) {
            matched = true;
        }
    });

    return matched;
});

console.log(data);

Outputs:

[ { First_Name: 'John', Last_Name: 'Smith' },
  { First_Name: 'Sam', Last_Name: 'Johnson' } ]

To work off a backbone collection you need to do this.collection.filter and also use model.attributes for the _.values(model.attributes, cb). assuming its a flat object.

This would of course match any value in the object, and I did it in node, but would easily run in backbone/browser.

0
votes

I am not too familiar with Backbone, but what about changing the exact matching to a .indexOf or .contains? That way it can filter while you type in the keyword and so on keyup you can move to a more exact matching, the more keywords you give..

This is for instance the system that shuffle.js uses. Have a look at how that search works, might be what you are searching for i think.

search: function(e) {
       var search = this.$('.search').val();  //pulling in from the search bar
       results = namesCollection.filter(function(item) { return item.get('First_Name').indexOf( search ) !== -1 }); //returns a filtered collection that matches (not exactly) the search results
       filteredCollection.reset(results); //this just resets the current view
}