UNPKG

built.io

Version:

SDK for Built.io Backend

1,764 lines 61 kB
var R                     = require('ramda');
var utility               = require('./utilities/utility');
var objectCons            = require('./object');
var instanceMethodBuilder = require('./utilities/instanceMethodBuilder')();
var md5                   = require("blueimp-md5").md5;
var when                  = require('when');
var Built                 = require('./built');
var node                  = require("./node")

/*
	Transforms that can be applied on query response
*/
var SDK_TRANSFORM      = 'SDK_TRANSFORM';
var BACKBONE_TRANSFORM = 'BACKBONE_TRANSFORM';
var JSON_TRANSFORM     = 'JSON_TRANSFORM';


/**
* @class Query
* @classdesc 
*   A class that defines a query on Built.io Backend
* @instance
* @description 
*   Constructs a query with initial query conditions 
* @param {object} conditions Plain JavaScript object specifying conditions
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query
* .where('username','John')
* .exec()
* .then(function(objects){
*    // objects having username field with value 'John'
* });
* @return {Query} 
*/
var queryCons = module.exports = R.curry(function(app,cls,headers,cachePolicy,data) {
	var returnObj = {
		app: app,
		cls: cls,
		headers: headers,
		cachePolicy:cachePolicy,
		data: data || {}
	}
	return instanceMethodBuilder.build(module.exports,returnObj);
})

var getConditions =  module.exports.getConditions = R.prop('data');
instanceMethodBuilder.define('getConditions',1);

var getQueryObj = module.exports.getQueryObj =  R.compose(R.prop('query'),getConditions)
instanceMethodBuilder.define('getQueryObj',1);

module.exports.getTransform =  R.compose(R.prop('transform'),R.prop('data'));
instanceMethodBuilder.define('getTransform',1);

/**
* Returns the result as an array of plain JavaScript objects
* @function toJSON
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query
* .toJSON()
* .exec()
* .then(function(objects){
*    // array of plain javascript objects
*    console.log(objects); 
* });
* @return {Query}
*/
module.exports.toJSON = function(query){
	var newConds          = R.mixin({},getConditions(query));
	newConds['transform'] = JSON_TRANSFORM;
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);
}
instanceMethodBuilder.define('toJSON',1);

/**
* Returns the result as a Backbone collection
* @function toBackbone
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query
* .toBackbone()
* .exec()
* .then(function(res){
*    console.log(res); // Backbone collection
* });
* @return {Query}
*/
module.exports.toBackbone = function(query){
	var newConds          = R.mixin({},getConditions(query));
	newConds['transform'] = BACKBONE_TRANSFORM;
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);
}
instanceMethodBuilder.define('toBackbone',1);
/**
* Set a header in query
* @function setHeader
* @param {String} header The header key
* @param {String} value The header value
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query = query.setHeader('My-Custom-Header','MyValue');
* @return {Query}
*/
var setHeader = module.exports.setHeader = R.curry(function(header,value,query) { //setHeader
	var existingHeaders = query.headers;
	var newHeader       = R.mixin({},existingHeaders);
	newHeader[header]   = value;
	return queryCons(query.app,query.cls,newHeader,query.cachePolicy,getConditions(query));
});
instanceMethodBuilder.define('setHeader',3);

/**
* Removes a header from a query
* @function removeHeader
* @param {String} header The header that needs to be removed
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query = query.removeHeader('My-Custom-Header');
* @return {Query}
*/

var removeHeader      = module.exports.removeHeader = R.curry(function(header,query) {
	var existingHeaders = query.header;
	var newHeader       = R.mixin({},existingHeaders);
	delete newHeader[header];
	return queryCons(query.app,query.cls,newHeader,query.cachePolicy,getConditions(query));
});
instanceMethodBuilder.define('removeHeader',2);
/**
* Sets the master key for a query 
* @function setMasterKey
* @param {String} masterKey The master key value
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query = query.setMasterKey('blt111sample24a6b');  
* @return {Query}
*/
module.exports.setMasterKey = setHeader('master_key')
instanceMethodBuilder.define('setMasterKey',2);
/**
* Removes the master key from Query
* @function removeMasterKey  
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query = query.removeMasterKey(); 
* @return {Query}
 */

module.exports.removeMasterKey = removeHeader('master_key');
instanceMethodBuilder.define('removeMasterKey',1);
/**
* Gets the header object for this query
* @function getHeaders  
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* var headers = query.getHeaders();
* @return {object}
*/
module.exports.getHeaders = function(query) {
	return query.headers;
}
instanceMethodBuilder.define('getHeaders',1);

module.exports.getCombinedHeaders = function(query) {
	var Cls             = require('./class');
	var clsHeaders      = Cls.getCombinedHeaders(query.cls);
	var combinedHeaders = R.mixin(clsHeaders.headers,query.headers)
	return combinedHeaders;
}
instanceMethodBuilder.define('getCombinedHeaders',1);

var existsHelper = R.curry(function(bool,field_uid,query){
	var newConds   = R.mixin({},getConditions(query));
	var newQuery   = R.mixin({},getQueryObj(query));
	newQuery[field_uid] = {
		$exists : bool
	};
	newConds.query = newQuery; 
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);
});

/**
* Gets all the object in which a value exists for a given field. i.e. Any object which has a value of 'undefined' for that field is filtered.
* @function exists
* @param {String} field_uid The field whose value is to be checked
* @memberof Query
* @instance
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* // Fetches all the objects that have have a value for "name" field
* query
* .exists('name');
* .exec()
* .then(function(objects){
*    console.log(objects[0].toJSON())
* });
* @return {Query}
*/
module.exports.exists = existsHelper(true);
instanceMethodBuilder.define('exists',2);

/**
* Gets all the object in which a value does not exists for a given field. i.e. Any object which has a value other than 'undefined' for that field is rejected.
* @function doesNotExists
* @param {String} field_uid The field whose value is to be checked
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* // Fetches all the objects that don't have a value for "name" field
* query = query.doesNotExists('name');
* query.exec()
* .then(function(objects){
* });
* @return {Query}
*/
module.exports.doesNotExists = existsHelper(false);
instanceMethodBuilder.define('doesNotExists',2);

