code: function(hash) {
  	return function(row) {
  		var testValue = function(value, rowValue) {
  			if (value === '*' || value === '' || !value) {
  				return true;
  			}
  			if (_.isString(rowValue)) {
  				return (rowValue.toLowerCase() === value.toLowerCase());
  			}
  			if (_.isArray(rowValue)) {
  				return (_.contains(_.map(rowValue, function(tag) {
  					return _.isString(tag) && tag.toLowerCase();
  				}), value.toLowerCase()) && true);
  			}
  			return false;				
  		};
  		
  		// _.all makes sure all members of the hash pass
  		return _.all(hash, function(value, key) {
  			var rowValue = row.getAlias(key);
  			
  			if (rowValue) {
  				
  				// if the filter value is an array, then the rowValue must also be an array
  				if (_.isArray(value) && _.isArray(rowValue)) {
  					
  					// expression is true if rowValue intersects completely with requested value.
  					return _.intersection(value, rowValue).length === value.length;
  				}
  
  				// if the value is an array, but not the rowValue, only test the first index;
  				return testValue(_.isArray(value) ? value[0] : value, rowValue)
  			}
  			return false;
  		});
  	};
  }
docs: matcher returns a function based on the provided hash. The new function takes the query hash and returns true only if all property values in the query match each property value in the subject;