foo
Showing
1 changed file
with
491 additions
and
117 deletions
... | @@ -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); |
159 | }; | 251 | }; |
160 | break; | 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)); | ||
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 | }); | ||
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; |
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); | ||
202 | } | 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) { |
416 | case 'change:checkedKeys': | ||
417 | case 'change:all': | ||
250 | return; | 418 | return; |
251 | } | 419 | } |
252 | var checkedCount = this.get('items').countBy(function(facetItem) { | 420 | var checkedKeys = []; |
253 | return facetItem.get('checked'); | 421 | var checkedCount |
254 | }).true; | 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++; | ||
430 | } | ||
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) { |
444 | case 'change:checkedKeys': | ||
445 | case 'item-change': | ||
264 | return; | 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,8 +565,41 @@ define(function(require) { | ... | @@ -340,8 +565,41 @@ 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); |
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}); | ||
343 | } | 593 | } |
344 | }); | 594 | }); |
595 | return { | ||
596 | suggestions: suggestions, | ||
597 | }; | ||
598 | } | ||
599 | return null; | ||
600 | }, | ||
601 | */ | ||
602 | }); | ||
345 | var ItemCollection = Backbone.Collection.extend({ | 603 | var ItemCollection = Backbone.Collection.extend({ |
346 | remove: function() { | 604 | remove: function() { |
347 | } | 605 | } |
... | @@ -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, | ||
528 | }); | 823 | }); |
529 | }, | 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); | ||
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 | } | ||
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; | ||
562 | this._resetItems.ordering = true; | ||
563 | this._doSearch(); | ||
564 | }, this); | 914 | }, this); |
565 | this.on('change:pageSize', function() { | 915 | this.on('change:pageSize', function(model, value, options) { |
566 | this._resetItems.currentPage = true; | 916 | this._doSearch(_.extend({}, options, {resetCurrentPage: true})); |
567 | this._doSearch(); | ||
568 | }, this); | 917 | }, this); |
569 | this.on('change:currentPage', this._doSearch); | 918 | this.on('change:currentPage', function(model, value, options) { |
570 | this.get('facets').on('item-change', function() { | 919 | this._doSearch(options); |
571 | this._resetItems.currentPage = true; | ||
572 | this._doSearch(); | ||
573 | }, this); | 920 | }, this); |
574 | this.get('ordering').on('change:value', function() { | 921 | this.get('facets').on('item-change', function(model, value, options) { |
575 | this._resetItems.currentPage = true; | 922 | this._doSearch(_.extend({}, options, {resetCurrentPage: true})); |
576 | this._doSearch(); | 923 | }, this); |
924 | this.get('ordering').on('change:value', function(model, value, options) { | ||
925 | this._doSearch(_.extend({}, options, {resetCurrentPage: true})); | ||
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 { | ||
584 | var resetItems = this._resetItems; | ||
585 | if (resetItems.currentPage) { | ||
586 | this.set('currentPage', 1); | ||
587 | } | 933 | } |
588 | if (resetItems.facets) { | 934 | var skipOptions = _.extend({}, options, {skipSearch: true}); |
589 | this.get('facets').resetSearch(); | 935 | if (options.resetCurrentPage) { |
936 | this.set('currentPage', 1, skipOptions); | ||
590 | } | 937 | } |
591 | if (resetItems.ordering) { | 938 | if (options.resetFacets) { |
592 | this.get('ordering').set('value', this.get('ordering').get('items').at(0).get('value')); | 939 | this.get('facets').resetSearch(skipOptions); |
593 | } | 940 | } |
594 | } finally { | 941 | if (options.resetOrdering) { |
595 | delete this._skipSearch; | 942 | this.get('ordering').set('value', this.get('ordering').get('items').at(0).get('value'), skipOptions); |
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 | }, { | ... | ... |
-
Please register or sign in to post a comment