var selectionHelper = R.curry(function(selectString,field_uid,queryObj,pickFieldUid,query){
	var newConds   = R.mixin({},getConditions(query));
	var newQuery   = R.mixin({},getQueryObj(query));
	newQuery[field_uid] = utility.keyValue(selectString,{
			query     : getConditions(queryObj).query,
			class_uid : queryObj.cls.uid,
			key       : pickFieldUid
	});
	newConds.query = newQuery;
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);  
});

/**
* Fires a select query on another class and gets objects that pass the query conditions. Similar to 'Joins' in relational databases like SQL.
* @function select
* @param {String} primaryKey Field in base class which should be used in join.
* @param {Query} query Query to be fired on the referred class.
* @param {String} foriegnKey Field in referred class which should be used in join.
* @instance
* @memberof Query
* @example
* // Example statement: Consider a class Person which has a reference field 'location', which refers to 'Address' class.
* // You should execute the below given query to retrieve all persons who live in 'Mumbai';
*   
* //First lets create a query which retrieves all objects of 'Address' class having 'city' as 'Mumbai'.
*	
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend
* var queryOnAddressCls = Built.App('blt5d4sample2633b').Class('address').Query()
*     .where('city', 'Mumbai');
*  
* // Now we can create a select query for retriving all persons from 'Person' class whoses location is 'Mumbai'.
*
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* // 'location' field contains uid of the address object whose city field is Mumbai
* .select('location', queryOnAddressCls, 'uid')  
* .exec()
* .then(function(objects){
*    // Fetches all person who live in Mumbai.
* });
* @return {Query}
*/
module.exports.select = selectionHelper('$select');
instanceMethodBuilder.define('select',4);

/**
* Fires a select query on another class and gets objects that do not pass the query conditions. Similar to 'Joins' in relational databases like SQL.
* @function dontSelect
* @param {String} primaryKey Field in base class which should be used in join.
* @param {Query} query Query to be fired on the referred class.
* @param {String} foriegnKey Field in referred class which should be used in join.
* @instance
* @memberof Query
* @example
* // Example statement: Consider a class Person which has a reference field 'location', which refers to 'Address' class.
* // You should execute the below given query to retrieve all persons who do not live in 'Mumbai';
*
* // First lets create a query which retrieves all objects of 'Address' class having 'city' as 'Mumbai'.
*	
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend
* var queryOnAddressCls = Built.App('blt5d4sample2633b').Class('address').Query()
*	                 .where('city', 'Mumbai');
*
* // Now we can create a select query for retriving all persons from 'Person' class whoses location is not 'Mumbai'.
*
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* //'location' field contains uid of the address object whose city field anything other than Mumbai.
* .dontSelect('location', queryOnAddressCls, 'uid')  
* .exec()
* .then(function(objects){
*    // Fetches all person who don't live in Mumbai.
* });
* @return {Query}
*/
module.exports.dontSelect = selectionHelper('$dont_select');
instanceMethodBuilder.define('dontSelect',4);

/**
* Gets the objects that match the where condition
* @function where
* @param {String} key The key
* @param {String} value The value
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query  = query.where('name','Super Project #41!');
* query
* .exec()
* .then(function(objects){
*   //fetches all the objects whose name is 'Super Project #41!' 
* });
* @return {Query}
*/
module.exports.where  = R.curry(function(key,value,query) {
	var newConds   = R.mixin({},getConditions(query));
	var newQuery   = R.mixin({},getQueryObj(query));
	// to make it like mongoSDK
	if(utility.isPlainObject(key)){
		newQuery = R.mixin(newQuery, key)
	}
	else{
		newQuery[key]  = value;
	}

	newConds.query = newQuery; 
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);
})
instanceMethodBuilder.define('where',3, [null]);
/**
* Custom query parameter can be sent
* @function addQueryParams
* @param {String} key Query parameter name.
* @param {String} value Query parameter value.
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* var query = query.addQueryParams('Custom_Query_Parameter','Custom_Value');
* query.exec()
* .then(function(objects){
*    // Objects that pass the query condtion
* });
* @instance
* @memberof Query
* @return {Query}
*/
var addQueryParams = module.exports.addQueryParams = R.curry(function(key,value,query){
	var newConds  = R.mixin({},getConditions(query));
	newConds[key] = value;
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);
});
instanceMethodBuilder.define('addQueryParams',3);

/**
* Includes the count of the number of objects returned along with the actual objects in response
* @function includeCount
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query = query.includeCount();
* @return {Query}
*/
module.exports.includeCount = addQueryParams('include_count',true);
instanceMethodBuilder.define('includeCount',1);
/**
* Gets only the count of the number of objects that pass the query condition
* @function count
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query = query.count();
* query.exec()
* .then(function(count){
*    // count of the number of objects fetched
* })  
* @return {Query}
*/
module.exports.count = addQueryParams('count',true);
instanceMethodBuilder.define('count',1);

/**
* By default, when a object is fetched referenced object's uids are returned in response. Using this method you can
* retrieve the reference object itself.
* @function include
* @param {Array} field_Uids Array of reference field uids whose actual referenced object is to be included
* @instance
* @memberof Query  
* @example
* //<b> Consider a Bugs class has a reference field "project" which is referring to Project class.</b>
* //<b> This function would include all objects that are referred by "project" field</b>
*
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'bugs' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('bugs').Query();
* query = query.include(['project']);
* @return {Query}
*/
module.exports.include = addQueryParams('include');
instanceMethodBuilder.define('include',2);
/**
* Skip 'n' number of objects
* @function skip
* @param {Number} number The number of objects to be skipped
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query = query.skip(5);
* @return {Query}
*/
module.exports.skip = addQueryParams('skip');
instanceMethodBuilder.define('skip',2);
/**
* Limits the response to contain only 'n' number of objects
* @function limit
* @param {Number} number The number of objects to be limited
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* // Would return only five objects even if more than five objects pass the condition
* query = query.limit(5);       
* @return {Query}
*/
module.exports.limit = addQueryParams('limit');
instanceMethodBuilder.define('limit',2);

