a80b0757 by Adam Heath

foo

1 parent a5eadaa4
...@@ -22,35 +22,111 @@ define(function(require) { ...@@ -22,35 +22,111 @@ define(function(require) {
22 return result; 22 return result;
23 } 23 }
24 24
25 function mergeStaticSets(startPtr, endPtr, obj, fieldName) {
26 function arrayToMap(array) {
27 var result = {};
28 _.each(array, function(value) {
29 result[value] = true;
30 });
31 return result;
32 }
33 var set = arrayToMap(obj);
34 var ptr = startPtr;
35 while (true) {
36 set = _.extend(set, arrayToMap(_.result(ptr, fieldName)));
37 if (ptr === endPtr) {
38 break;
39 }
40 ptr = ptr.__super__.constructor;
41 }
42 return _.keys(set);
43 }
44
25 function getItemKeyAccessor(item) { 45 function getItemKeyAccessor(item) {
26 return item.get('key'); 46 return item.get('key');
27 } 47 }
28 var SolrFacets = (function() { 48 var SolrFacets = (function() {
29 var Facets = NestedModels.mixin(Backbone.Model.extend({ 49 var Facets = NestedModels.mixin(Backbone.Model.extend({
30 initialize: function(data, options) { 50 initialize: function(data, options) {
51 this.url = function() {
52 return options.search.url();
53 };
31 _.each(this.keys(), _.bind(function(facetName) { 54 _.each(this.keys(), _.bind(function(facetName) {
32 var facet = this.get(facetName); 55 var facet = this.get(facetName);
33 facet.on('item-change', function() { 56 _.each({
34 this.trigger('item-change'); 57 'item-change': 'item-change',
58 'change:query': 'child-query',
59 'change:queryMin': 'item-change',
60 'change:queryMax': 'item-change',
61 }, function(eventValue, eventKey) {
62 facet.on(eventKey, function(model, value, options) {
63 this.trigger(eventValue, facet, null, options);
64 }, this);
35 }, this); 65 }, this);
36 }, this)); 66 }, this));
67
68 this._doSuggestions = _.debounce(_.bind(function(childFacet) {
69 if (childFacet.get('query')) {
70 this.fetch({
71 data: this.toJSON({childFacet: childFacet, facetSuggestions: true}),
72 childFacet: childFacet,
73 facetSuggestions: true,
74 merge: true,
75 traditional: true,
76 });
77 } else {
78 childFacet.set('suggestions', []);
79 }
80 }, this), 250);
81 this.on('child-query', this._doSuggestions, this);
82
37 return Facets.__super__.initialize.apply(this, arguments); 83 return Facets.__super__.initialize.apply(this, arguments);
38 }, 84 },
39 resetSearch: function() { 85 resetSearch: function(options) {
40 _.each(this.values(), function(facet) { 86 _.each(this.values(), function(facet) {
41 facet.get('items').reset(); 87 facet.get('items').reset(null, options);
42 }); 88 });
43 }, 89 },
44 applyFacetResults: function(data) { 90 applyFacetResults: function(data, options) {
91 options = options || {};
45 var facetCounts = getField(data, 'facet_counts'); 92 var facetCounts = getField(data, 'facet_counts');
93 var facetIntervals = getField(facetCounts, 'facet_intervals');
46 var facetRanges = getField(facetCounts, 'facet_ranges'); 94 var facetRanges = getField(facetCounts, 'facet_ranges');
47 var facetFields = getField(facetCounts, 'facet_fields'); 95 var facetFields = getField(facetCounts, 'facet_fields');
48 var facetQueries = getField(facetCounts, 'facet_queries'); 96 var facetQueries = getField(facetCounts, 'facet_queries');
97 var statsFields = getField(data.stats, 'stats_fields');
98 function getFacetList(facetName, type) {
99 switch (type) {
100 case 'year':
101 case 'range':
102 return facetRanges[facetName] ? facetRanges[facetName].counts : null;
103 case 'interval':
104 return facetIntervals[facetName] ? facetIntervals[facetName].counts : null;
105 case 'year-field':
106 case 'field':
107 return facetFields[facetName];
108 }
109 }
49 _.each(this.keys(), _.bind(function(facetName) { 110 _.each(this.keys(), _.bind(function(facetName) {
50 var facet = this.get(facetName); 111 var facet = this.get(facetName);
51 var type = facet.facetType; 112 var type = facet.facetType;
52 var list; 113
53 var key, newItems = []; 114 var suggestions = [];
115 _.each(getFacetList('suggestions:' + facetName, type), function(value, index) {
116 if (index % 2 === 0) {
117 key = value;
118 } else if (value > 0) {
119 suggestions.push({key: key, value: value});
120 }
121 });
122 facet.set('suggestions', suggestions);
123 if (!!options.facetSuggestions && options.childFacet === facet) {
124 return;
125 }
126
127 var key;
128 var list = getFacetList(facetName, type);
129 var newItems = [];
54 var items = facet.get('items'); 130 var items = facet.get('items');
55 items.invoke('set', 'hidden', true); 131 items.invoke('set', 'hidden', true);
56 var valueOverrides = {}; 132 var valueOverrides = {};
...@@ -59,12 +135,8 @@ define(function(require) { ...@@ -59,12 +135,8 @@ define(function(require) {
59 excludeValues[includeValue] = true; 135 excludeValues[includeValue] = true;
60 } 136 }
61 switch (type) { 137 switch (type) {
62 case 'year': 138 case 'year-field':
63 case 'range':
64 list = facetRanges[facetName].counts;
65 break;
66 case 'field': 139 case 'field':
67 list = facetFields[facetName];
68 _.each(facet.bins, function(includeValues, key) { 140 _.each(facet.bins, function(includeValues, key) {
69 var tag = facetName + ':' + key; 141 var tag = facetName + ':' + key;
70 valueOverrides[key] = facetQueries[tag]; 142 valueOverrides[key] = facetQueries[tag];
...@@ -109,6 +181,7 @@ define(function(require) { ...@@ -109,6 +181,7 @@ define(function(require) {
109 } 181 }
110 }); 182 });
111 switch (type) { 183 switch (type) {
184 case 'year-field':
112 case 'field': 185 case 'field':
113 _.each(facet.bins, function(includeValues, key) { 186 _.each(facet.bins, function(includeValues, key) {
114 var tag = facetName + ':' + key; 187 var tag = facetName + ':' + key;
...@@ -117,49 +190,85 @@ define(function(require) { ...@@ -117,49 +190,85 @@ define(function(require) {
117 break; 190 break;
118 } 191 }
119 items.set(newItems, {remove: false}); 192 items.set(newItems, {remove: false});
193 if (facet.facetStats) {
194 var thisFacetStats = statsFields[facetName];
195 facet.set({minValue: thisFacetStats.min, maxValue: thisFacetStats.max});
196 }
120 }, this)); 197 }, this));
121 }, 198 },
122 getFacetFormData: function() { 199 getFacetFormData: function(options) {
200 options = options || {};
123 var result = { 201 var result = {
124 facet: true, 202 facet: true,
203 stats: true,
125 'facet.missing': true, 204 'facet.missing': true,
126 'facet.field': [], 205 'facet.field': [],
127 'facet.range': [], 206 'facet.range': [],
207 'facet.interval': [],
128 'facet.query': [], 208 'facet.query': [],
209 'stats.field': [],
129 fq: [], 210 fq: [],
130 }; 211 };
131 _.each(this.keys(), _.bind(function(facetName) { 212 _.each(this.keys(), _.bind(function(facetName) {
132 var facet = this.get(facetName); 213 var facet = this.get(facetName);
133 var queryField = facet.queryField ? facet.queryField : facetName; 214 var queryField = facet.queryField;
215 var facetField = facet.facetField ? facet.facetField : queryField;
134 var type = facet.facetType; 216 var type = facet.facetType;
135 var valueFormatter; 217 var valueFormatter;
136 var facetOptions = {}; 218 var facetOptions = {};
219 var quoteFormatter = function(value) {
220 return value.replace ? ('"' + value.replace(/"/, '\\"') + '"') : value;
221 };
222 var rangeFormatter = function(from, to) {
223 return '[' + quoteFormatter(from) + ' TO ' + quoteFormatter(to) + ']';
224 };
137 switch (type) { 225 switch (type) {
138 case 'year': 226 case 'year':
139 type = 'range'; 227 type = 'range';
228 quoteFormatter = function(value) {
229 return value.toISOString();
230 };
140 valueFormatter = function(item) { 231 valueFormatter = function(item) {
141 var key = item.get('key'); 232 var key = item.get('key');
142 var fromDate, toDate; 233 var fromDate, toDate;
143 if (key === 'before') { 234 if (key === 'before') {
144 return '[* TO ' + facet.rangeStart + '-1SECOND]'; 235 return rangeFormatter('*', facet.rangeStart + '-1SECOND');
145 } 236 }
146 fromDate = new Date(key); 237 fromDate = new Date(key);
147 toDate = new Date(key); 238 toDate = new Date(key);
148 toDate.setUTCFullYear(toDate.getUTCFullYear() + 1); 239 toDate.setUTCFullYear(toDate.getUTCFullYear() + 1);
149 return '[' + fromDate.toISOString() + ' TO ' + toDate.toISOString() + ']'; 240 return rangeFormatter(fromDate, toDate);
150 }; 241 };
151 if (facet.other) { 242 if (facet.other) {
152 facetOptions['facet.range.other'] = facet.other; 243 facetOptions['facet.range.other'] = facet.other;
153 } 244 }
154 break; 245 break;
155 case 'range': 246 case 'range':
247 case 'interval':
156 valueFormatter = function(item) { 248 valueFormatter = function(item) {
157 var key = parseInt(item.get('key')); 249 var key = parseInt(item.get('key'));
158 return '[' + key + ' TO ' + (key + facet.rangeGap) + ']'; 250 return rangeFormatter(key, key + facet.rangeGap);
251 };
252 break;
253 case 'year-field':
254 type = 'field';
255 quoteFormatter = function(value) {
256 return new Date(value).toISOString();
257 };
258 valueFormatter = facet.queryValue ? facet.queryValue : function(value) {
259 return quoteFormatter(getItemKeyAccessor(value));
159 }; 260 };
261 _.each(facet.bins, function(includeValues, key) {
262 var query = _.map(includeValues, function(includeValue, index) {
263 return queryField + ':\'' + includeValue + '\'';
264 }).join(' OR ');
265 result['facet.query'].push('{!key=' + facetName + ':' + key + ' tag=' + facetName + ':' + key + '}(' + query + ')');
266 });
160 break; 267 break;
161 case 'field': 268 case 'field':
162 valueFormatter = facet.queryValue ? facet.queryValue : getItemKeyAccessor; 269 valueFormatter = facet.queryValue ? facet.queryValue : function(value) {
270 return quoteFormatter(getItemKeyAccessor(value));
271 };
163 _.each(facet.bins, function(includeValues, key) { 272 _.each(facet.bins, function(includeValues, key) {
164 var query = _.map(includeValues, function(includeValue, index) { 273 var query = _.map(includeValues, function(includeValue, index) {
165 return queryField + ':' + includeValue; 274 return queryField + ':' + includeValue;
...@@ -174,8 +283,16 @@ define(function(require) { ...@@ -174,8 +283,16 @@ define(function(require) {
174 facetOptions['facet.range.end'] = facet.rangeEnd; 283 facetOptions['facet.range.end'] = facet.rangeEnd;
175 facetOptions['facet.range.gap'] = facet.rangeGap; 284 facetOptions['facet.range.gap'] = facet.rangeGap;
176 break; 285 break;
286 case 'interval':
287 facetOptions['facet.interval.set'] = '[*,*]';
288 break;
177 } 289 }
178 var facetValues = []; 290 var facetValues = [];
291 var queryMax = facet.get('queryMax');
292 var queryMin = facet.get('queryMin');
293 if (queryMax !== undefined && queryMin !== undefined) {
294 facetValues.push(rangeFormatter(queryMin, queryMax));
295 }
179 facet.get('items').each(function(item, index) { 296 facet.get('items').each(function(item, index) {
180 if (!item.get('hidden') && item.get('checked')) { 297 if (!item.get('hidden') && item.get('checked')) {
181 facetValues.push(valueFormatter(item)); 298 facetValues.push(valueFormatter(item));
...@@ -184,22 +301,61 @@ define(function(require) { ...@@ -184,22 +301,61 @@ define(function(require) {
184 facetOptions.key = facetName; 301 facetOptions.key = facetName;
185 if (facetValues.length) { 302 if (facetValues.length) {
186 facetOptions.ex = facetName; 303 facetOptions.ex = facetName;
187 result.fq.push('{!tag=' + facetName + '}' + queryField + ':(' + facetValues.join(' OR ') + ')'); 304 result.fq.push('{!tag=' + facetName + '}' + facetField + ':(' + facetValues.join(' OR ') + ')');
188 } else { 305 } else {
189 facetOptions.tag = facetName; 306 facetOptions.tag = facetName;
190 } 307 }
308 if (!!options.facetSuggestions) {
309 var childFacet = options.childFacet;
310 if (childFacet === facet) {
311 facetOptions['facet.contains'] = facet.get('query');
312 facetOptions['facet.contains.ignoreCase'] = true;
313 facetOptions['facet.mincount'] = 1;
314 }
315 }
191 var facetOptionList = []; 316 var facetOptionList = [];
192 _.each(facetOptions, function(value, key) { 317 _.each(facetOptions, function(value, key) {
193 if (false && key.indexOf('facet.') === 0) { 318 if (value !== undefined) {
194 result['f.' + queryField + '.' + key] = value;
195 } else {
196 facetOptionList.push(key + '=' + value); 319 facetOptionList.push(key + '=' + value);
197 } 320 }
198 }); 321 });
199 result['facet.' + type].push('{!' + facetOptionList.join(' ') + '}' + queryField); 322 result['facet.' + type].push('{!' + facetOptionList.join(' ') + '}' + queryField);
323 if (facet.facetStats) {
324 result['stats.field'].push('{!' + facetOptionList.join(' ') + '}' + queryField);
325 }
326 var facetQuery = facet.get('query');
327 if (facetQuery !== null && facetQuery !== '') {
328 facetOptions.key = 'suggestions:' + facetName;
329 facetOptionList = [];
330 _.each(facetOptions, function(value, key) {
331 if (value !== undefined) {
332 facetOptionList.push(key + '=' + value);
333 }
334 });
335 result['facet.' + type].push('{!' + facetOptionList.join(' ') + '}' + queryField);
336 }
200 }, this)); 337 }, this));
201 return result; 338 return result;
202 } 339 },
340
341
342
343 toJSON: function toJSON(options) {
344 if (!!options.facetSuggestions) {
345 return _.extend({
346 rows: 0,
347 facet: true,
348 wt: 'json',
349 q: '*:*',
350 }, this.getFacetFormData(options));
351 }
352 },
353 parse: function parse(data, options) {
354 if (!!options.facetSuggestions) {
355 this.applyFacetResults(data, options);
356 }
357 return null;
358 },
203 })); 359 }));
204 var Sort = Facets.Sort = { 360 var Sort = Facets.Sort = {
205 standard: function standardSort(a, b) { 361 standard: function standardSort(a, b) {
...@@ -241,31 +397,95 @@ define(function(require) { ...@@ -241,31 +397,95 @@ define(function(require) {
241 }; 397 };
242 } 398 }
243 var Facet = Facets.Facet = Backbone.Model.extend({ 399 var Facet = Facets.Facet = Backbone.Model.extend({
400 defaults: function defaults() {
401 return {
402 formName: null,
403 query: null,
404 suggestions: [],
405 };
406 },
244 initialize: function(data, options) { 407 initialize: function(data, options) {
245 this.set('all', true); 408 this.set('all', true);
409 this.on('change:queryMax change:queryMin', function() {
410 this.set('hasQuery', this.get('queryMin') !== undefined && this.get('queryMax') !== undefined);
411 }, this);
246 (function(self) { 412 (function(self) {
247 var skipCallback; 413 var skipCallback;
248 self.on('item-change', function() { 414 self.on('item-change change:queryMax change:queryMin', function() {
249 if (skipCallback === 'change:all') { 415 switch (skipCallback) {
250 return; 416 case 'change:checkedKeys':
417 case 'change:all':
418 return;
419 }
420 var checkedKeys = [];
421 var checkedCount
422 this.get('items').each(function(facetItem) {
423 if (facetItem.get('checked')) {
424 checkedKeys.push(facetItem.get('key'));
425 }
426 });
427 var checkedCount = checkedKeys.length;
428 if (this.get('queryMin') !== undefined && this.get('queryMax') !== undefined) {
429 checkedCount++;
251 } 430 }
252 var checkedCount = this.get('items').countBy(function(facetItem) {
253 return facetItem.get('checked');
254 }).true;
255 skipCallback = 'item-change'; 431 skipCallback = 'item-change';
256 try { 432 try {
257 this.set('all', checkedCount === 0); 433 this.set({
434 all: checkedCount === 0,
435 checkedKeys: checkedKeys,
436
437 });
258 } finally { 438 } finally {
259 skipCallback = null; 439 skipCallback = null;
260 } 440 }
261 }, self); 441 }, self);
262 self.on('change:all', function() { 442 self.on('change:all', function() {
263 if (skipCallback === 'item-change') { 443 switch (skipCallback) {
264 return; 444 case 'change:checkedKeys':
445 case 'item-change':
446 return;
265 } 447 }
266 skipCallback = 'change:all'; 448 skipCallback = 'change:all';
267 try { 449 try {
268 this.get('items').invoke('set', 'checked', false); 450 this.get('items').invoke('set', {checked: false});
451 this.set({
452 checkedKeys: [],
453 queryMax: undefined,
454 queryMin: undefined,
455 });
456 } finally {
457 skipCallback = null;
458 }
459 }, self);
460 self.on('change:checkedKeys', function() {
461 switch (skipCallback) {
462 case 'item-change':
463 case 'change:all':
464 return;
465 }
466 var checkedKeys = {};
467 _.each(this.get('checkedKeys'), function(value, i) {
468 checkedKeys[value] = true;
469 });
470 skipCallback = 'change:checkedKeys';
471 var checkedCount = 0;
472 if (this.get('queryMin') !== undefined && this.get('queryMax') !== undefined) {
473 checkedCount++;
474 }
475 try {
476 this.get('items').each(function(facetItem) {
477 var newChecked = checkedKeys[facetItem.get('key')];
478 var currentChecked = facetItem.get('checked');
479 if (newChecked !== currentChecked) {
480 facetItem.set('checked', newChecked);
481 }
482 if (facetItem.get('checked')) {
483 checkedCount++;
484 }
485 });
486 this.set({
487 all: checkedCount === 0,
488 });
269 } finally { 489 } finally {
270 skipCallback = null; 490 skipCallback = null;
271 } 491 }
...@@ -284,6 +504,7 @@ define(function(require) { ...@@ -284,6 +504,7 @@ define(function(require) {
284 switch (options.facetType) { 504 switch (options.facetType) {
285 case 'year': 505 case 'year':
286 case 'range': 506 case 'range':
507 case 'interval':
287 comparator = wrapComparatorForAllBeforeAfter(comparator, ['all', 'before'].indexOf(options.other) !== -1, ['all', 'after'].indexOf(options.other) !== -1); 508 comparator = wrapComparatorForAllBeforeAfter(comparator, ['all', 'before'].indexOf(options.other) !== -1, ['all', 'after'].indexOf(options.other) !== -1);
288 break; 509 break;
289 } 510 }
...@@ -317,7 +538,9 @@ define(function(require) { ...@@ -317,7 +538,9 @@ define(function(require) {
317 })(comparator); 538 })(comparator);
318 this.set('items', new ItemCollection([], {comparator: comparator})); 539 this.set('items', new ItemCollection([], {comparator: comparator}));
319 this.facetType = options.facetType; 540 this.facetType = options.facetType;
541 this.facetStats = options.facetStats;
320 this.other = options.other; 542 this.other = options.other;
543 this.facetField = options.facetField;
321 this.queryField = options.queryField; 544 this.queryField = options.queryField;
322 this.queryValue = options.queryValue; 545 this.queryValue = options.queryValue;
323 this.bins = options.bins; 546 this.bins = options.bins;
...@@ -333,6 +556,8 @@ define(function(require) { ...@@ -333,6 +556,8 @@ define(function(require) {
333 this.rangeGap = options.gap; 556 this.rangeGap = options.gap;
334 break; 557 break;
335 case 'field': 558 case 'field':
559 case 'year-field':
560 case 'interval':
336 break; 561 break;
337 default: 562 default:
338 var e = new Error('Unsupported facet type: ' + this.facetType); 563 var e = new Error('Unsupported facet type: ' + this.facetType);
...@@ -340,7 +565,40 @@ define(function(require) { ...@@ -340,7 +565,40 @@ define(function(require) {
340 throw e; 565 throw e;
341 } 566 }
342 return Facet.__super__.initialize.apply(this, arguments); 567 return Facet.__super__.initialize.apply(this, arguments);
343 } 568 },
569 /*
570 toJSON: function toJSON(options) {
571 if (!!options.facetSuggestions) {
572 return {
573 rows: 0,
574 facet: true,
575 wt: 'json',
576 q: '*:*',
577 'facet.contains': this.get('query'),
578 'facet.contains.ignoreCase': true,
579 'facet.field': this.queryField,
580 'facet.mincount': 1,
581 };
582 }
583 },
584 parse: function parse(data, options) {
585 if (!!options.facetSuggestions) {
586 var suggestions = [];
587 var key;
588 _.each(data.facet_counts.facet_fields[this.queryField], function(value, index) {
589 if (index % 2 === 0) {
590 key = value;
591 } else if (value > 0) {
592 suggestions.push({key: key, value: value});
593 }
594 });
595 return {
596 suggestions: suggestions,
597 };
598 }
599 return null;
600 },
601 */
344 }); 602 });
345 var ItemCollection = Backbone.Collection.extend({ 603 var ItemCollection = Backbone.Collection.extend({
346 remove: function() { 604 remove: function() {
...@@ -419,7 +677,7 @@ define(function(require) { ...@@ -419,7 +677,7 @@ define(function(require) {
419 startAt = 1; 677 startAt = 1;
420 } 678 }
421 if (endAt - startAt < 9) { 679 if (endAt - startAt < 9) {
422 /* global console: false */ 680 /* global console:false */
423 console.log('foo'); 681 console.log('foo');
424 } 682 }
425 683
...@@ -464,7 +722,7 @@ define(function(require) { ...@@ -464,7 +722,7 @@ define(function(require) {
464 next = _.bind(function(e) { 722 next = _.bind(function(e) {
465 e.preventDefault(); 723 e.preventDefault();
466 this.set('currentPage', this.get('currentPage') + pageCount); 724 this.set('currentPage', this.get('currentPage') + pageCount);
467 return true; 725 return false;
468 }, this); 726 }, this);
469 } else { 727 } else {
470 next = stepFalse; 728 next = stepFalse;
...@@ -473,7 +731,7 @@ define(function(require) { ...@@ -473,7 +731,7 @@ define(function(require) {
473 previous = _.bind(function(e) { 731 previous = _.bind(function(e) {
474 e.preventDefault(); 732 e.preventDefault();
475 this.set('currentPage', this.get('currentPage') - pageCount); 733 this.set('currentPage', this.get('currentPage') - pageCount);
476 return true; 734 return false;
477 }, this); 735 }, this);
478 } else { 736 } else {
479 previous = stepFalse; 737 previous = stepFalse;
...@@ -505,6 +763,14 @@ define(function(require) { ...@@ -505,6 +763,14 @@ define(function(require) {
505 value: null, 763 value: null,
506 items: new Backbone.Collection(), 764 items: new Backbone.Collection(),
507 }, 765 },
766 initialize: function(data, options) {
767 if (this.get('value') === null) {
768 var firstItem = this.get('items').at(0);
769 if (firstItem) {
770 this.set('value', firstItem.get('value'));
771 }
772 }
773 },
508 parse: function(data) { 774 parse: function(data) {
509 var result = _.clone(data); 775 var result = _.clone(data);
510 if (result.items && !(result.items instanceof Backbone.Collection)) { 776 if (result.items && !(result.items instanceof Backbone.Collection)) {
...@@ -513,94 +779,173 @@ define(function(require) { ...@@ -513,94 +779,173 @@ define(function(require) {
513 return result; 779 return result;
514 }, 780 },
515 }); 781 });
782 var QueryTextField = Backbone.Model.extend({
783 defaults: {
784 formName: null,
785 name: null,
786 queries: [],
787 fields: null,
788 multi: false
789 },
790 });
516 var SolrSearch = Pagination.extend({ 791 var SolrSearch = Pagination.extend({
517 url: function url() { 792 url: function url() {
518 return this.constructor.selectUrl; 793 return this.constructor.selectUrl;
519 }, 794 },
520 defaults: function defaults() { 795 defaults: function defaults() {
796 var constructor = this.constructor;
797 var formNameMap = {};
798 var facets = new SolrFacets(this.constructor.facets, {search: this});
799 _.each(facets.values(), function(facet) {
800 var formName = facet.get('formName');
801 if (formName) {
802 formNameMap[formName] = facet;
803 }
804 });
805 var queryFields = new Backbone.Model();
806 _.each(constructor.queryTextFields, function(definition, queryName) {
807 var qtf = new QueryTextField({formName: definition.formName, name: queryName, queries: [], fields: definition.fields, multi: !!definition.multi});
808 var formName = qtf.get('formName');
809 if (formName) {
810 formNameMap[formName] = qtf;
811 }
812 queryFields.set(queryName, qtf);
813 }, this);
521 return _.extend(_.result(SolrSearch.__super__, 'defaults'), { 814 return _.extend(_.result(SolrSearch.__super__, 'defaults'), {
522 initializing: true, 815 initializing: true,
523 initialized: false, 816 initialized: false,
524 query: '', 817 query: '',
818 formNameMap: formNameMap,
525 results: new Backbone.Collection(), 819 results: new Backbone.Collection(),
526 ordering: new Ordering({items: this.constructor.orderingItems}, {parse: true}), 820 ordering: new Ordering({items: constructor.orderingItems}, {parse: true}),
527 facets: new SolrFacets(this.constructor.facets), 821 facets: facets,
822 queryFields: queryFields,
823 });
824 },
825 applyQueryParameters: function() {
826 var skipOptions = {skipSearch: true};
827 var parts = document.location.href.match(/.*\?(.*)/);
828 var facets = this.get('facets');
829 facets.resetSearch();
830 _.each(this.get('queryFields').values(), function(qtf) {
831 qtf.set({
832 query: null,
833 queries: [],
834 }, skipOptions);
528 }); 835 });
836
837 if (parts) {
838 var formNameMap = this.get('formNameMap');
839 var keyValueParts = parts[1].split('&');
840 for (var i = 0; i < keyValueParts.length; i++) {
841 var keyFieldValue = keyValueParts[i].match(/^([^.]+)(?:\.([^.]+))?=(.*)$/);
842 if (keyFieldValue) {
843 var key = keyFieldValue[1];
844 var field = keyFieldValue[2];
845 var value = keyFieldValue[3];
846 value = value.replace(/(\+|%20)/g, ' ');
847 var impl = formNameMap[key];
848 if (impl) {
849 if (impl instanceof QueryTextField) {
850 impl.set('query', value, skipOptions);
851 } else if (impl.facetType === 'field' && value) {
852 if (field === 'min') {
853 impl.set('queryMin', parseInt(value), skipOptions);
854 } else if (field === 'max') {
855 impl.set('queryMax', parseInt(value), skipOptions);
856 } else if (field === 'items') {
857 var items = impl.get('items');
858 var item = items.get(value);
859 if (!item) {
860 item = new SolrFacets.Facet.Item({key: value, value: 0});
861 items.add(item);
862 item.on('change:checked', function(model, value, options) {
863 this.trigger('item-change', null, null, options);
864 impl.trigger('item-change', null, null, options);
865 }, facets);
866 }
867 item.set('checked', true, skipOptions);
868 }
869 }
870 }
871 }
872 }
873 }
529 }, 874 },
530 initialize: function(data, options) { 875 initialize: function(data, options) {
531 console.log('SolrSearch:initialize');
532 options = (options || {}); 876 options = (options || {});
533 this.set('options', new Backbone.Model({ 877 this.set('options', new Backbone.Model({
534 faceting: !!options.faceting, 878 faceting: !!options.faceting,
535 highlighting: !!options.highlighting, 879 highlighting: !!options.highlighting,
880 showAll: !!options.showAll,
536 })); 881 }));
537 this._resetItems = {}; 882 this._doSearchImmediately = _.bind(function(options) {
538 this._doSearch = _.debounce(_.bind(function() { 883 this.fetch(_.extend({}, options, {
539 console.log('doSearch[b], about to call fetch');
540 this.fetch({
541 data: this.toJSON(), 884 data: this.toJSON(),
542 merge: false, 885 merge: false,
543 traditional: true, 886 traditional: true,
544 }); 887 }));
545 }, this), 250); 888 }, this);
889 this._doSearch = _.debounce(this._doSearchImmediately, 250);
546 this._doSearch = (function(doSearch) { 890 this._doSearch = (function(doSearch) {
547 return function() { 891 return function(options) {
548 console.log('doSearch[a]', this._skipSearch); 892 var skipSearch = this._skipSearch || (options || {}).skipSearch;
549 if (!this._skipSearch) { 893 if (!skipSearch) {
550 doSearch(); 894 doSearch(options);
551 } 895 }
552 }; 896 };
553 })(this._doSearch); 897 })(this._doSearch);
554 898 _.each(this.get('queryFields').values(), function(subQueryModel) {
555 this.get('options').on('change:faceting change:highlighting', this._doSearch); 899 subQueryModel.on('change:query change:queries', function(model, value, options) {
556 this.on('change:query', function() { 900 this.trigger('change:queryFields', null, null, options);
901 }, this);
902 }, this);
903 this.on('change:queryFields', function(model, value, options) {
904 this.trigger('change:query', null, null, options);
905 }, this);
906 this.get('options').on('change:faceting change:highlighting', function(model, value, options) {
907 this._doSearch(options);
908 }, this);
909 this.on('change:query', function(model, value, options) {
557 // this is silent, which causes the change events to not fire; 910 // this is silent, which causes the change events to not fire;
558 // this is ok, because the next .on will also see the change 911 // this is ok, because the next .on will also see the change
559 // event on query, and resend the search 912 // event on query, and resend the search
560 this._resetItems.currentPage = true; 913 this._doSearch(_.extend({}, options, {resetCurrentPage: true, resetFacets: true, resetOrdering: true}));
561 this._resetItems.facets = true; 914 }, this);
562 this._resetItems.ordering = true; 915 this.on('change:pageSize', function(model, value, options) {
563 this._doSearch(); 916 this._doSearch(_.extend({}, options, {resetCurrentPage: true}));
564 }, this); 917 }, this);
565 this.on('change:pageSize', function() { 918 this.on('change:currentPage', function(model, value, options) {
566 this._resetItems.currentPage = true; 919 this._doSearch(options);
567 this._doSearch();
568 }, this); 920 }, this);
569 this.on('change:currentPage', this._doSearch); 921 this.get('facets').on('item-change', function(model, value, options) {
570 this.get('facets').on('item-change', function() { 922 this._doSearch(_.extend({}, options, {resetCurrentPage: true}));
571 this._resetItems.currentPage = true;
572 this._doSearch();
573 }, this); 923 }, this);
574 this.get('ordering').on('change:value', function() { 924 this.get('ordering').on('change:value', function(model, value, options) {
575 this._resetItems.currentPage = true; 925 this._doSearch(_.extend({}, options, {resetCurrentPage: true}));
576 this._doSearch();
577 }, this); 926 }, this);
578 return SolrSearch.__super__.initialize.apply(this, arguments); 927 return SolrSearch.__super__.initialize.apply(this, arguments);
928
579 }, 929 },
580 parse: function parse(data, options) { 930 parse: function parse(data, options) {
581 // console.debug('SEARCH', data); 931 if (options.facetSuggestions) {
582 this._skipSearch = true; 932 return null;
583 try { 933 }
584 var resetItems = this._resetItems; 934 var skipOptions = _.extend({}, options, {skipSearch: true});
585 if (resetItems.currentPage) { 935 if (options.resetCurrentPage) {
586 this.set('currentPage', 1); 936 this.set('currentPage', 1, skipOptions);
587 } 937 }
588 if (resetItems.facets) { 938 if (options.resetFacets) {
589 this.get('facets').resetSearch(); 939 this.get('facets').resetSearch(skipOptions);
590 } 940 }
591 if (resetItems.ordering) { 941 if (options.resetOrdering) {
592 this.get('ordering').set('value', this.get('ordering').get('items').at(0).get('value')); 942 this.get('ordering').set('value', this.get('ordering').get('items').at(0).get('value'), skipOptions);
593 }
594 } finally {
595 delete this._skipSearch;
596 } 943 }
597 this._resetItems = {};
598 var facets = this.get('facets'); 944 var facets = this.get('facets');
599 if (this.get('options').get('faceting')) { 945 if (this.get('options').get('faceting')) {
600 facets.applyFacetResults(data); 946 facets.applyFacetResults(data);
601 console.log('facets', facets);
602 } else { 947 } else {
603 facets.resetSearch(); 948 facets.resetSearch(options);
604 } 949 }
605 var list = []; 950 var list = [];
606 var highlighting = this.get('options').get('highlighting') ? data.highlighting : {}; 951 var highlighting = this.get('options').get('highlighting') ? data.highlighting : {};
...@@ -631,7 +976,7 @@ define(function(require) { ...@@ -631,7 +976,7 @@ define(function(require) {
631 _.each(fieldsToParse, function(value, key) { 976 _.each(fieldsToParse, function(value, key) {
632 var parsed; 977 var parsed;
633 if (_.isFunction(value)) { 978 if (_.isFunction(value)) {
634 parsed = value.call(this, doc, index, itemHighlighting); 979 parsed = value.call(this, doc, index, itemHighlighting, key);
635 } else if (_.isArray(value)) { 980 } else if (_.isArray(value)) {
636 parsed = []; 981 parsed = [];
637 _.each(value, function(item, i) { 982 _.each(value, function(item, i) {
...@@ -650,41 +995,83 @@ define(function(require) { ...@@ -650,41 +995,83 @@ define(function(require) {
650 }, this); 995 }, this);
651 return result; 996 return result;
652 }, 997 },
653 toJSON: function() { 998 toJSON: function(options) {
654 var currentPage = this.get('currentPage'); 999 if (!options) {
655 var pageSize = this.get('pageSize'); 1000 options = {};
656 var sort = this.get('ordering').get('value');
657 var query = this.get('query');
658 query = query.replace(/:+/g, ' ');
659 if (this._resetItems.currentPage) {
660 currentPage = 1;
661 }
662 if (this._resetItems.ordering) {
663 sort = this.get('ordering').get('items').at(0).get('value');
664 } 1001 }
1002 var facets = this.get('facets');
665 var constructor = this.constructor; 1003 var constructor = this.constructor;
666 var queryFilter = constructor.queryFilter;
667 var result = { 1004 var result = {
668 defType: 'dismax', 1005 defType: 'edismax',
669 qf: queryFilter ? queryFilter : 'text', 1006 qf: constructor.queryField,
670 sort: sort, 1007 wt: 'json',
671 //'f.sku.hl.fragmenter': 'regex',
672 //'f.sku.hl.mergeContiguous': 'true',
673 //'f.sku.hl.regex.pattern': '[a-zA-Z]+-[0-9]{4}-[0-9]{1}-[a-zA-Z]{1}-[0-9]+',
674 //'f.sku.hl.regex.pattern': '...-....-..-.-....',
675 fl: '*,score',
676 rows: pageSize,
677 start: (currentPage - 1) * pageSize,
678 wt: 'json'
679 }; 1008 };
680 result.fl = mergeStaticProps(this.constructor, SolrSearch, [], 'returnFields'); 1009 var ordering = this.get('ordering');
1010 var sort = ordering.get('value');
1011 if (options.resetOrdering) {
1012 sort = ordering.get('items').at(0).get('value');
1013 }
1014 var currentPage = this.get('currentPage');
1015 var pageSize = this.get('pageSize');
1016 if (options.resetCurrentPage) {
1017 currentPage = 1;
1018 }
1019 result.sort = sort;
1020 result.rows = pageSize;
1021 result.start = (currentPage - 1) * pageSize;
1022 result.fl = mergeStaticSets(constructor, SolrSearch, [], 'returnFields');
681 if (this.get('options').get('highlighting')) { 1023 if (this.get('options').get('highlighting')) {
682 result.hl = true; 1024 result.hl = true;
683 result['hl.fl'] = ['content', 'title'].concat(constructor.extraHighlightFields); 1025 result['hl.fl'] = ['content', 'title'].concat(constructor.extraHighlightFields);
684 } 1026 }
1027
1028 if (this.get('options').get('faceting')) {
1029 var facetFormData = facets.getFacetFormData();
1030 var fq = facetFormData.fq;
1031 delete(facetFormData.fq);
1032 result = _.extend(result, facetFormData);
1033 if (fq.length) {
1034 if (result.fq) {
1035 result.fq = result.fq.concat(fq);
1036 } else {
1037 result.fq = fq;
1038 }
1039 }
1040 }
1041
1042 var queryParts = [];
1043 var queryFields = this.get('queryFields');
1044 _.each(queryFields.keys(), function(queryName) {
1045 var subQueryModel = queryFields.get(queryName);
1046 var fields = subQueryModel.get('fields');
1047 var queries = subQueryModel.get('queries');
1048 var query = subQueryModel.get('query');
1049 if (query) {
1050 queries = queries.concat([query]);
1051 }
1052 var subQuery = [];
1053 for (var i = 0; i < fields.length; i++) {
1054 var fieldName = fields[i];
1055 for (var j = 0; j < queries.length; j++) {
1056 // FIXME: quote the query
1057 subQuery.push(fieldName + ':\'' + queries[j] + '\'');
1058 }
1059 }
1060 if (subQuery.length) {
1061 queryParts.push(subQuery.join(' OR '));
1062 }
1063 }, this);
1064
1065 var query = queryParts.join(' AND ');
1066 if (!query) {
1067 query = '*:*';
1068 if (!this.get('options').get('showAll') && !result.fq) {
1069 result.rows = 0;
1070 }
1071 }
685 var dateBoostField = constructor.dateBoostField; 1072 var dateBoostField = constructor.dateBoostField;
686 var popularityBoostField = constructor.popularityBoostField; 1073 var popularityBoostField = constructor.popularityBoostField;
687 if (dateBoostField || popularityBoostField) { 1074 if (result.rows !== 0 && (dateBoostField || popularityBoostField)) {
688 var boostSum = []; 1075 var boostSum = [];
689 if (dateBoostField) { 1076 if (dateBoostField) {
690 result.dateBoost = 'recip(ms(NOW,' + dateBoostField + '),3.16e-11,1,1)'; 1077 result.dateBoost = 'recip(ms(NOW,' + dateBoostField + '),3.16e-11,1,1)';
...@@ -699,19 +1086,6 @@ define(function(require) { ...@@ -699,19 +1086,6 @@ define(function(require) {
699 } else { 1086 } else {
700 result.q = query; 1087 result.q = query;
701 } 1088 }
702 if (this.get('options').get('faceting')) {
703 var facetFormData = this.get('facets').getFacetFormData();
704 var fq = facetFormData.fq;
705 delete(facetFormData.fq);
706 result = _.extend(result, facetFormData);
707 if (fq.length) {
708 if (result.fq) {
709 result.fq = result.fq.concat(fq);
710 } else {
711 result.fq = fq;
712 }
713 }
714 }
715 return result; 1089 return result;
716 }, 1090 },
717 }, { 1091 }, {
......