1 // $Id$ 2 3 /** 4 * Baseclass for all facet widgets. 5 * 6 * @class AbstractFacetWidget 7 * @augments AjaxSolr.AbstractWidget 8 */ 9 AjaxSolr.AbstractFacetWidget = AjaxSolr.AbstractWidget.extend( 10 /** @lends AjaxSolr.AbstractFacetWidget.prototype */ 11 { 12 /** 13 * The field to facet on. 14 * 15 * @field 16 * @public 17 * @type String 18 */ 19 field: null, 20 21 /** 22 * Set to <tt>false</tt> to force a single "fq" parameter for this widget. 23 * 24 * @field 25 * @public 26 * @type Boolean 27 * @default true 28 */ 29 multivalue: true, 30 31 init: function () { 32 this.initStore(); 33 }, 34 35 /** 36 * Add facet parameters to the parameter store. 37 */ 38 initStore: function () { 39 /* http://wiki.apache.org/solr/SimpleFacetParameters */ 40 var parameters = [ 41 'facet.prefix', 42 'facet.sort', 43 'facet.limit', 44 'facet.offset', 45 'facet.mincount', 46 'facet.missing', 47 'facet.method', 48 'facet.enum.cache.minDf' 49 ]; 50 51 this.manager.store.addByValue('facet', true); 52 53 // Set facet.field, facet.date or facet.range to truthy values to add 54 // related per-field parameters to the parameter store. 55 if (this['facet.field'] !== undefined) { 56 this.manager.store.addByValue('facet.field', this.field); 57 } 58 else if (this['facet.date'] !== undefined) { 59 this.manager.store.addByValue('facet.date', this.field); 60 parameters = parameters.concat([ 61 'facet.date.start', 62 'facet.date.end', 63 'facet.date.gap', 64 'facet.date.hardend', 65 'facet.date.other', 66 'facet.date.include' 67 ]); 68 } 69 else if (this['facet.range'] !== undefined) { 70 this.manager.store.addByValue('facet.range', this.field); 71 parameters = parameters.concat([ 72 'facet.range.start', 73 'facet.range.end', 74 'facet.range.gap', 75 'facet.range.hardend', 76 'facet.range.other', 77 'facet.range.include' 78 ]); 79 } 80 81 for (var i = 0, l = parameters.length; i < l; i++) { 82 if (this[parameters[i]] !== undefined) { 83 this.manager.store.addByValue('f.' + this.field + '.' + parameters[i], this[parameters[i]]); 84 } 85 } 86 }, 87 88 /** 89 * @returns {Boolean} Whether any filter queries have been set using this 90 * widget's facet field. 91 */ 92 isEmpty: function () { 93 return !this.manager.store.find('fq', new RegExp('^-?' + this.field + ':')); 94 }, 95 96 /** 97 * Sets the filter query. 98 * 99 * @returns {Boolean} Whether the selection changed. 100 */ 101 set: function (value) { 102 return this.changeSelection(function () { 103 var a = this.manager.store.removeByValue('fq', new RegExp('^-?' + this.field + ':')), 104 b = this.manager.store.addByValue('fq', this.fq(value)); 105 return a || b; 106 }); 107 }, 108 109 /** 110 * Adds a filter query. 111 * 112 * @returns {Boolean} Whether a filter query was added. 113 */ 114 add: function (value) { 115 return this.changeSelection(function () { 116 return this.manager.store.addByValue('fq', this.fq(value)); 117 }); 118 }, 119 120 /** 121 * Removes a filter query. 122 * 123 * @returns {Boolean} Whether a filter query was removed. 124 */ 125 remove: function (value) { 126 return this.changeSelection(function () { 127 return this.manager.store.removeByValue('fq', this.fq(value)); 128 }); 129 }, 130 131 /** 132 * Removes all filter queries using the widget's facet field. 133 * 134 * @returns {Boolean} Whether a filter query was removed. 135 */ 136 clear: function () { 137 return this.changeSelection(function () { 138 return this.manager.store.removeByValue('fq', new RegExp('^-?' + this.field + ':')); 139 }); 140 }, 141 142 /** 143 * Helper for selection functions. 144 * 145 * @param {Function} Selection function to call. 146 * @returns {Boolean} Whether the selection changed. 147 */ 148 changeSelection: function (func) { 149 changed = func.apply(this); 150 if (changed) { 151 this.afterChangeSelection(); 152 } 153 return changed; 154 }, 155 156 /** 157 * An abstract hook for child implementations. 158 * 159 * <p>This method is executed after the filter queries change.</p> 160 */ 161 afterChangeSelection: function () {}, 162 163 /** 164 * One of "facet.field", "facet.date" or "facet.range" must be set on the 165 * widget in order to determine where the facet counts are stored. 166 * 167 * @returns {Array} An array of objects with the properties <tt>facet</tt> and 168 * <tt>count</tt>, e.g <tt>{ facet: 'facet', count: 1 }</tt>. 169 */ 170 getFacetCounts: function () { 171 var property; 172 if (this['facet.field'] !== undefined) { 173 property = 'facet_fields'; 174 } 175 else if (this['facet.date'] !== undefined) { 176 property = 'facet_dates'; 177 } 178 else if (this['facet.range'] !== undefined) { 179 property = 'facet_ranges'; 180 } 181 if (property !== undefined) { 182 switch (this.manager.store.get('json.nl').val()) { 183 case 'map': 184 return this.getFacetCountsMap(property); 185 case 'arrarr': 186 return this.getFacetCountsArrarr(property); 187 default: 188 return this.getFacetCountsFlat(property); 189 } 190 } 191 throw 'Cannot get facet counts unless one of the following properties is set to "true" on widget "' + this.id + '": "facet.field", "facet.date", or "facet.range".'; 192 }, 193 194 /** 195 * Used if the facet counts are represented as a JSON object. 196 * 197 * @param {String} property "facet_fields", "facet_dates", or "facet_ranges". 198 * @returns {Array} An array of objects with the properties <tt>facet</tt> and 199 * <tt>count</tt>, e.g <tt>{ facet: 'facet', count: 1 }</tt>. 200 */ 201 getFacetCountsMap: function (property) { 202 var counts = []; 203 for (var facet in this.manager.response.facet_counts[property][this.field]) { 204 counts.push({ 205 facet: facet, 206 count: parseInt(this.manager.response.facet_counts[property][this.field][facet]) 207 }); 208 } 209 return counts; 210 }, 211 212 /** 213 * Used if the facet counts are represented as an array of two-element arrays. 214 * 215 * @param {String} property "facet_fields", "facet_dates", or "facet_ranges". 216 * @returns {Array} An array of objects with the properties <tt>facet</tt> and 217 * <tt>count</tt>, e.g <tt>{ facet: 'facet', count: 1 }</tt>. 218 */ 219 getFacetCountsArrarr: function (property) { 220 var counts = []; 221 for (var i = 0, l = this.manager.response.facet_counts[property][this.field].length; i < l; i++) { 222 counts.push({ 223 facet: this.manager.response.facet_counts[property][this.field][i][0], 224 count: parseInt(this.manager.response.facet_counts[property][this.field][i][1]) 225 }); 226 } 227 return counts; 228 }, 229 230 /** 231 * Used if the facet counts are represented as a flat array. 232 * 233 * @param {String} property "facet_fields", "facet_dates", or "facet_ranges". 234 * @returns {Array} An array of objects with the properties <tt>facet</tt> and 235 * <tt>count</tt>, e.g <tt>{ facet: 'facet', count: 1 }</tt>. 236 */ 237 getFacetCountsFlat: function (property) { 238 var counts = []; 239 for (var i = 0, l = this.manager.response.facet_counts[property][this.field].length; i < l; i += 2) { 240 counts.push({ 241 facet: this.manager.response.facet_counts[property][this.field][i], 242 count: parseInt(this.manager.response.facet_counts[property][this.field][i+1]) 243 }); 244 } 245 return counts; 246 }, 247 248 /** 249 * @param {String} value The value. 250 * @returns {Function} Sends a request to Solr if it successfully adds a 251 * filter query with the given value. 252 */ 253 clickHandler: function (value) { 254 var self = this, meth = this.multivalue ? 'add' : 'set'; 255 return function () { 256 if (self[meth].call(self, value)) { 257 self.manager.doRequest(0); 258 } 259 return false; 260 } 261 }, 262 263 /** 264 * @param {String} value The value. 265 * @returns {Function} Sends a request to Solr if it successfully removes a 266 * filter query with the given value. 267 */ 268 unclickHandler: function (value) { 269 var self = this; 270 return function () { 271 if (self.remove(value)) { 272 self.manager.doRequest(0); 273 } 274 return false; 275 } 276 }, 277 278 /** 279 * @param {String} value The facet value. 280 * @param {Boolean} exclude Whether to exclude this fq parameter value. 281 * @returns {String} An fq parameter value. 282 */ 283 fq: function (value, exclude) { 284 return (exclude ? '-' : '') + this.field + ':' + AjaxSolr.Parameter.escapeValue(value); 285 } 286 }); 287