/**
* Includes owner information for each object in the response
* @function includeOwner
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query = query.includeOwner();       
* @return {Query}
*/
module.exports.includeOwner = addQueryParams('include_owner',true);
instanceMethodBuilder.define('includeOwner',1);

/**
* Include the schema in response
* @function includeSchema
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query = query.includeSchema();       
* @return {Query}
*/
module.exports.includeSchema = addQueryParams('include_schema',true);
instanceMethodBuilder.define('includeSchema',1);

/**
* Includes all unpublished objects of a class
* @function includeDrafts
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query = query.includeDrafts();
* @return {Query}
*/
module.exports.includeDrafts = addQueryParams('include_unpublished',true);
instanceMethodBuilder.define('includeDrafts',1);

/**
* Includes only unpublished objects of a class
* @function onlyDrafts
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backend 
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query = query.onlyDrafts();
* @return {Query}
*/
module.exports.onlyDrafts = function(query){
	var incDraftQuery     = query.includeDrafts();
	var newConds          = R.mixin({},getConditions(incDraftQuery));
	var newQuery          = R.mixin({},getQueryObj(incDraftQuery));
	newQuery['published'] = false;
	newConds.query = newQuery; 
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);
}
instanceMethodBuilder.define('onlyDrafts',1);

var inQueryHelper = R.curry(function(operator,field_uid,queryObj,query){
	var newConds        = R.mixin({},getConditions(query));
	var newQuery        = R.mixin({},getQueryObj(query));
	newQuery[field_uid] = utility.keyValue(operator,getQueryObj(queryObj));  //eg {"$in_query": {"author": "James"}}
	newConds.query = newQuery; 
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);
});

/**
* Applies query on the referenced class' objects and fetches all objects from the referring(base) class which referred to these filtered out referenced objects
* @function inQuery
* @param {String} field_uid The uid of reference field
* @param {Query} queryObj The query that should be applied on the referenced class' objects
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query returns a 'Query' constructor
* // 'project' is a uid of a class on Built.io Backend 
* // 'bugs' is a uid of a class on Built.io Backed
*
* // Consider class Bugs has a reference field 'project' which refers the Project class
*
* var app = Built.App('blt5d4sample2633b');
* var query = app.Class('bugs').Query();
*
* // Query which would be applied on Project class
* var whereQuery = query.where('name','Super Project #41!');          
*
* var inQuery = query.inQuery('project', whereQuery);  
* inQuery
* .exec()
* .then(function(res){
*    //Returns all objects from Bugs class whose 'project' field references to Project class objects that have name as 'Super Project #41!'
* });
* @instance
* @memberof Query
* @return {Query}
*/
module.exports.inQuery = inQueryHelper('$in_query');
instanceMethodBuilder.define('inQuery',3);

/**
* Applies query conditions on referenced class' objects and fetches all objects from the referring class that did not refer to these objects
* @function notInQuery
* @param {String} field_uid The uid of reference field
* @param {Query} queryObj Query object containing the query 
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query returns a 'Query' constructor
* // 'bugs' is a uid of a class on Built.io Backed
* // Consider class Bugs has a reference field 'project' which refers the Project class
*   
* var app = Built.App('blt5d4sample2633b');
* var query = app.Class('project').Query();           
* 
* // Query which would be applied on Project class
* var whereQuery = query.where('name','Super Project #41!');          
* 
* var inQuery = query.notInQuery('project',whereQuery);  
* inQuery.exec().then(function(res){
*    //Returns all objects from Bugs class whose 'project' field references to Project class objects that don't have name as 'Super Project #41!'
* });
* @instance
* @memberof Query
* @return {Query}
*/
module.exports.notInQuery = inQueryHelper('$nin_query');
instanceMethodBuilder.define('notInQuery',3);
/**
* Gets all the objects that match the regular expression
* @function matches
* @param {String} field_uid The field on which the regular expression is applied
* @param {String} regex The regular expression
* @param {String} options Options for regular expression (Optional Defaults to 'i')
* @instance
* @memberof Query
* @example 
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
*
* var app = Built.App('blt5d4sample2633b');
* var query = app.Class('project').Query();
* // Fetches all objects whose name start with 'abc'
* query = query.matches('name','^abc'); 
* @return {Query}
*/
module.exports.matches = R.curry(function(field_uid,regex,options,query){
	var newConds   = R.mixin({},getConditions(query));
	var newQuery   = R.mixin({},getQueryObj(query));
	newQuery[field_uid]  ={
		'$regex'  : regex,
		'$options':options
	}
	newConds.query = newQuery; 
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);
});
instanceMethodBuilder.define('matches',4,['i']);

/**
* Fetches all the objects that are within a specified location
* @function withinLocation
* @param {Array} locations Can be an array of Built.Location instances or an array of object uids whose location is to be used. (Atleast three locations are required)
* @instance
* @memberof Query
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
*
* var location1 = Built.Location(60,80);
* var location2 = Built.Location(41,90);
* var location3 = Built.Location(52,72);
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query = query.withinLocation([location1, location2, location3]);
* @return {Query}
*/
module.exports.withinLocation = R.curry(function(locations,query){
	var newConds   = R.mixin({},getConditions(query));
	var newQuery   = R.mixin({},getQueryObj(query));
	if(utility.isArray(locations)){
		if(locations.length < 3){
			throw new Error("Minimum 3 locations or objectUids required");
		}
		var withInLoc = locations.map(function(loc) {
			if (loc.data) {
				return [loc.toJSON().longitude, loc.toJSON().latitude];
			} else if(utility.isString(loc)) {
				return {object: loc}
			} else {
				throw new Error('Invalid parameter'+loc);
			}
		})
	}else{
		throw new Error('Locations should be a Array');
	}
	newQuery['$within'] = withInLoc;
	newConds.query = newQuery;
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);
});
instanceMethodBuilder.define('withinLocation',2);
/**
* Gets the objects that have its field value matching the contents of array
* @function containedIn
* @param {String} uid The field's uid
* @param {Array} array The array against which the field value will be compared
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query returns a 'Query' constructor
* // 'project' is a uid of a class on Built.io Backed
* var app = Built.App('blt5d4sample2633b');
* var Query = app.Class('project').Query;
* var query = Query();
* query = query.containedIn('severity',["Show Stopper","Critical"])
* query.exec()
* .then(function(query){
*    // Fetches all objects that have severity as "Show Stopper" or "Critical"
* });
* @return {Query}
*/

