问题
I have a backbone.js collection where I need to do a fulltextsearch on. The tools I have at hand are the following:
Backbone.js, underscore.js, jQuery
For those not familiar with backbone:
A backbones collection is just an object. Inside the collection there is an array with models. Each model has an array with attributes. I have to search each attribute for a string.
The code I am using for this is:
query = 'some user input';
query = $.trim(query);
query = query.replace(/ /gi, '|');
var pattern = new RegExp(query, "i");
// this.collection.forEach is the same as _.each
// only it get's the models from the collection
this.collection.forEach(function(model) {
var check = true;
_.each(model.attributes, function(attr){
if(pattern.test(attr) && check){
// Do something with the matched item
check = false;
}
}, this);
}, this);
Maybe one of the tools I am using has a better way of dealing with this ?
回答1:
Backbone extends a lot of the underscore methods into the Collection
class so you can get rid of some of that stuff. Really you probably want to impliment this on the collection itself as a method, then I would probably look at those keys using a good old fashioned for
loop, especially if I wanted to break out of it.
// in Backbone.Collection.extend
search: function( query, callback ){
var pattern = new RegExp( $.trim( query ).replace( / /gi, '|' ), "i");
var collection = this;
collection.each(function(model) {
for( k in model.attributes ){
if( model.attributes.hasOwnProperty(k) && pattern.test(model.attributes[k]) ){
callback.call( collection, model, k );
break; // ends the for loop.
}
}
});
}
// later
collection.search('foo', function( model, attr ){
console.log('found foo in '+model.cid+' attribute '+attr);
});
That said, this would only ever return the first match from the collection. You may prefer an implementation that returns an array of results as [model, attribute] pairs.
// in Backbone.Collection.extend
search: function( query, callback ){
var matches = [];
var pattern = new RegExp( $.trim( query ).replace( / /gi, '|' ), "i");
this.each(function(model) {
for( k in model.attributes ){
if( model.attributes.hasOwnProperty(k) && pattern.test(model.attributes[k]) ){
matches.push([model, k]);
}
}
});
callback.call( this, matches );
}
// later
collection.search('foo', function( matches ){
_.each(matches, function(match){
console.log('found foo in '+match[0].cid+' attribute '+match[1]);
});
});
Or, if you wanted an array of models which matched but don't care which attribute matched you can use filter
// in Backbone.Collection.extend
search: function( query, callback ){
var pattern = new RegExp( $.trim( query ).replace( / /gi, '|' ), "i");
callback.call( this, this.filter(function( model ){
for( k in model.attributes ){
if( model.attributes.hasOwnProperty(k) && pattern.test(k) )
return true;
}
}));
}
// later
collection.search('foo', function( matches ){
_.each(matches, function(match){
console.log('found foo in '+match[0].cid+' somewhere');
});
});
回答2:
Your inner each
is kludging a short-circuit so you could switch to _.any() instead of your _.each() and a flag combination; any
stops iterating as soon as the callback function returns true
and also delegates to the native some
method if available.
this.collection.each(function(model) {
_(model.attributes).any(function(attr, key) {
if(!pattern.test(attr))
return false;
// Do something with the matched item...
return true;
});
});
I also dropped the context this
arguments since you're not using this
anywhere, you can put them back if the "Do something" needs them.
You could look into stemming and a reverse index of the collection if a simple regex search isn't good enough.
来源:https://stackoverflow.com/questions/10468039/searching-for-text-inside-nested-object-backbone-js-collection-as-example