Search code examples
javascriptjqueryhtmlknockout.jsko.observablearray

Custom filtering knockout js observableArray


I have an observableArray with testsuites that I want to filter according to text in a search field. The filtering updates after every key stroke, so I am searching for the most efficient method.

Check out this JS-fiddle for a simplified version of my problem:

http://jsfiddle.net/LkqTU/23180/

Below, you can see a snippet from the fiddle. As of now the filtering takes the entire text in the search field and checks the "Name" field of each testsuite against it.

What I want is to divide the filter text into words and search every field in testsuite for every word in the search field.

I you for example write "user lol" in the search field, I want it to only return the testsuite that contains these words in any field (here two of the testsuites has "user" in the name and one has "lol" in the description).

self.filteredTestsuites = ko.computed(function () {

    // If many white spaces in a row, replace with only one white space
    fText = self.filterText().replace(/\s+/g, ' ');

    // If there is anything in the search box, filter for this
    // As of now this does not divide the filterText and only searches the Name field
    var filteredCollection = ko.utils.arrayFilter(self.testsuites(), function(test) {
        if(fText.length)
            return ( test.name.toUpperCase().indexOf(fText.toUpperCase()) >= 0);
        else
            return 1;
    });

    return filteredCollection;
}, self);

My question is how can I do the searching most efficient? A possible solution is that for every word in the search field, I search every field in the current testsuite. However I would like a more generic solution, where I dont have to specify the fields (e.g. name, description, etc), and I am also unsure about the efficiency of this method.

Suggestions?


Solution

  • A simple solution that ive used before is to merge all searchable keys/text keys into one long searchable text and use that to do all your searching.

    A bit simplified version is below.

    http://jsfiddle.net/rainerpl/v2krqev5/2/

    function ViewModel(){
        var self = this, x, i, suits;
    
        self.filterText = ko.observable(""); // Text from search field
    
        // Collection of testsuites
        self.testsuites = ko.observableArray([
            { name: "Register User", description: "Bla bla bla", etc: "Many more fields..." },
            { name: "Delete User", description: "some description", etc: "Many more fields" },
            { name: "Send Money", description: "na-na-na bat man", etc: "Many more fields" }
        ]);
        suits = self.testsuites();
    
        for ( i = 0; i < suits.length; i++) {
            suits[i]["search_content"] = ">";
            for ( x in suits[i] ) {
                if ( !suits[i].hasOwnProperty(x) || x == "search_content" || typeof suits[i][x] !== "string") {continue;}
                suits[i]["search_content"] += suits[i][x].toUpperCase();
            }
        }
        // Collection of testsuites after going through search filter
        self.filteredTestsuites = ko.computed(function () {
            var reg;
            // If many white spaces in a row, replace with only one white space
            fText = self.filterText().replace(/\s+/gi, '|');
            fText = fText.replace(/\|\s*$/gi, '');
            console.log("regex:", fText);
            reg = new RegExp(fText, "gi");
            // If there is anything in the search box, filter for this
            // As of now this does not divide the filterText and only searches the Name field
            var filteredCollection = ko.utils.arrayFilter(self.testsuites(), function(test) {
                if(fText.length)
                    return test.search_content.match(reg);
                else
                    return 1;
            });
    
            return filteredCollection;
        }, self);
    
    }
    
    $(document).ready( function(){
        var vm = new ViewModel();
        ko.applyBindings(vm);
    } );