module.exports.containedIn = R.curry(function(key, array, query) {
	var newConds   = R.mixin({},getConditions(query));
	var newQuery   = R.mixin({},getQueryObj(query));
	if(!utility.isArray(array)){
		array = [array];
	}
	newQuery[key] = {
		"$in": array
	};
	newConds.query = newQuery; 
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);
})
instanceMethodBuilder.define('containedIn',3);

/**
* Gets the objects that don't have its field value matching the contents of array
* @function notContainedIn
* @param {String} uid The field's uid
* @param {Array} array The array against which the field value will be compared
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query returns a 'Query' constructor
* // 'project' is a uid of a class on Built.io Backed@example
* var app = Built.App('blt5d4sample2633b');
* var Query = app.Class('project').Query;
* var query = Query();
* query  = query.notContainedIn('severity',["Show Stopper","Critical"])
* query.exec()
* .then(function(query){
*    // Fetches all objects which don't have severity as "Show Stopper" or "Critical"
* });
* @return {Query}
*/
module.exports.notContainedIn = R.curry(function(uid,array,query) {
	var newConds   = R.mixin({},getConditions(query));
	var newQuery   = R.mixin({},getQueryObj(query));
	if(!utility.isArray(array)){
		array = [array];
	}
	newQuery[uid]  = {
		"$nin": array
	};
	newConds.query = newQuery; 
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);
})
instanceMethodBuilder.define('notContainedIn',3);

/**
* Gets the objects in ascending order
* @function ascending
* @param {String} uid Uid of the field based on which the ordering should be done
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query returns a 'Query' constructor
* // 'project' is a uid of a class on Built.io Backed
* var app = Built.App('blt5d4sample2633b');
* var Query = app.Class('project').Query;
* var query = Query();
* query = query.ascending('name'); 
* @return {Query}
*/
module.exports.ascending =  addQueryParams('asc');
instanceMethodBuilder.define('ascending',2);
/**
* Gets the objects in descending order
* @function descending
* @param {String} uid Uid of the field based on which the ordering should be done
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query returns a 'Query' constructor
* // 'project' is a uid of a class on Built.io Backed
* var app = Built.App('blt5d4sample2633b');
* var Query = app.Class('project').Query;
* var query = Query();
* query = query.descending('name'); 
* @return {Query}
*/
module.exports.descending = addQueryParams('desc');
instanceMethodBuilder.define('descending',2);

var andOrHelper = R.curry(function(str,queryArray,query){
	var newConds  = R.mixin({},getConditions(query));
	var newQuery  = R.mixin({},getQueryObj(query));
	newQuery[str] = [];  
	for (var i = 0; i < queryArray.length; i++) {
		var tempQuery = getConditions(queryArray[i]);
		 newQuery[str].push(tempQuery.query);
		 newConds 		= R.mixin(newConds, tempQuery)
	 };
	 newConds.query = newQuery; 
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);  
});

/**
* Combines all the queries together using AND operator
* @function and
* @param {Array} queryArray Array of query objects
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
* var app = Built.App('blt5d4sample2633b');
* var query = app.Class('project').Query();
* var query1 = query.where('name','Super Project #41!');
* var query2 = query.where('description','Small description');
* var queryArray = [query1,query2];
* var andQuery = query.and(queryArray);
* @instance
* @memberof Query
* @return {Query}
*/
module.exports.and = andOrHelper('$and');
instanceMethodBuilder.define('and',2);

/**
* Combines all the queries together using OR operator
* @function or
* @param {Array} queryArray Array of Query objects containing conditions
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
* var app = Built.App('blt5d4sample2633b');
* var query = app.Class('project').Query();
* var query1 = query.where('name','Super Project #41!');
* var query2 = query.where('description','Small description');
* var queryArray = [query1,query2];
* var orQuery = query.or(queryArray);
* @return {Query}
*/
module.exports.or = andOrHelper('$or');
instanceMethodBuilder.define('or',2);

var equalityHelper    = R.curry(function(operator,field_uid,value,query){
	var newConds        = R.mixin({},getConditions(query));
	var newQuery        = R.mixin({},getQueryObj(query));
	newQuery[field_uid] = utility.keyValue(operator,value);
	newConds.query      = newQuery;
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);  
});

/**
* Gets all the objects that have its field_uid's value greater than the given value
* @function greaterThan
* @param {String} field_uid The uid of the field that needs to be compared
* @param {Number} value A numeric value 
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'milestone' is a uid of a class on Built.io Backed
* var app = Built.App('blt5d4sample2633b');
* var query = app.Class('milestone').Query();
* query.greaterThan('end_date',new Date());
* @return {Query}
*/
module.exports.greaterThan = equalityHelper('$gt');
instanceMethodBuilder.define('greaterThan',3);

/**
* Gets all the objects that have its field_uid's value greater than or equal to the given value
* @function greaterThanOrEqualTo
* @param {String} field_uid The uid of the field that needs to be compared
* @param {Number} value A numeric value 
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'milestone' is a uid of a class on Built.io Backed
* var app = Built.App('blt5d4sample2633b');
* var query = app.Class('milestone').Query();
* query.greaterThanOrEqualTo('end_date',new Date());
* @return {Query}
*/
module.exports.greaterThanOrEqualTo = equalityHelper('$gte');
instanceMethodBuilder.define('greaterThanOrEqualTo',3);

