1 // $Id$
  2 
  3 /**
  4  * The ParameterStore, as its name suggests, stores Solr parameters. Widgets
  5  * expose some of these parameters to the user. Whenever the user changes the
  6  * values of these parameters, the state of the application changes. In order to
  7  * allow the user to move back and forth between these states with the browser's
  8  * Back and Forward buttons, and to bookmark these states, each state needs to
  9  * be stored. The easiest method is to store the exposed parameters in the URL
 10  * hash (see the <tt>ParameterHashStore</tt> class). However, you may implement
 11  * your own storage method by extending this class.
 12  *
 13  * <p>For a list of possible parameters, please consult the links below.</p>
 14  *
 15  * @see http://wiki.apache.org/solr/CoreQueryParameters
 16  * @see http://wiki.apache.org/solr/CommonQueryParameters
 17  * @see http://wiki.apache.org/solr/SimpleFacetParameters
 18  * @see http://wiki.apache.org/solr/HighlightingParameters
 19  * @see http://wiki.apache.org/solr/MoreLikeThis
 20  * @see http://wiki.apache.org/solr/SpellCheckComponent
 21  * @see http://wiki.apache.org/solr/StatsComponent
 22  * @see http://wiki.apache.org/solr/TermsComponent
 23  * @see http://wiki.apache.org/solr/TermVectorComponent
 24  * @see http://wiki.apache.org/solr/LocalParams
 25  *
 26  * @param properties A map of fields to set. Refer to the list of public fields.
 27  * @class ParameterStore
 28  */
 29 AjaxSolr.ParameterStore = AjaxSolr.Class.extend(
 30   /** @lends AjaxSolr.ParameterStore.prototype */
 31   {
 32   /**
 33    * The names of the exposed parameters. Any parameters that your widgets
 34    * expose to the user, directly or indirectly, should be listed here.
 35    *
 36    * @field
 37    * @public
 38    * @type String[]
 39    * @default []
 40    */
 41   exposed: [],
 42 
 43   /**
 44    * The Solr parameters.
 45    *
 46    * @field
 47    * @private
 48    * @type Object
 49    * @default {}
 50    */
 51   params: {},
 52 
 53   /**
 54    * A reference to the parameter store's manager. For internal use only.
 55    *
 56    * @field
 57    * @private
 58    * @type AjaxSolr.AbstractManager
 59    */
 60   manager: null,
 61 
 62   /**
 63    * An abstract hook for child implementations.
 64    *
 65    * <p>This method should do any necessary one-time initializations.</p>
 66    */
 67   init: function () {},
 68 
 69   /**
 70    * Some Solr parameters may be specified multiple times. It is easiest to
 71    * hard-code a list of such parameters. You may change the list by passing
 72    * <code>{ multiple: /pattern/ }</code> as an argument to the constructor of
 73    * this class or one of its children, e.g.:
 74    *
 75    * <p><code>new ParameterStore({ multiple: /pattern/ })</code>
 76    *
 77    * @param {String} name The name of the parameter.
 78    * @returns {Boolean} Whether the parameter may be specified multiple times.
 79    * @see http://lucene.apache.org/solr/api/org/apache/solr/handler/DisMaxRequestHandler.html
 80    */
 81   isMultiple: function (name) {
 82     return name.match(/^(?:bf|bq|facet\.date|facet\.date\.other|facet\.date\.include|facet\.field|facet\.pivot|facet\.range|facet\.range\.other|facet\.range\.include|facet\.query|fq|group\.field|group\.func|group\.query|pf|qf)$/);
 83   },
 84 
 85   /**
 86    * Returns a parameter. If the parameter doesn't exist, creates it.
 87    *
 88    * @param {String} name The name of the parameter.
 89    * @returns {AjaxSolr.Parameter|AjaxSolr.Parameter[]} The parameter.
 90    */
 91   get: function (name) {
 92     if (this.params[name] === undefined) {
 93       var param = new AjaxSolr.Parameter({ name: name });
 94       if (this.isMultiple(name)) {
 95         this.params[name] = [ param ];
 96       }
 97       else {
 98         this.params[name] = param;
 99       }
100     }
101     return this.params[name];
102   },
103 
104   /**
105    * If the parameter may be specified multiple times, returns the values of
106    * all identically-named parameters. If the parameter may be specified only
107    * once, returns the value of that parameter.
108    *
109    * @param {String} name The name of the parameter.
110    * @returns {String[]|Number[]} The value(s) of the parameter.
111    */
112   values: function (name) {
113     if (this.params[name] !== undefined) {
114       if (this.isMultiple(name)) {
115         var values = [];
116         for (var i = 0, l = this.params[name].length; i < l; i++) {
117           values.push(this.params[name][i].val());
118         }
119         return values;
120       }
121       else {
122         return [ this.params[name].val() ];
123       }
124     }
125     return [];
126   },
127 
128   /**
129    * If the parameter may be specified multiple times, adds the given parameter
130    * to the list of identically-named parameters, unless one already exists with
131    * the same value. If it may be specified only once, replaces the parameter.
132    *
133    * @param {String} name The name of the parameter.
134    * @param {AjaxSolr.Parameter} [param] The parameter.
135    * @returns {AjaxSolr.Parameter|Boolean} The parameter, or false.
136    */
137   add: function (name, param) {
138     if (param === undefined) {
139       param = new AjaxSolr.Parameter({ name: name });
140     }
141     if (this.isMultiple(name)) {
142       if (this.params[name] === undefined) {
143         this.params[name] = [ param ];
144       }
145       else {
146         if (AjaxSolr.inArray(param.val(), this.values(name)) == -1) {
147           this.params[name].push(param);
148         }
149         else {
150           return false;
151         }
152       }
153     }
154     else {
155       this.params[name] = param;
156     }
157     return param;
158   },
159 
160   /**
161    * Deletes a parameter.
162    *
163    * @param {String} name The name of the parameter.
164    * @param {Number} [index] The index of the parameter.
165    */
166   remove: function (name, index) {
167     if (index === undefined) {
168       delete this.params[name];
169     }
170     else {
171       this.params[name].splice(index, 1);
172       if (this.params[name].length == 0) {
173         delete this.params[name];
174       }
175     }
176   },
177 
178   /**
179    * Finds all parameters with matching values.
180    *
181    * @param {String} name The name of the parameter.
182    * @param {String|Number|String[]|Number[]|RegExp} value The value.
183    * @returns {String|Number[]} The indices of the parameters found.
184    */
185   find: function (name, value) {
186     if (this.params[name] !== undefined) {
187       if (this.isMultiple(name)) {
188         var indices = [];
189         for (var i = 0, l = this.params[name].length; i < l; i++) {
190           if (AjaxSolr.equals(this.params[name][i].val(), value)) {
191             indices.push(i);
192           }
193         }
194         return indices.length ? indices : false;
195       }
196       else {
197         if (AjaxSolr.equals(this.params[name].val(), value)) {
198           return name;
199         }
200       }
201     }
202     return false;
203   },
204 
205   /**
206    * If the parameter may be specified multiple times, creates a parameter using
207    * the given name and value, and adds it to the list of identically-named
208    * parameters, unless one already exists with the same value. If it may be
209    * specified only once, replaces the parameter.
210    *
211    * @param {String} name The name of the parameter.
212    * @param {String|Number|String[]|Number[]} value The value.
213    * @returns {AjaxSolr.Parameter|Boolean} The parameter, or false.
214    */
215   addByValue: function (name, value) {
216     if (this.isMultiple(name) && AjaxSolr.isArray(value)) {
217       var ret = [];
218       for (var i = 0, l = value.length; i < l; i++) {
219         ret.push(this.add(name, new AjaxSolr.Parameter({ name: name, value: value[i] })));
220       }
221       return ret;
222     }
223     else {
224       return this.add(name, new AjaxSolr.Parameter({ name: name, value: value }))
225     }
226   },
227 
228   /**
229    * Deletes any parameter with a matching value.
230    *
231    * @param {String} name The name of the parameter.
232    * @param {String|Number|String[]|Number[]|RegExp} value The value.
233    * @returns {String|Number[]} The indices deleted.
234    */
235   removeByValue: function (name, value) {
236     var indices = this.find(name, value);
237     if (indices) {
238       if (AjaxSolr.isArray(indices)) {
239         for (var i = indices.length - 1; i >= 0; i--) {
240           this.remove(name, indices[i]);
241         }
242       }
243       else {
244         this.remove(indices);
245       }
246     }
247     return indices;
248   },
249 
250   /**
251    * Returns the Solr parameters as a query string.
252    *
253    * <p>IE6 calls the default toString() if you write <tt>store.toString()
254    * </tt>. So, we need to choose another name for toString().</p>
255    */
256   string: function () {
257     var params = [];
258     for (var name in this.params) {
259       if (this.isMultiple(name)) {
260         for (var i = 0, l = this.params[name].length; i < l; i++) {
261           params.push(this.params[name][i].string());
262         }
263       }
264       else {
265         params.push(this.params[name].string());
266       }
267     }
268     return AjaxSolr.compact(params).join('&');
269   },
270 
271   /**
272    * Parses a query string into Solr parameters.
273    *
274    * @param {String} str The string to parse.
275    */
276   parseString: function (str) {
277     var pairs = str.split('&');
278     for (var i = 0, l = pairs.length; i < l; i++) {
279       if (pairs[i]) { // ignore leading, trailing, and consecutive &'s
280         var param = new AjaxSolr.Parameter();
281         param.parseString(pairs[i]);
282         this.add(param.name, param);
283       }
284     }
285   },
286 
287   /**
288    * Returns the exposed parameters as a query string.
289    *
290    * @returns {String} A string representation of the exposed parameters.
291    */
292   exposedString: function () {
293     var params = [];
294     for (var i = 0, l = this.exposed.length; i < l; i++) {
295       if (this.params[this.exposed[i]] !== undefined) {
296         if (this.isMultiple(this.exposed[i])) {
297           for (var j = 0, m = this.params[this.exposed[i]].length; j < m; j++) {
298             params.push(this.params[this.exposed[i]][j].string());
299           }
300         }
301         else {
302           params.push(this.params[this.exposed[i]].string());
303         }
304       }
305     }
306     return AjaxSolr.compact(params).join('&');
307   },
308 
309   /**
310    * Resets the values of the exposed parameters.
311    */
312   exposedReset: function () {
313     for (var i = 0, l = this.exposed.length; i < l; i++) {
314       this.remove(this.exposed[i]);
315     }
316   },
317 
318   /**
319    * Loads the values of exposed parameters from persistent storage. It is
320    * necessary, in most cases, to reset the values of exposed parameters before
321    * setting the parameters to the values in storage. This is to ensure that a
322    * parameter whose name is not present in storage is properly reset.
323    *
324    * @param {Boolean} [reset=true] Whether to reset the exposed parameters.
325    *   before loading new values from persistent storage. Default: true.
326    */
327   load: function (reset) {
328     if (reset === undefined) {
329       reset = true;
330     }
331     if (reset) {
332       this.exposedReset();
333     }
334     this.parseString(this.storedString());
335   },
336   
337   /**
338    * An abstract hook for child implementations.
339    *
340    * <p>Stores the values of the exposed parameters in persistent storage. This
341    * method should usually be called before each Solr request.</p>
342    */
343   save: function () {},
344 
345   /**
346    * An abstract hook for child implementations.
347    *
348    * <p>Returns the string to parse from persistent storage.</p>
349    *
350    * @returns {String} The string from persistent storage.
351    */
352   storedString: function () {
353     return '';
354   }
355 });
356