UNPKG

pourover

Version:

A library for simple, fast filtering and sorting of large collections in the browser

117 lines (102 loc) 7.06 kB
// # Advanced filters // This example will cover the preset filter types and demonstrate how to create custom filter types. // First, let's use our set up from the [basic example](/pourover/examples/examples_build/basic_pourover_ing.html) var monsters = [{name: "sphinx", mythology: "greek", eyes: 2, sex: "f", hobbies: ["riddles","sitting","being a wonder"]}, {name: "hydra", mythology: "greek", eyes: 18, sex: "m", hobbies: ["coiling","terrorizing","growing"]}, {name: "huldra", mythology: "norse", eyes: 2, sex: "f", hobbies: ["luring","terrorizing"]}, {name: "cyclops", mythology: "greek", eyes: 1, sex: "m", hobbies: ["staring","terrorizing"]}, {name: "fenrir", mythology: "norse", eyes: 2, sex: "m", hobbies: ["growing","god-killing"]}, {name: "medusa", mythology: "greek", eyes: 2, sex: "f", hobbies: ["coiling","staring"]}]; var collection = new PourOver.Collection(monsters); // ### Preset Filters // We have already seen `exactFilter` and `inclusionFilter` in the [basic example](/pourover/examples/examples_build/basic_pourover_ing.html). // *NOTE: `exactFilter`s' and `inclusionFilter`s' names must be identical to the item attribute that they index* // We have seen `manualFilter` in the [advanced views example](/pourover/examples/examples_build/advanced_views.html) var mythology_filter = PourOver.makeExactFilter("mythology", ["greek","norse"]); var gender_filter = PourOver.makeExactFilter("sex", ["m","f"]); var hobbies_filter = PourOver.makeInclusionFilter("hobbies",["riddles", "sitting", "being a wonder", "coiling", "terrorizing", "growing", "luring", "staring", "god-killing"]); var favorites_filter = PourOver.makeManualFilter("favorites"); collection.addFilters([mythology_filter, gender_filter, hobbies_filter, favorites_filter]); // There are two more preset filters which are related to each other: // - `rangeFilter` : Possibilities are inclusive, numerical ranges ([[1,5],[6,10]]), queries can only be one of these exact ranges // and they return all items, whose relevant attribute falls between the extents of the range // - `dvrangeFilter` : Possibilities are an ordered set of values ([1,2,3,4,5,6,7,8,9,10]), queries are any lower and upper bound for the // sorted set of possibilites and they return all items, whose relevant attribute falls between the extents of the range // // Range filters are more efficient than dvrangeFilters for wide domains ([[1,10000],[10001,100000]]). They trade efficiency for flexibility; you can only query by the // preset ranges // // *NOTE: Range filters must be numeric. DvrangeFilters can be any sorted set, it determines a range based on index.* // Here we create two eyes filters. One for each of the range filter types. var eyes_range_filter = PourOver.makeRangeFilter("eyes_range",[[1,1],[2,10],[11,20]],{attr: "eyes"}) var eyes_dvrange_filter = PourOver.makeDVrangeFilter("eyes_dvrange",[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],{attr: "eyes"}) collection.addFilters([eyes_range_filter,eyes_dvrange_filter]) // To get all 2-10 eyed monsters var some_eyed_monster_cids = collection.filters.eyes_range.getFn([2,10]).cids var some_eyed_monsters = collection.get(some_eyed_monster_cids) // To get 1-2 eyed monsters var lte_two_eyed_monster_cids = collection.filters.eyes_dvrange.getFn([1,2]).cids var lte_two_eyed_monsters = collection.get(lte_two_eyed_monster_cids) // While we could get, say, all the 1-10 eyed monsters by `or` combining the 1-1 range and the 2-10 range, // there would be no way to get 1-2 eyed monsters out of the `eyes_range_filter`. Here we see the flexibility of // dvrangeFilters // ### Making new filter types // One of the virtues of PourOver is how easy it is to construct new filter types for either convenience or optimization. // PourOver's default filtering is fast but not optimized for huge numerical domains, like Crossfilter. You could, if you wanted to, // make a custom filter type to integrate Crossfilters continunous numerical filtering power with the patterns and abstractions of // PourOver. // // Here we will create a new, toy filter type, caseInsensitiveFilter. It will work similarly to the exactFilter. However, it will // match queries regardless of their case // To create a new filter type we have to extend `PourOver.Filter` with a `cacheResults`,`addCacheResults` and `getFn` // - `cacheResults` specifies how to take set of items and assign them to possibilities // - `addCacheResults` specifies how to append a set of items to extant possibility caches (This can be the same thing as cacheResults and // only needs to be different for optimization purposes. // - `getFn` specifies how to translate a query into some combination of possibility caches var CaseInsensitiveFilter = PourOver.Filter.extend({ cacheResults: function(items){ var possibilities = this.possibilities, attribute = this.attr; _(items).each(function(i){ var value = i[attribute].toLowerCase(); _(possibilities).each(function(p){ if (p.value.toLowerCase() === value) { p.matching_cids = PourOver.insert_sorted(p.matching_cids,i.cid) } }) }); }, addCacheResults: function(new_items){ this.cacheResults.call(this,new_items); }, getFn: function(query){ var query_lc = query.toLowerCase(), matching_possibility = _(this.possibilities).find(function(p){ var value_lc = p.value.toLowerCase(); return value_lc === query_lc; }); // `getFn` must return a `MatchSet` // `makeQueryMatchSet` is a convenience function for making match sets. return this.makeQueryMatchSet(matching_possibility.matching_cids,query) } }); // Generally, we have to create convenience functions, mapping over input values to create the value objects that the Filter // constructor expects. var makeCaseInsensitiveFilter = function(name,values,attr){ var values = _(values).map(function(i){return {value:i}}), opts = {associated_attrs: [attr], attr: attr}, filter = new CaseInsensitiveFilter(name,values,opts); return filter; } var case_insensitive_name_filter = makeCaseInsensitiveFilter("ci_name_filter",["sphinx","hydra","huldra","cyclops","medusa","fenrir"],"name") collection.addFilters([case_insensitive_name_filter]) var ci_cyclops_match_set = collection.filters.ci_name_filter.getFn("CyClOps") var ci_cyclops = collection.get(ci_cyclops_match_set.cids)