/**
* Gets all the objects that have its field_uid's value less than the given value
* @function lessThan
* @param {String} field_uid The uid of the field that needs to be compared
* @param {Number} value A numeric value 
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'milestone' is a uid of a class on Built.io Backed
* var app = Built.App('blt5d4sample2633b');
* var query = app.Class('milestone').Query();
* query.lessThan('end_date',new Date());
* @return {Query}
*/
module.exports.lessThan = equalityHelper('$lt');
instanceMethodBuilder.define('lessThan',3);

/**
* Gets all the objects that have its field_uid's value less than or equal to the given value
* @function lessThanOrEqualTo
* @param {String} field_uid The uid of the field that needs to be compared
* @param {Number} value A numeric value 
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'milestone' is a uid of a class on Built.io Backed
* var app = Built.App('blt5d4sample2633b');
* var query = app.Class('milestone').Query();
* query.lessThanOrEqualTo('end_date',new Date());
* @return {Query}
*/
module.exports.lessThanOrEqualTo = equalityHelper('$lte');
instanceMethodBuilder.define('lessThanOrEqualTo',3);

/**
* Gets all the object that have its field_uid's value not equal to the given value
* @function notEqualTo
* @param {String} field_uid The uid of the field that needs to be compared
* @param {Number} value A numeric value 
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'milestone' is a uid of a class on Built.io Backed
* var app = Built.App('blt5d4sample2633b');
* var query = app.Class('milestone').Query();
* query.notEqualTo('end_date',new Date());
* @return {Query}
*/
module.exports.notEqualTo = equalityHelper('$ne');
instanceMethodBuilder.define('notEqualTo',3);

/**
* Sets the a cache policy for this query
* @function setCachePolicy
* @param {Number} value A numeric value 
* @throws new Error("Invalid cache policy");
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* query = query.setCachePolicy(Built.CachePolicy.CACHE_ELSE_NETWORK);
* @return {Query}
*/
module.exports.setCachePolicy = R.curry(function(cachePolicy,query){
	if(query.app.validateCachePolicy(cachePolicy))
		return queryCons(query.app,query.cls,query.headers,cachePolicy,getConditions(query));
	else
		throw new Error("Invalid cache policy");
});
instanceMethodBuilder.define('setCachePolicy',2);

var setInCache = R.curry(function (key,value){
	if(utility.isLocalStroageAvaliable())
	{
		try{
			localStorage.setItem(key,JSON.stringify(value))
		}
		catch(e){
			if(!e.stack)
				console.log("built.io sdk | Cache Error", e)
		}
	}
	return value;
});

var deltaHelper = R.curry(function(type,date,query){
	if(date instanceof Date)
		date = date.toISOString();
	var newConds      = R.mixin({},getConditions(query));
	var newDelta      = R.mixin({},newConds.delta);
	newDelta[type]    = date;
	newConds['delta'] = newDelta;
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds); 
});

/**
* Gets the objects that were created on or after the given date
* @function deltaCreatedFrom
* @param {Date|String} date Instance of Date or a string in the following format 'YYYY-MM-DD'
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
*
* //<b>Example demonstrate method accepting a formated string as date. </b>
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* var query = query.deltaCreatedFrom('2014-09-02');
* query.exec()
* .then(function(object)({
*    // object.created is array of all objects that 
*    // were created on or after the given date
* }));
*
* //<b>Example demonstrate method accepting a date instance.</b>
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* var query = query.deltaCreatedFrom(new Date());    
* query.exec()
* .then(function(object)({
*    // object.created is array of all objects that 
*    // were created on or after the given date
* }));
* @return {Query}
*/
var deltaCreatedFrom = module.exports.deltaCreatedFrom  = deltaHelper("created_at");
instanceMethodBuilder.define('deltaCreatedFrom',2);

/**
* Gets the objects that were updated on or after the given date
* @function deltaUpdatedFrom
* @param {Date|String} date Instance of date or a string in the following format 'YYYY-MM-DD'.
* @instance 
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
*
* //<b>Example demonstrate method accepting a formated string as date. </b>
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* var query = query.deltaUpdatedFrom('2014-09-02');
* query.exec()
* .then(function(object)({
*    // object.updated is array of all objects that were updated on
*    //or after the given date
* }));
*
* //<b>Example demonstrate method accepting a date instance.</b>
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* var query = query.deltaUpdatedFrom(new Date());
* query.exec()
* .then(function(object)({
*    // object.updated is array of all objects that were updated on 
*    //or after the given date
* }));    
* @return {Query}
*/
var deltaUpdatedFrom = module.exports.deltaUpdatedFrom   = deltaHelper("updated_at");
instanceMethodBuilder.define('deltaUpdatedFrom',2);

/**
* Gets the objects which were deleted on or after the given date
* @function deltaDeletedFrom
* @param {Date|String} date Instance of Date or a string in the following format 'YYYY-MM-DD'
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
*
* //<b>Example demonstrate method accepting a formated string as date. </b>
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* var query = query.deltaDeletedFrom('2014-09-02');
* query.exec()
* .then(function(object)({
*    // object.deleted is array of all objects that were deleted on or 
*    // after the given date
* }));
*
* //<b>Example demonstrate method accepting a date instance.</b>
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* var query = query.deltaDeletedFrom(new Date());
* query.exec()
* .then(function(object)({
*    // object.deleted is array of all objects that were deleted on or 
*    // after the given date
* }));     
* @return {Query}
*/
var deltaDeletedFrom = module.exports.deltaDeletedFrom  = deltaHelper("deleted_at");
instanceMethodBuilder.define('deltaDeletedFrom',2);

/**
* Get the objects which were created, updated or deleted on or after the given date
* @function deltaAll
* @param {Date|String} date Instance of Date or a string in the following format 'YYYY-MM-DD'
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
*
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* //<b>Example demonstrate method accepting a formated string as date. </b>
* var query = query.deltaAll('2014-09-02');
* query.exec()
* .then(function(object)({
*    // object.created   //is a array of all object that were created on or after the given date.    
*    // object.updated   //is a array of all object that were update on or after the given date.  
*    // object.deleted   //is a array of all object that were deleted on or after the given date.  
* }));
* //<b>Example demonstrate method accepting a date instance.</b>
* var query = Built.App('blt5d4sample2633b').Class('project').Query();
* var query = query.deltaAll(new Date());     
* @return {Query}
*/
module.exports.deltaAll = deltaHelper("ALL");
instanceMethodBuilder.define('deltaAll',2);

