define(["jquery", "underscore", "backbone", "models/filters/Filter", "models/filters/BooleanFilter",
"models/filters/ChoiceFilter", "models/filters/DateFilter", "models/filters/NumericFilter",
"models/filters/ToggleFilter"],
function($, _, Backbone, Filter, BooleanFilter, ChoiceFilter, DateFilter, NumericFilter, ToggleFilter) {
"use strict";
/**
* @class Filters
* @classdesc A collection of Filter models that represents a full search
* @name Filters
* @extends Backbone.Collection
* @constructor
*/
var Filters = Backbone.Collection.extend(
/** @lends Filters.prototype */{
/**
* Is executed when a new Filters collection is created
*/
initialize: function(models, options) {
if (typeof options === "undefined") {
var options = {};
}
if (options.catalogSearch) {
this.createCatalogFilters();
}
},
/**
* Creates the type of Filter Model based on the given filter type. This
* function is typically not called directly. It is used by Backbone.js when adding
* a new model to the collection.
* @param {object} attrs - A literal object that contains the attributes to pass to the model
* @property {string} attrs.filterType - The type of Filter to create
* @param {object} options - A literal object of additional options to pass to the model
* @returns {Filter|BooleanFilter|ChoiceFilter|DateFilter|NumericFilter|ToggleFilter}
*/
model: function(attrs, options){
//If no filterType was specified, but an objectDOM exists (from parsing a Collection
// or Portal document), get the filter type from the objectDOM node name
if( !attrs.filterType && attrs.objectDOM ){
switch( attrs.objectDOM.nodeName ){
case "booleanFilter":
return new BooleanFilter(attrs, options);
case "dateFilter":
return new DateFilter(attrs, options);
case "numericFilter":
return new NumericFilter(attrs, options);
default:
return new Filter(attrs, options);
}
}
switch ( attrs.filterType ) {
case "BooleanFilter":
return new BooleanFilter(attrs, options);
case "ChoiceFilter":
return new ChoiceFilter(attrs, options);
case "DateFilter":
return new DateFilter(attrs, options);
case "NumericFilter":
return new NumericFilter(attrs, options);
case "ToggleFilter":
return new ToggleFilter(attrs, options);
default:
return new Filter(attrs, options);
}
},
/**
* Builds the query string to send to the query engine. Iterates over each filter
* in the collection and adds to the query string.
*
* @return {string} The query string to send to Solr
*/
getQuery: function() {
//Create an array to store all the query pieces
var allGroupsQueryFragments = [],
//The complete query string that eventually gets returned
completeQuery = "",
//Get the list of filters that use the 'id' field, since these are used differently
idFilters = this.filter(function(filter){
return filter.get("fields").includes("id");
}),
otherFilters = this.difference(idFilters),
//Separate the filter models in this collection by their query group.
groupedFilters = _.groupBy(otherFilters, function(m){
return m.get("queryGroup");
});
//Filters that are used in the data catalog are treated specially
var catalogFilters = groupedFilters.catalog;
delete groupedFilters.catalog;
//Create a query string for each group of filters
_.mapObject(groupedFilters, function(filterModels, groupName) {
//Get a query string for this group of Filters
var groupQuery = this.getGroupQuery(filterModels);
//If there is a query string, add it to the array
if( groupQuery ){
allGroupsQueryFragments.push(groupQuery);
}
}, this);
//Join the query fragments with an OR. By default, Filter model groups are ORed together
if( allGroupsQueryFragments.length ){
completeQuery += "(" + allGroupsQueryFragments.join("%20OR%20") + ")";
}
//Add the Data Catalog filters, if there are any
if( Array.isArray(catalogFilters) && catalogFilters.length ){
//If there are other filters besides the catalog filters, AND them
if( completeQuery.trim().length ){
completeQuery += "%20AND%20";
}
//Get the query string for the catalog filters
completeQuery += this.getGroupQuery(catalogFilters);
}
//Create the grouped query for the id filters
var idFilterQuery = this.getGroupQuery(idFilters, "OR");
//Add the grouped query for the id filters
if( completeQuery.length && idFilterQuery.length ){
completeQuery = "(" + completeQuery + ")%20OR%20" + idFilterQuery;
}
//If the query is ONLY made of id filters, then the id filter query is the complete query
else if( !completeQuery.length && idFilterQuery.length ){
completeQuery = idFilterQuery;
}
//Return the completed query
return completeQuery;
},
/**
* Get a query string for a group of Filters.
* The Filters will be ANDed together, unless a different operator is given.
* @param {Filter[]} filterModels - The Filters to turn into a query string
* @param {string} [operator] - The oeprator to use between filter models
* @return {string} The query string
*/
getGroupQuery: function(filterModels, operator){
//Default to the AND operator
if(typeof operator != "string"){
var operator = "AND";
}
//Start an array to contian the query fragments
var groupQueryFragments = [];
//For each Filter in this group, get the query string
_.each(filterModels, function(filterModel){
//Get the Solr query string from this model
var filterQuery = filterModel.getQuery();
//Add the filter query string to the overall array
if ( filterQuery && filterQuery.length > 0 ) {
groupQueryFragments.push(filterQuery);
}
}, this);
//Join this group's query fragments with an OR operator
if( groupQueryFragments.length ){
return "(" + groupQueryFragments.join("%20" + operator + "%20") + ")"
}
//Otherwise, return an empty string
else{
return "";
}
},
/**
* Given a Solr field name, determines if that field is set as a filter option
*/
filterIsAvailable: function(field) {
var matchingFilter = this.find(function(filterModel) {
return _.contains(filterModel.fields, field);
});
if (matchingFilter) {
return true;
} else {
return false;
}
},
/*
* Returns an array of filter models in this collection that have a value set
*
* @return {Array} - an array of filter models in this collection that have a value set
*/
getCurrentFilters: function() {
var currentFilters = new Array();
this.each(function(filterModel) {
//If the filter model has values set differently than the default AND it is
// not an invisible filter, then add it to the current filters array
if (!filterModel.get("isInvisible") &&
((Array.isArray(filterModel.get("values")) && filterModel.get("values").length &&
_.difference(filterModel.get("values"), filterModel.defaults().values).length) ||
(!Array.isArray(filterModel.get("values")) && filterModel.get("values") !== filterModel.defaults().values))
) {
currentFilters.push(filterModel);
}
});
return currentFilters;
},
/*
* Clear the values of all geohash-related models in the collection
*/
resetGeohash: function() {
//Find all the filters in this collection that are related to geohashes
this.each(function(filterModel) {
if (!filterModel.get("isInvisible") &&
_.intersection(filterModel.fields, ["geohashes", "geohashLevel", "geohashGroups"]).length) {
filterModel.resetValue();
}
});
},
/*
* Creates and adds FilterModels to this collection that are standard filters
* to be sent with every Data Catalog query.
*/
createCatalogFilters: function() {
//Exclude obsoleted objects from the search
this.add(new Filter({
fields: ["obsoletedBy"],
values: ["*"],
exclude: true,
isInvisible: true,
queryGroup: "catalog"
}));
//Only search for metadata objects
this.add(new Filter({
fields: ["formatType"],
values: ["METADATA"],
matchSubstring: false,
isInvisible: true,
queryGroup: "catalog"
}));
},
/**
* Removes Filter models from this collection if they match the given field
* @param {string} field - The field whose matching filters that should be removed from this collection
*/
removeFiltersByField: function(field){
var toRemove = [];
this.each(function(filter){
if(filter.get("fields").includes(field)){
toRemove.push(filter);
}
});
this.remove(toRemove);
}
/*
hasGeohashFilter: function() {
var currentFilters = this.getCurrentFilters();
var geohashFilter = _.find(currentFilters, function(filterModel){
return (_.intersection(filterModel.get("fields"),
["geohashes", "geohash"]).length > 0);
});
if(geohashFilter) {
return true;
} else {
return false;
}
}
*/
});
return Filters;
});