72920506 by Adam Heath

Split files.

1 parent bbb7f7fb
{
"directory": "src/lib/bower"
}
......@@ -11,7 +11,7 @@ module.exports = function(grunt) {
browserOptions: {
},
model: [
'src/scripts/solr-search/model/**/*.js',
'src/scripts/solr/model/**/*.js',
],
};
/*
......
......@@ -3,49 +3,15 @@ define(function(require) {
var _ = require('underscore');
var Backbone = require('backbone');
var NestedModels = require('backbone-nested-models');
//var module = require('module');
function getField(obj, key) {
return obj[key];
}
function mergeStaticProps(startPtr, endPtr, obj, fieldName) {
var result = obj;
var ptr = startPtr;
while (true) {
result = _.extend(result, _.result(ptr, fieldName));
if (ptr === endPtr) {
break;
}
ptr = ptr.__super__.constructor;
}
return result;
}
function mergeStaticSets(startPtr, endPtr, obj, fieldName) {
function arrayToMap(array) {
var result = {};
_.each(array, function(value) {
result[value] = true;
});
return result;
}
var set = arrayToMap(obj);
var ptr = startPtr;
while (true) {
set = _.extend(set, arrayToMap(_.result(ptr, fieldName)));
if (ptr === endPtr) {
break;
}
ptr = ptr.__super__.constructor;
}
return _.keys(set);
}
function getItemKeyAccessor(item) {
return item.get('key');
}
var SolrFacets = (function() {
var Facets = NestedModels.mixin(Backbone.Model.extend({
initialize: function(data, options) {
this.url = function() {
......@@ -621,515 +587,4 @@ define(function(require) {
});
return Facets;
})();
var Pagination = (function() {
function stepFalse(e) {
e.preventDefault();
return false;
}
var Pagination = Backbone.Model.extend({
defaults: {
currentPage: 1,
hasNext: false,
hasNextJumpPage: false,
hasPrevious: false,
hasPreviousJumpPage: false,
nextJumpPage: undefined,
nextPage: undefined,
pages: [],
pageJump: 5,
pageSize: 10,
previousJumpPage: undefined,
previousPage: undefined,
totalCount: 0,
totalPages: 0,
},
initialize: function(data, options) {
var buildPages = function buildPages() {
var currentPage = parseInt(this.get('currentPage'));
var pageSize = parseInt(this.get('pageSize'));
var totalCount = parseInt(this.get('totalCount'));
if (!currentPage || !pageSize || !totalCount) {
return;
}
var pages = [];
var totalPages = Math.floor((totalCount + pageSize - 1) / pageSize);
function addPage(self, i) {
pages.push({
current: i === currentPage,
jump: function() {
self.set('currentPage', i);
return true;
},
number: i,
});
}
var startAt = currentPage - 4, endAt = currentPage + 5;
if (startAt < 1) {
endAt += (1 - startAt);
}
if (endAt > totalPages) {
startAt -= endAt - totalPages - 1;
endAt = totalPages + 1;
}
if (startAt < 1) {
startAt = 1;
}
if (endAt - startAt < 9) {
/* global console:false */
console.log('foo');
}
for (var i = startAt; i < endAt; i++) {
if (i > 0 && i <= totalPages) {
addPage(this, i);
}
}
var hasPrevious = currentPage > 1;
var hasNext = currentPage < totalPages;
var pageJump = this.get('pageJump');
var nextJumpPage, previousJumpPage, hasNextJump, hasPreviousJump;
if (pageJump) {
nextJumpPage = currentPage + pageJump;
previousJumpPage = currentPage - pageJump;
hasNextJump = nextJumpPage < totalPages;
hasPreviousJump = previousJumpPage > 0;
} else {
hasNextJump = false;
hasPreviousJump = false;
}
this.set({
hasNext: hasNext,
hasNextJump: hasNextJump,
hasPrevious: hasPrevious,
hasPreviousJump: hasPreviousJump,
nextJumpPage: hasNextJump ? nextJumpPage : undefined,
nextPage: hasNext ? currentPage + 1 : undefined,
pages: pages,
previousJumpPage: hasNextJump ? previousJumpPage : undefined,
previousPage: hasPrevious ? currentPage - 1 : undefined,
totalPages: totalPages,
});
};
this.on('change:pageSize change:currentPage change:totalCount', buildPages, this);
var installActions = _.bind(function installActions(eventName, nextName, hasNextName, previousName, hasPreviousName, pageCount) {
this.on(eventName, function() {
var hasNext = this.get(hasNextName);
var hasPrevious = this.get(hasPreviousName);
var next, previous;
if (hasNext) {
next = _.bind(function(e) {
e.preventDefault();
this.set('currentPage', this.get('currentPage') + pageCount);
return false;
}, this);
} else {
next = stepFalse;
}
if (hasPrevious) {
previous = _.bind(function(e) {
e.preventDefault();
this.set('currentPage', this.get('currentPage') - pageCount);
return false;
}, this);
} else {
previous = stepFalse;
}
this.set(nextName, next);
this.set(previousName, previous);
}, this);
this[nextName] = _.bind(function() {
return this.get(nextName)();
}, this);
this[previousName] = _.bind(function() {
return this.get(previousName)();
}, this);
}, this);
this.on('change:pageJump', function() {
var pageJump = this.get('pageJump');
installActions('change:hasNextJump change:hasPreviousJump', 'nextJump', 'hasNextJump', 'previousJump', 'hasPreviousJump', pageJump);
}, this);
installActions('change:hasNext change:hasPrevious', 'next', 'hasNext', 'previous', 'hasPrevious', 1);
buildPages.apply(this);
this.trigger('change:pageJump');
return Pagination.__super__.initialize.apply(this, arguments);
}
});
return Pagination;
})();
var Ordering = Backbone.Model.extend({
defaults: {
value: null,
items: new Backbone.Collection(),
},
initialize: function(data, options) {
if (this.get('value') === null) {
var firstItem = this.get('items').at(0);
if (firstItem) {
this.set('value', firstItem.get('value'));
}
}
},
parse: function(data) {
var result = _.clone(data);
if (result.items && !(result.items instanceof Backbone.Collection)) {
result.items = new Backbone.Collection(result.items, {parse: true});
}
return result;
},
});
var QueryTextField = Backbone.Model.extend({
defaults: {
formName: null,
name: null,
queries: [],
fields: null,
multi: false
},
});
var SolrSearch = Pagination.extend({
url: function url() {
return this.constructor.selectUrl;
},
defaults: function defaults() {
var constructor = this.constructor;
var formNameMap = {};
var facets = new SolrFacets(this.constructor.facets, {search: this});
_.each(facets.values(), function(facet) {
var formName = facet.get('formName');
if (formName) {
formNameMap[formName] = facet;
}
});
var queryFields = new Backbone.Model();
_.each(constructor.queryTextFields, function(definition, queryName) {
var qtf = new QueryTextField({formName: definition.formName, name: queryName, queries: [], fields: definition.fields, multi: !!definition.multi});
var formName = qtf.get('formName');
if (formName) {
formNameMap[formName] = qtf;
}
queryFields.set(queryName, qtf);
}, this);
return _.extend(_.result(SolrSearch.__super__, 'defaults'), {
initializing: true,
initialized: false,
query: '',
formNameMap: formNameMap,
results: new Backbone.Collection(),
ordering: new Ordering({items: constructor.orderingItems}, {parse: true}),
facets: facets,
queryFields: queryFields,
});
},
applyQueryParameters: function() {
var skipOptions = {skipSearch: true};
var parts = document.location.href.match(/.*\?(.*)/);
var facets = this.get('facets');
facets.resetSearch();
_.each(this.get('queryFields').values(), function(qtf) {
qtf.set({
query: null,
queries: [],
}, skipOptions);
});
if (parts) {
var formNameMap = this.get('formNameMap');
var keyValueParts = parts[1].split('&');
_.each(keyValueParts, function(keyValuePart, i) {
var keyFieldValue = keyValuePart.match(/^([^.]+)(?:\.([^.]+))?=(.*)$/);
if (keyFieldValue) {
var key = keyFieldValue[1];
var field = keyFieldValue[2];
var value = keyFieldValue[3];
value = value.replace(/(\+|%20)/g, ' ');
var impl = formNameMap[key];
if (impl) {
if (impl instanceof QueryTextField) {
impl.set('query', value, skipOptions);
} else if (impl.facetType === 'field' && value) {
if (field === 'min') {
impl.set('queryMin', parseInt(value), skipOptions);
} else if (field === 'max') {
impl.set('queryMax', parseInt(value), skipOptions);
} else if (field === 'items') {
var items = impl.get('items');
var item = items.get(value);
if (!item) {
item = new SolrFacets.Facet.Item({key: value, value: 0});
items.add(item);
item.on('change:checked', function(model, value, options) {
this.trigger('item-change', null, null, options);
impl.trigger('item-change', null, null, options);
}, facets);
}
item.set('checked', true, skipOptions);
}
}
}
}
}, this);
}
},
initialize: function(data, options) {
options = (options || {});
this.set('options', new Backbone.Model({
faceting: !!options.faceting,
highlighting: !!options.highlighting,
showAll: !!options.showAll,
}));
this._doSearchImmediately = _.bind(function(options) {
this.fetch(_.extend({}, options, {
data: this.toJSON(),
merge: false,
traditional: true,
}));
}, this);
this._doSearch = _.debounce(this._doSearchImmediately, 250);
this._doSearch = (function(doSearch) {
return function(options) {
var skipSearch = this._skipSearch || (options || {}).skipSearch;
if (!skipSearch) {
doSearch(options);
}
};
})(this._doSearch);
_.each(this.get('queryFields').values(), function(subQueryModel) {
subQueryModel.on('change:query change:queries', function(model, value, options) {
this.trigger('change:queryFields', null, null, options);
}, this);
}, this);
this.on('change:queryFields', function(model, value, options) {
this.trigger('change:query', null, null, options);
}, this);
this.get('options').on('change:faceting change:highlighting', function(model, value, options) {
this._doSearch(options);
}, this);
this.on('change:query', function(model, value, options) {
// this is silent, which causes the change events to not fire;
// this is ok, because the next .on will also see the change
// event on query, and resend the search
this._doSearch(_.extend({}, options, {resetCurrentPage: true, resetFacets: true, resetOrdering: true}));
}, this);
this.on('change:pageSize', function(model, value, options) {
this._doSearch(_.extend({}, options, {resetCurrentPage: true}));
}, this);
this.on('change:currentPage', function(model, value, options) {
this._doSearch(options);
}, this);
this.get('facets').on('item-change', function(model, value, options) {
this._doSearch(_.extend({}, options, {resetCurrentPage: true}));
}, this);
this.get('ordering').on('change:value', function(model, value, options) {
this._doSearch(_.extend({}, options, {resetCurrentPage: true}));
}, this);
return SolrSearch.__super__.initialize.apply(this, arguments);
},
parse: function parse(data, options) {
if (options.facetSuggestions) {
return null;
}
var skipOptions = _.extend({}, options, {skipSearch: true});
if (options.resetCurrentPage) {
this.set('currentPage', 1, skipOptions);
}
if (options.resetFacets) {
this.get('facets').resetSearch(skipOptions);
}
if (options.resetOrdering) {
this.get('ordering').set('value', this.get('ordering').get('items').at(0).get('value'), skipOptions);
}
var facets = this.get('facets');
if (this.get('options').get('faceting')) {
facets.applyFacetResults(data);
} else {
facets.resetSearch(options);
}
var list = [];
var highlighting = this.get('options').get('highlighting') ? data.highlighting : {};
var self = this;
_.each(data.response.docs, function(doc, index) {
var itemHighlighting = highlighting[doc.id];
if (!itemHighlighting) {
itemHighlighting = {};
}
list.push(self.searchParseDoc(doc, index, itemHighlighting));
});
return {
initializing: false,
initialized: true,
results: new Backbone.Collection(list),
facets: facets,
options: this.get('options'),
totalCount: data.response.numFound,
queryTime: (data.responseHeader.QTime / 1000).toFixed(2),
hasResults: list.length > 0,
};
},
searchParseDoc: function searchParseDoc(doc, index, itemHighlighting) {
var fieldsToParse = mergeStaticProps(this.constructor, SolrSearch, {}, 'parsedFieldMap');
var result = {};
_.each(fieldsToParse, function(value, key) {
var parsed;
if (_.isFunction(value)) {
parsed = value.call(this, doc, index, itemHighlighting, key);
} else if (_.isArray(value)) {
parsed = [];
_.each(value, function(item, i) {
var rawValue = doc[item.name];
if (rawValue) {
var parsedValue = item.parse.call(this, doc, index, itemHighlighting, rawValue, item.name);
if (parsedValue) {
parsed.push(parsedValue);
}
}
}, this);
} else {
parsed = doc[value];
}
result[key] = parsed;
}, this);
return result;
},
toJSON: function(options) {
if (!options) {
options = {};
}
var facets = this.get('facets');
var constructor = this.constructor;
var result = {
defType: 'edismax',
qf: constructor.queryField,
wt: 'json',
};
var ordering = this.get('ordering');
var sort = ordering.get('value');
if (options.resetOrdering) {
sort = ordering.get('items').at(0).get('value');
}
var currentPage = this.get('currentPage');
var pageSize = this.get('pageSize');
if (options.resetCurrentPage) {
currentPage = 1;
}
result.sort = sort;
result.rows = pageSize;
result.start = (currentPage - 1) * pageSize;
result.fl = mergeStaticSets(constructor, SolrSearch, [], 'returnFields');
if (this.get('options').get('highlighting')) {
result.hl = true;
result['hl.fl'] = ['content', 'title'].concat(constructor.extraHighlightFields);
}
if (this.get('options').get('faceting')) {
var facetFormData = facets.getFacetFormData();
var fq = facetFormData.fq;
delete(facetFormData.fq);
result = _.extend(result, facetFormData);
if (fq.length) {
if (result.fq) {
result.fq = result.fq.concat(fq);
} else {
result.fq = fq;
}
}
}
var queryParts = [];
var queryFields = this.get('queryFields');
_.each(queryFields.keys(), function(queryName) {
var subQueryModel = queryFields.get(queryName);
var fields = subQueryModel.get('fields');
var queries = subQueryModel.get('queries');
var query = subQueryModel.get('query');
if (query) {
queries = queries.concat([query]);
}
var subQuery = [];
for (var i = 0; i < fields.length; i++) {
var fieldName = fields[i];
for (var j = 0; j < queries.length; j++) {
// FIXME: quote the query
subQuery.push(fieldName + ':\'' + queries[j] + '\'');
}
}
if (subQuery.length) {
queryParts.push(subQuery.join(' OR '));
}
}, this);
var query = queryParts.join(' AND ');
if (!query) {
query = '*:*';
if (!this.get('options').get('showAll') && !result.fq) {
result.rows = 0;
}
}
var dateBoostField = constructor.dateBoostField;
var popularityBoostField = constructor.popularityBoostField;
if (result.rows !== 0 && (dateBoostField || popularityBoostField)) {
var boostSum = [];
if (dateBoostField) {
result.dateBoost = 'recip(ms(NOW,' + dateBoostField + '),3.16e-11,1,1)';
boostSum.push('$dateBoost');
}
if (popularityBoostField) {
result.popularityBoost = 'def(' + popularityBoostField + ',0)';
boostSum.push('$popularityBoost');
}
result.qq = query;
result.q = '{!boost b=sum(' + boostSum.join(',') + ') v=$qq defType=$defType}';
} else {
result.q = query;
}
return result;
},
}, {
cleanup: function cleanup(txt) {
if (txt) {
txt = txt.replace(/&amp;/g, '&');
txt = txt.replace(/&#39;/g, '\'');
txt = txt.replace(/[^a-zA-Z0-9 -\.,:;%<>\/'"|]/g, ' ');
}
return txt;
},
returnFields: [
'keywords',
'last_modified',
'path',
'score',
'title',
],
parsedFieldMap: {
content: function(doc, index, itemHighlighting) {
var content = itemHighlighting.content;
if (content && content.constructor === Array) {
content = content.join(' ');
}
if (!content) {
content = '';
}
return this.constructor.cleanup(content);
},
keywords: 'keywords',
lastModified: 'last_modified',
path: 'url',
score: 'score',
title: function(doc, index, itemHighlighting) {
var title;
if (itemHighlighting.title) {
title = itemHighlighting.title.join(' ');
} else {
title = doc.title[0];
}
return this.constructor.cleanup(title);
},
},
});
SolrSearch.Facets = SolrFacets;
SolrSearch.Pagination = Pagination;
return SolrSearch;
});
......
define(function(require) {
'use strict';
var Backbone = require('backbone');
var Ordering = Backbone.Model.extend({
defaults: {
value: null,
items: new Backbone.Collection(),
},
initialize: function(data, options) {
if (this.get('value') === null) {
var firstItem = this.get('items').at(0);
if (firstItem) {
this.set('value', firstItem.get('value'));
}
}
},
parse: function(data) {
var result = _.clone(data);
if (result.items && !(result.items instanceof Backbone.Collection)) {
result.items = new Backbone.Collection(result.items, {parse: true});
}
return result;
},
});
return Ordering;
});
define(function(require) {
'use strict';
var Backbone = require('backbone');
function stepFalse(e) {
e.preventDefault();
return false;
}
var Pagination = Backbone.Model.extend({
defaults: {
currentPage: 1,
hasNext: false,
hasNextJumpPage: false,
hasPrevious: false,
hasPreviousJumpPage: false,
nextJumpPage: undefined,
nextPage: undefined,
pages: [],
pageJump: 5,
pageSize: 10,
previousJumpPage: undefined,
previousPage: undefined,
totalCount: 0,
totalPages: 0,
},
initialize: function(data, options) {
var buildPages = function buildPages() {
var currentPage = parseInt(this.get('currentPage'));
var pageSize = parseInt(this.get('pageSize'));
var totalCount = parseInt(this.get('totalCount'));
if (!currentPage || !pageSize || !totalCount) {
return;
}
var pages = [];
var totalPages = Math.floor((totalCount + pageSize - 1) / pageSize);
function addPage(self, i) {
pages.push({
current: i === currentPage,
jump: function() {
self.set('currentPage', i);
return true;
},
number: i,
});
}
var startAt = currentPage - 4, endAt = currentPage + 5;
if (startAt < 1) {
endAt += (1 - startAt);
}
if (endAt > totalPages) {
startAt -= endAt - totalPages - 1;
endAt = totalPages + 1;
}
if (startAt < 1) {
startAt = 1;
}
if (endAt - startAt < 9) {
/* global console:false */
console.log('foo');
}
for (var i = startAt; i < endAt; i++) {
if (i > 0 && i <= totalPages) {
addPage(this, i);
}
}
var hasPrevious = currentPage > 1;
var hasNext = currentPage < totalPages;
var pageJump = this.get('pageJump');
var nextJumpPage, previousJumpPage, hasNextJump, hasPreviousJump;
if (pageJump) {
nextJumpPage = currentPage + pageJump;
previousJumpPage = currentPage - pageJump;
hasNextJump = nextJumpPage < totalPages;
hasPreviousJump = previousJumpPage > 0;
} else {
hasNextJump = false;
hasPreviousJump = false;
}
this.set({
hasNext: hasNext,
hasNextJump: hasNextJump,
hasPrevious: hasPrevious,
hasPreviousJump: hasPreviousJump,
nextJumpPage: hasNextJump ? nextJumpPage : undefined,
nextPage: hasNext ? currentPage + 1 : undefined,
pages: pages,
previousJumpPage: hasNextJump ? previousJumpPage : undefined,
previousPage: hasPrevious ? currentPage - 1 : undefined,
totalPages: totalPages,
});
};
this.on('change:pageSize change:currentPage change:totalCount', buildPages, this);
var installActions = _.bind(function installActions(eventName, nextName, hasNextName, previousName, hasPreviousName, pageCount) {
this.on(eventName, function() {
var hasNext = this.get(hasNextName);
var hasPrevious = this.get(hasPreviousName);
var next, previous;
if (hasNext) {
next = _.bind(function(e) {
e.preventDefault();
this.set('currentPage', this.get('currentPage') + pageCount);
return false;
}, this);
} else {
next = stepFalse;
}
if (hasPrevious) {
previous = _.bind(function(e) {
e.preventDefault();
this.set('currentPage', this.get('currentPage') - pageCount);
return false;
}, this);
} else {
previous = stepFalse;
}
this.set(nextName, next);
this.set(previousName, previous);
}, this);
this[nextName] = _.bind(function() {
return this.get(nextName)();
}, this);
this[previousName] = _.bind(function() {
return this.get(previousName)();
}, this);
}, this);
this.on('change:pageJump', function() {
var pageJump = this.get('pageJump');
installActions('change:hasNextJump change:hasPreviousJump', 'nextJump', 'hasNextJump', 'previousJump', 'hasPreviousJump', pageJump);
}, this);
installActions('change:hasNext change:hasPrevious', 'next', 'hasNext', 'previous', 'hasPrevious', 1);
buildPages.apply(this);
this.trigger('change:pageJump');
return Pagination.__super__.initialize.apply(this, arguments);
}
});
return Pagination;
});
define(function(require) {
'use strict';
var Backbone = require('backbone');
var QueryTextField = Backbone.Model.extend({
defaults: {
formName: null,
name: null,
queries: [],
fields: null,
multi: false
},
});
return QueryTextField;
});
define(function(require) {
'use strict';
var _ = require('underscore');
var Backbone = require('backbone');
var Facets = require('solr/model/Facets');
var Ordering = require('solr/model/Ordering');
var Pagination = require('solr/model/Pagination');
var QueryTextField = require('solr/model/QueryTextField');
//var module = require('module');
function mergeStatic(startPtr, endPtr, obj, fieldName, filterFunc) {
var result = filterFunc(obj);
var ptr = startPtr;
while (true) {
result = _.extend(result, filterFunc(_.result(ptr, fieldName)));
if (ptr === endPtr) {
break;
}
ptr = ptr.__super__.constructor;
}
return result;
}
function mergeStaticProps(startPtr, endPtr, obj, fieldName) {
return mergeStaticFunc(startPtr, endPtr, obj, fieldName, _.identity);
}
function mergeStaticSets(startPtr, endPtr, obj, fieldName) {
return _.keys(mergeStaticFunc(startPtr, endPtr, obj, fieldName, function arrayToMap(array) {
var result = {};
_.each(array, function(value) {
result[value] = true;
});
return result;
}));
}
function getItemKeyAccessor(item) {
return item.get('key');
}
var Solr = Pagination.extend({
url: function url() {
return this.constructor.selectUrl;
},
defaults: function defaults() {
var constructor = this.constructor;
var formNameMap = {};
var facets = new Facets(this.constructor.facets, {search: this});
_.each(facets.values(), function(facet) {
var formName = facet.get('formName');
if (formName) {
formNameMap[formName] = facet;
}
});
var queryFields = new Backbone.Model();
_.each(constructor.queryTextFields, function(definition, queryName) {
var qtf = new QueryTextField({formName: definition.formName, name: queryName, queries: [], fields: definition.fields, multi: !!definition.multi});
var formName = qtf.get('formName');
if (formName) {
formNameMap[formName] = qtf;
}
queryFields.set(queryName, qtf);
}, this);
return _.extend(_.result(Solr.__super__, 'defaults'), {
initializing: true,
initialized: false,
query: '',
formNameMap: formNameMap,
results: new Backbone.Collection(),
ordering: new Ordering({items: constructor.orderingItems}, {parse: true}),
facets: facets,
queryFields: queryFields,
});
},
applyQueryParameters: function() {
var skipOptions = {skipSearch: true};
var parts = document.location.href.match(/.*\?(.*)/);
var facets = this.get('facets');
facets.resetSearch();
_.each(this.get('queryFields').values(), function(qtf) {
qtf.set({
query: null,
queries: [],
}, skipOptions);
});
if (parts) {
var formNameMap = this.get('formNameMap');
var keyValueParts = parts[1].split('&');
_.each(keyValueParts, function(keyValuePart, i) {
var keyFieldValue = keyValuePart.match(/^([^.]+)(?:\.([^.]+))?=(.*)$/);
if (keyFieldValue) {
var key = keyFieldValue[1];
var field = keyFieldValue[2];
var value = keyFieldValue[3];
value = value.replace(/(\+|%20)/g, ' ');
var impl = formNameMap[key];
if (impl) {
if (impl instanceof QueryTextField) {
impl.set('query', value, skipOptions);
} else if (impl.facetType === 'field' && value) {
if (field === 'min') {
impl.set('queryMin', parseInt(value), skipOptions);
} else if (field === 'max') {
impl.set('queryMax', parseInt(value), skipOptions);
} else if (field === 'items') {
var items = impl.get('items');
var item = items.get(value);
if (!item) {
item = new Facets.Facet.Item({key: value, value: 0});
items.add(item);
item.on('change:checked', function(model, value, options) {
this.trigger('item-change', null, null, options);
impl.trigger('item-change', null, null, options);
}, facets);
}
item.set('checked', true, skipOptions);
}
}
}
}
}, this);
}
},
initialize: function(data, options) {
options = (options || {});
this.set('options', new Backbone.Model({
faceting: !!options.faceting,
highlighting: !!options.highlighting,
showAll: !!options.showAll,
}));
this._doSearchImmediately = _.bind(function(options) {
this.fetch(_.extend({}, options, {
data: this.toJSON(),
merge: false,
traditional: true,
}));
}, this);
this._doSearch = _.debounce(this._doSearchImmediately, 250);
this._doSearch = (function(doSearch) {
return function(options) {
var skipSearch = this._skipSearch || (options || {}).skipSearch;
if (!skipSearch) {
doSearch(options);
}
};
})(this._doSearch);
_.each(this.get('queryFields').values(), function(subQueryModel) {
subQueryModel.on('change:query change:queries', function(model, value, options) {
this.trigger('change:queryFields', null, null, options);
}, this);
}, this);
this.on('change:queryFields', function(model, value, options) {
this.trigger('change:query', null, null, options);
}, this);
this.get('options').on('change:faceting change:highlighting', function(model, value, options) {
this._doSearch(options);
}, this);
this.on('change:query', function(model, value, options) {
// this is silent, which causes the change events to not fire;
// this is ok, because the next .on will also see the change
// event on query, and resend the search
this._doSearch(_.extend({}, options, {resetCurrentPage: true, resetFacets: true, resetOrdering: true}));
}, this);
this.on('change:pageSize', function(model, value, options) {
this._doSearch(_.extend({}, options, {resetCurrentPage: true}));
}, this);
this.on('change:currentPage', function(model, value, options) {
this._doSearch(options);
}, this);
this.get('facets').on('item-change', function(model, value, options) {
this._doSearch(_.extend({}, options, {resetCurrentPage: true}));
}, this);
this.get('ordering').on('change:value', function(model, value, options) {
this._doSearch(_.extend({}, options, {resetCurrentPage: true}));
}, this);
return Solr.__super__.initialize.apply(this, arguments);
},
parse: function parse(data, options) {
if (options.facetSuggestions) {
return null;
}
var skipOptions = _.extend({}, options, {skipSearch: true});
if (options.resetCurrentPage) {
this.set('currentPage', 1, skipOptions);
}
if (options.resetFacets) {
this.get('facets').resetSearch(skipOptions);
}
if (options.resetOrdering) {
this.get('ordering').set('value', this.get('ordering').get('items').at(0).get('value'), skipOptions);
}
var facets = this.get('facets');
if (this.get('options').get('faceting')) {
facets.applyFacetResults(data);
} else {
facets.resetSearch(options);
}
var list = [];
var highlighting = this.get('options').get('highlighting') ? data.highlighting : {};
var self = this;
_.each(data.response.docs, function(doc, index) {
var itemHighlighting = highlighting[doc.id];
if (!itemHighlighting) {
itemHighlighting = {};
}
list.push(self.searchParseDoc(doc, index, itemHighlighting));
});
return {
initializing: false,
initialized: true,
results: new Backbone.Collection(list),
facets: facets,
options: this.get('options'),
totalCount: data.response.numFound,
queryTime: (data.responseHeader.QTime / 1000).toFixed(2),
hasResults: list.length > 0,
};
},
searchParseDoc: function searchParseDoc(doc, index, itemHighlighting) {
var fieldsToParse = mergeStaticProps(this.constructor, Solr, {}, 'parsedFieldMap');
var result = {};
_.each(fieldsToParse, function(value, key) {
var parsed;
if (_.isFunction(value)) {
parsed = value.call(this, doc, index, itemHighlighting, key);
} else if (_.isArray(value)) {
parsed = [];
_.each(value, function(item, i) {
var rawValue = doc[item.name];
if (rawValue) {
var parsedValue = item.parse.call(this, doc, index, itemHighlighting, rawValue, item.name);
if (parsedValue) {
parsed.push(parsedValue);
}
}
}, this);
} else {
parsed = doc[value];
}
result[key] = parsed;
}, this);
return result;
},
toJSON: function(options) {
if (!options) {
options = {};
}
var facets = this.get('facets');
var constructor = this.constructor;
var result = {
defType: 'edismax',
qf: constructor.queryField,
wt: 'json',
};
var ordering = this.get('ordering');
var sort = ordering.get('value');
if (options.resetOrdering) {
sort = ordering.get('items').at(0).get('value');
}
var currentPage = this.get('currentPage');
var pageSize = this.get('pageSize');
if (options.resetCurrentPage) {
currentPage = 1;
}
result.sort = sort;
result.rows = pageSize;
result.start = (currentPage - 1) * pageSize;
result.fl = mergeStaticSets(constructor, Solr, [], 'returnFields');
if (this.get('options').get('highlighting')) {
result.hl = true;
result['hl.fl'] = ['content', 'title'].concat(constructor.extraHighlightFields);
}
if (this.get('options').get('faceting')) {
var facetFormData = facets.getFacetFormData();
var fq = facetFormData.fq;
delete(facetFormData.fq);
result = _.extend(result, facetFormData);
if (fq.length) {
if (result.fq) {
result.fq = result.fq.concat(fq);
} else {
result.fq = fq;
}
}
}
var queryParts = [];
var queryFields = this.get('queryFields');
_.each(queryFields.keys(), function(queryName) {
var subQueryModel = queryFields.get(queryName);
var fields = subQueryModel.get('fields');
var queries = subQueryModel.get('queries');
var query = subQueryModel.get('query');
if (query) {
queries = queries.concat([query]);
}
var subQuery = [];
for (var i = 0; i < fields.length; i++) {
var fieldName = fields[i];
for (var j = 0; j < queries.length; j++) {
// FIXME: quote the query
subQuery.push(fieldName + ':\'' + queries[j] + '\'');
}
}
if (subQuery.length) {
queryParts.push(subQuery.join(' OR '));
}
}, this);
var query = queryParts.join(' AND ');
if (!query) {
query = '*:*';
if (!this.get('options').get('showAll') && !result.fq) {
result.rows = 0;
}
}
var dateBoostField = constructor.dateBoostField;
var popularityBoostField = constructor.popularityBoostField;
if (result.rows !== 0 && (dateBoostField || popularityBoostField)) {
var boostSum = [];
if (dateBoostField) {
result.dateBoost = 'recip(ms(NOW,' + dateBoostField + '),3.16e-11,1,1)';
boostSum.push('$dateBoost');
}
if (popularityBoostField) {
result.popularityBoost = 'def(' + popularityBoostField + ',0)';
boostSum.push('$popularityBoost');
}
result.qq = query;
result.q = '{!boost b=sum(' + boostSum.join(',') + ') v=$qq defType=$defType}';
} else {
result.q = query;
}
return result;
},
}, {
cleanup: function cleanup(txt) {
if (txt) {
txt = txt.replace(/&amp;/g, '&');
txt = txt.replace(/&#39;/g, '\'');
txt = txt.replace(/[^a-zA-Z0-9 -\.,:;%<>\/'"|]/g, ' ');
}
return txt;
},
returnFields: [
'keywords',
'last_modified',
'path',
'score',
'title',
],
parsedFieldMap: {
content: function(doc, index, itemHighlighting) {
var content = itemHighlighting.content;
if (content && content.constructor === Array) {
content = content.join(' ');
}
if (!content) {
content = '';
}
return this.constructor.cleanup(content);
},
keywords: 'keywords',
lastModified: 'last_modified',
path: 'url',
score: 'score',
title: function(doc, index, itemHighlighting) {
var title;
if (itemHighlighting.title) {
title = itemHighlighting.title.join(' ');
} else {
title = doc.title[0];
}
return this.constructor.cleanup(title);
},
},
});
Solr.Facets = Facets;
Solr.Pagination = Pagination;
return Solr;
});