var objectWrapper = R.curry(function(query, response) {
	var objCons     = query.cls.ObjectCons;
	var keys        = Object.keys(response);
	var objs        = response.objects;
	var transform   = query.getTransform();
	var transformFn = sdkTransformer(objCons);

	// Applies the appropriate object transformer
	switch(transform){
		case BACKBONE_TRANSFORM :
			transformFn = backboneTransformer(objCons); // Curried its first arguement
			break;
		case JSON_TRANSFORM :
			transformFn = jsonTransformer;
			break;
	}

	if (keys.length > 1) {
		var returnArray = [];
		returnArray.push(handleResponseObjects(objs, transformFn));
		if (response.schema)
			returnArray.push(response.schema);
		if (response.count)
			returnArray.push(response.count);
		return returnArray;
	} else {
		if (utility.isNumber(objs))
			return objs
		else
			return handleResponseObjects(objs, transformFn);
	}
});

function handleResponseObjects(objs,transformFn) {
	if (utility.isArray(objs)) {
		return transformFn(objs);
	} else{
		return constructDelta(objs, transformFn);
	}
}

function constructDelta(obj, transformFn) {
	var deltaObj = {};
	if (obj.created_at)
		deltaObj["created"] = transformFn(obj.created_at);
	if (obj.deleted_at)
		deltaObj["deleted"] = transformFn(obj.deleted_at);
	if (obj.updated_at)
		deltaObj["updated"] = transformFn(obj.updated_at);
	return deltaObj;
}

var  sdkTransformer = R.curry(function(objCons,objArray) {
	return objArray.map(function(obj){
		return objCons({},{},obj,{});
	});
});

var backboneTransformer = R.curry(function(objCons,objArray){
	var sdkObjects = sdkTransformer(objCons, objArray);
	
	var backboneCollection = objCons.BackboneCollection({
		objects: sdkObjects
	});
	return backboneCollection;
});

function jsonTransformer(objArray){
	return objArray;
}
/**
* Executes the query
* @function exec
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
*
* var app = Built.App('blt5d4sample2633b');
* var query = app.Class('project').Query({
*    name:'John'
* });
* //<b>Normal Query</b>
* query.exec()
* .then(function(objects)({
*    // Fetches all objects having name John
* }));
*
* //<b>Query containing query.includeCount() </b>
* query
* .includeCount()
* .exec()
* .spread(function(objects,count)({
*    //"objects" is array of objects return.
*    //"count" is the number of objects returned.
* }));
* //<b>Query containing query.includeCount() and query.includeSchema() </b>
* query
* .includeCount()
* .includeSchema()
* .exec()
* .spread(function(objects,schema,count)({
*    //"objects" is array of objects returned.
*    //"count" is the number of objects returned.
* }));        
* //<b>Query containing query.count()</b>
* query
* .count();
* .exec()
* .then(function(count)({
*    //Count is the number of objects returned.
* }));
* 
* //<b>When a query consists of a delta parameter, the response is plain javascript object with keys for each delta</b>
* var app   = Built.App('blt5d4sample2633b');
* var query = app.Class('project').Query({
*    name:'John'
* });
* query.deltaCreatedFrom(new Date())
* .exec()
* .then(function(result)({
*    // result.created is array of all objects that were created on or 
*    // after the given date
* }));
*
* //<b>When an query is following Built.CachePolicy.CACHE_THEN_NETWORK the ouput structure is as follows</b>
* query.setCachePolicy(Built.CachePolicy.CACHE_THEN_NETWORK);
* var result = query.exec(); // This is the only use case where exec returns an object and not a promise.
*
* //object.cache is a promise that is resloved when data is avaliable through cache. 
* result.cache.then(function(objects){   
*    // objects is array of SDK objects
* });
*
* //object.network is a promise that is resloved when data is avaliable through network. 
* result.network.then(function(objects){ 
*    // objects is array of SDK objects
* });
*		
* // object.both is a function that accepts a callback which is executed twice, 
* // once when the data is avaliable through cache and then again when its avaliable through network.
* result.both(function(error,data){ 
*    // error is the network error.   
* });
* @return {Promise<Variable>}
*/
module.exports.exec = function(query) {
	var hashKey              = getHashFor(getQueryGist(query));
	var curriedSetInCache    = setInCache(hashKey);
	var curriedObjectWrapper = objectWrapper(query);
	switch(query.cachePolicy){
		case Built.CachePolicy.ONLY_NETWORK:
			return getFromNetwork(query)
				.then(curriedObjectWrapper);
		break;
		case Built.CachePolicy.CACHE_ELSE_NETWORK:
			return getFromCache(hashKey)
				.then(curriedObjectWrapper)
			.catch(function(){
				return getFromNetwork(query)
				.then(curriedSetInCache)
				.then(curriedObjectWrapper);
			});
		break;
		case Built.CachePolicy.NETWORK_ELSE_CACHE:
			return getFromNetwork(query)
				.then(curriedSetInCache)
				.then(curriedObjectWrapper)
				.catch(function(networkError){
					return getFromCache(hashKey)
						.then(curriedObjectWrapper)
						.catch(function(){
							throw networkError;
						})
				});
		break;
		case Built.CachePolicy.CACHE_THEN_NETWORK:
			var bothCallback = function() {}
			return {
				cache   : getFromCache(hashKey)
									.then(curriedObjectWrapper)
									.then(function(cacheData){
										bothCallback(null, cacheData);
										return cacheData;
									}),
				network : getFromNetwork(query)
									.then(curriedSetInCache)
									.then(curriedObjectWrapper)
									.then(function(networkData){
										bothCallback(null,networkData);
										return networkData;
									})
									.catch(function(error){
										bothCallback(error,null);
									}),
				both    : function(cb) {bothCallback=cb} 
			}
		break;
	}
}
instanceMethodBuilder.define('exec',1);

/**
* Whenever you execute a query that contains some dynamic field, it generates a new hashkey everytime.
* Using this method, you can restrict the query from creating a new hashkey whenever it is executed.
* It blacklists the given fields, and returns the same hashkey everytime.
* @function setBlacklist
* @param {Array} fieldUidArray Array of field uid's which are to be blacklisted
* @instance 
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
*
* //<b>Example demonstrate method accepting a updated_at field. </b>
* var query = Built.App('blt5d4sample2633b')
*              .setCachePolicy(Built.CachePolicy.CACHE_ELSE_NETWORK)
*              .Class('project').Query();
* var query = query
*              .lessThan('updated_at', new Date())
*              .setBlacklist(['updated_at']);
* var result = project.exec(); 
*
* result.cache.then(function(objects){   
*    // objects from cache
* });
*
* result.network.then(function(objects){ 
*    // objects from network
* });
* @return {Query}
*/
module.exports.setBlacklist = function(array, query) {
	if(!utility.isArray(array)){
		throw new Error("Expected 'Array' found '"+ typeof(array)+"'")
	}
	
	var newConds = R.mixin({},getConditions(query));
	newConds['blacklists'] = array;
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);
}
instanceMethodBuilder.define('setBlacklist',2);

/**
* Get the array of all the Blacklisted fields
* @function getBlacklist
* @instance 
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
*
* //<b>Example demonstrate method accepting a updated_at field. </b>
* var query = Built.App('blt5d4sample2633b')
*              .setCachePolicy(Built.CachePolicy.CACHE_ELSE_NETWORK)
*              .Class('project').Query();
* var query = query
*              .lessThan('updated_at', new Date())
*              .setBlacklist(['updated_at']);
*
* var blacklist = query.getBlacklist()
* @return {Array}
*/
module.exports.getBlacklist = function(query) {	
	return getConditions(query).blacklists
}
instanceMethodBuilder.define('getBlacklist',1);

function getQueryGist(query){
	var newQuery = R.mixin({},getConditions(query));
	if(query.getBlacklist()){
		newQuery = removeBlacklistFields(newQuery, query.getBlacklist());
	}
	return {
		conditions: newQuery,
		headers   : query.headers,
		className : query.cls.uid,
		apiKey    : query.app.getApiKey()
	}
}

function findBlacklistField(obj,field){
	for (var key in obj) {
		if (key === field) {
			delete obj[key];
		}
		else if (utility.isArray(obj[key])){
			obj[key].map(function(object){
				findBlacklistField(object, field); 
			})
		}
		else if (typeof obj[key] == "object")
			findBlacklistField(obj[key], field);
	}
	return obj;
}

function removeBlacklistFields(obj, blacklistsArr){
	var modifiedObj = JSON.parse(JSON.stringify(obj)); //deep clone the object
	for(var i = 0; i < blacklistsArr.length; i++){
		modifiedObj = findBlacklistField(modifiedObj,blacklistsArr[i]);
	}
	return modifiedObj;
}

function getHashFor(obj){
	return md5(JSON.stringify(obj));
}

function getFromCache(hashKey){
	var deferred = when.defer();
	if(utility.isLocalStroageAvaliable()){
		var cacheData = localStorage.getItem(hashKey);
		if(cacheData){
			deferred.resolve(JSON.parse(cacheData));
		}
		else
			deferred.reject(null);
	}else{
		deferred.reject();
	}
	return deferred.promise;
}


function getFromNetwork(query){
	var adaptor         = query.app.options.adaptor;
	var newConds        = R.mixin({},getConditions(query));

	var requestObject   = utility.getAdaptorObj('GET',getURL(query),module.exports.getCombinedHeaders(query),null, newConds);
	return makeRequest(requestObject, adaptor);
}
/**
* Fetches objects that are nearer to a specified location within the given radius
* @function nearLocation
* @param {Location|String} location Built.Location object or Object's uid in the latter case, location of the object is being used
* @param {Number} radius The radius within which to query (Optional & Defaults to 1000)
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
* var point = Built.Location(60,80);
* var Query = Built.App('blt5d4sample2633b').Class('project').Query;
* var query = Query();
* query = query.nearLocation(point,1200);
* @return {Query}
*/
module.exports.nearLocation = R.curry(function(location,radius,query){
	radius = radius || 1000;
	var newConds   = R.mixin({},getConditions(query));
	var newQuery   = R.mixin({},getQueryObj(query));
	if(location && location.data && utility.isNumber(radius)){ // temp check
		var locObj = location.toJSON();
		newQuery['$near'] = {
			coords  : [locObj.longitude,locObj.latitude],
			radius  : radius
		}
	}else if(utility.isString(location) && utility.isNumber(radius)){
		newQuery['$near'] = {
			coords: {
				object: location
			},
			radius:radius
		}
	}else{
		throw new Error('Invalid parameters');
	}
	newConds.query = newQuery;
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);  
});
instanceMethodBuilder.define('nearLocation',3,[null]);

/**
* Deletes all the objects that fulfill the query condition
* @function delete
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
* var Query = Built.App('blt5d4sample2633b').Class('project').Query;
* var query = Query();
* query = query.containedIn('uid',['blt51dsampl79eaa8d7','bltbcfdetest2772f9d']);
* query.delete()
* .then(function(data){
*    console.log(data) // null;
* });
* @return {Promise<null>}
*/
module.exports.delete = function(query){
	var adaptor         = query.app.options.adaptor;
	var newConds        = R.mixin({},getConditions(query));
	var requestObject   = utility.getAdaptorObj('DELETE',getURL(query),module.exports.getCombinedHeaders(query),newConds,null);
	return makeRequest(requestObject, adaptor).yield(null);
}
instanceMethodBuilder.define('delete', 1);


var exceptOnlyHelper  = R.curry(function(operation,key,uidArray,query){
	var newConds        = R.mixin({},getConditions(query));
	if(newConds[operation]){
		newConds[operation] = R.mixin(newConds[operation], utility.keyValue(key, uidArray))
	} 
	else
		newConds[operation] = utility.keyValue(key,uidArray)
	return queryCons(query.app,query.cls,query.headers,query.cachePolicy,newConds);  
});

/**
* Removes a set of fields from response
* @function except
* @param {Array} fieldUidArray Array of field uid's that need to be excluded from response
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query() returns a 'Query' instance
* // 'project' is a uid of a class on Built.io Backed
* var Query = Built.App('blt5d4sample2633b').Class('project').Query;
* var query = Query();
* query = query.where('uid','blt111sample24a6b');
* query = query.except(['username']);
* query.exec()
* .then(function(res){
*    // The response won't contain username field
* });
* @return {Query}
*/
module.exports.except = exceptOnlyHelper('except','BASE');
instanceMethodBuilder.define('except',2);

/**
* Removes a set of fields from the referenced object
* @function exceptReference
* @param {String} Reference field uid
* @param {Array} fieldUidArray Array of field uids that need to be excluded from the referenced object
* @instance
* @memberof Query
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query returns a 'Query' constructor
* // 'project' is a uid of a class on Built.io Backed
*
* var Query = Built.App('blt5d4sample2633b').Class('project').Query;
* var query = Query();
* query = query.where('uid','blt111sample24a6b');
* query = query.include(['per_city'])
* query = query.exceptReference('per_city',['city']); //per_city is a reference field that references the Address class
* query.exec()
* .then(function(res){
*    // The response won't contain city field in the Address object
* });
* @return {Query}
*/
module.exports.exceptReference = exceptOnlyHelper('except');
instanceMethodBuilder.define('exceptReference',3);

/**
* This method should be used when it is required to only have a set of fields in the base class object's respone
* @function only
* @param {Array} field_Uids Array of field uid's that need to be present in the response 
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query returns a 'Query' constructor
* // 'project' is a uid of a class on Built.io Backed
* var Query = Built.App('blt5d4sample2633b').Class('project').Query;
* var query = Query();
* query = query.where('uid','blt111sample24a6b');
* query = query.only(['username']);
* query.exec()
* .then(function(res){
*    // The response will only have the username field
* });
* @instance
* @memberof Query
* @return {Query}
*/
module.exports.only = exceptOnlyHelper('only','BASE');
instanceMethodBuilder.define('only',2);

/**
* This method should be used when it is required to only have a set of fields in the referenced object
* @function onlyReference
* @param {String} Reference field uid
* @param {Array} field_Uids Array of field uids that need to be present in the referenced object
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query returns a 'Query' constructor
* // 'bugs' is a uid of a class on Built.io Backed
*
* var Query = Built.App('blt5d4sample2633b').Class('bugs').Query;
* var query = Query();
* query = query.where('uid','blt111sample24a6b');
* query = query.include(['project']);
* // 'name' is a reference field that references the Project class
* query = query.onlyReference('project',['name']); 
*
* query.exec()
* .then(function(res){
*    // The response will only have name field in the Project object
* });
* @instance
* @memberof Query
* @return {Query}
*/
module.exports.onlyReference = exceptOnlyHelper('only');
instanceMethodBuilder.define('onlyReference',3);


/**
* Adds a rawQuery
* @function setRawQuery
* @memberof Query
* @instance
* @param {String} Reference field uid
* @param {Array} field_Uids Array of field uids that need to be present in the referenced object
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* // 'Class('class_name').Query returns a 'Query' constructor
* // 'bugs' is a uid of a class on Built.io Backed
*
* var Query = Built.App('blt5d4sample2633b').Class('bugs').Query;
* var query = Query();
* query = query.setRawQuery({
*    name: 'Super Project #41!'
* })
* query.exec()
* .then(function(res){
*    // The response will only have name field in the Project object
* });
* @return {Query}
*/
module.exports.setRawQuery = R.curry(function(rawQuery, query){
	var newConds   = R.mixin({}, getConditions(query));
	newConds.query = rawQuery; 
	return queryCons(query.app, query.cls, query.headers, query.cachePolicy, newConds);
})
instanceMethodBuilder.define('setRawQuery', 2);

/**
* Export objects from a class to an Excel File
* @function exportToExcel
* @instance
* @memberof Query 
* @example
* // 'blt5d4sample2633b' is a dummy Application API key
* <h3><u>On Browser</u></h3>
* var query = Built.App("blt5d4sample2633b").Class('sample').Query();
* query.exportToExcel();
*
* <h3><u>On Node</u></h3>
* var query = Built.App("blt5d4sample2633b").Class('sample').Query();
* query.exportToExcel()
* .then(function(res) {
*   // a nodejs 'Stream' is  returned
* });
* @return {Promise}
*/
module.exports.exportToExcel = R.curry(function(query){
	var exportUrl = getExportUrl(query)

	if(utility.isBrowser()){
		window.open(exportUrl, "", "width=10,height=10");
	}else{
		var deferred = when.defer();
		node.https.get(exportUrl, function(response) {
			deferred.resolve(response);
		});
		return deferred.promise;
	}
})
instanceMethodBuilder.define('exportToExcel', 1);

function getExportUrl(query) {
	var url      = getURL(query) + "/export_to_excel?"
	var newConds = R.mixin({}, getConditions(query)); 
	var headers  = module.exports.getCombinedHeaders(query)
	url          = attachHeaders(url, headers)
	
	if(newConds.query)
	 	url += "query=" + JSON.stringify(newConds.query)

	return url
}

function attachHeaders(url, headers) {
	Object.keys(headers).map(function(key) {
		url += key + "=" + headers[key] + "&"
	})

	return url
}

function makeRequest(requestObject, adaptor){
	return adaptor.makeCall(requestObject)
	.then(function(response) {
		return response.entity;
	});
}

function getURL(query) {
	var Cls = require('./class');
	return Cls.getURL(query.cls) + '/objects'
}

function getMixinedConditions(query){
	var newQuery              = R.mixin({},getConditions(query));
	newQuery.query            = newQuery.query || {};
	return newQuery;
}