'use strict';
/**
过滤器生成器
Feartures :
1. 编译字符串过滤,“name = @name and (age > @age and type = @type)”,生产过滤条件参数或者过滤方法
2. 参数过滤,以参数的方式全部构建"="的方式构建查询方法
3. 方法过滤
4. 忽略null的条件
5. 自定义扩展过滤操作符
6. 条件&参数合并
Update Note:
+ 2014.7 :Created
@module FilterBuilder
*/
_stDefine('filterBuilder', function(st) {
//空值忽略条件标示符
var NullIgnoreSign = ["{", "}"],
//关系字符
Relations = ["and", "or"],
isArray = st.isArray;
/**
过滤生成器对象;可以使用:条件字符串;参数;方法来构建;
条件字符串的过滤操作见[Operations](Operations.html)
@class FilterBuilder
@constructor
@param {string|function|object} filter 三种类型:
1. {string}, 查询字符串
2. {object}, 参数对象
3. {function}, 过滤方法
@return {FilterBuilder} 返回过滤生成器对象
@example
//定义数据
var data = [
{name: "roy",age: 30,role: "sa",project: "smartjs"},
{name: "roy",age: 30,role: "coder",project: "smartdoc"},
{name: "coder1", age: 20, role: "coder", project: "smartjs"}
];
//查询字符串,{age > @age}用{}包含的条件表示当@age为null的时候,忽略此条件
var str = "{age > @age} and (role = @sa or role = @coder) and {project = @project}";
//创建字符串过滤器
var strFilter = st.filterBuilder(str);
//生成过滤方法
var fnFilterCoder = strFilter.buildFn({
coder : 'coder',
});
//过滤所有coder
var coders = data.filter(fnFilterCoder);
expect(coders.length).toBe(2);
expect(coders[0].name).toBe('roy');
expect(coders[1].name).toBe('coder1');
//再次生成smartjs项目年纪大于20的coder或sa
var filterFn = strFilter.buildFn({
age : 20,
coder : 'coder',
sa : 'sa',
project : 'smartjs'
});
var member = data.filter(filterFn);
expect(member.length).toBe(1);
expect(member[0].name).toBe('roy');
//创建过滤器
var paramFilter = st.filterBuilder();
//根据参数创建过滤方法
var filterFn2 = paramFilter.buildFn({
name : 'coder1'
});
var coder1 = data.filter(filterFn2);
expect(coder1.length).toBe(1);
expect(coder1[0].name).toBe('coder1');
*/
function FilterBuilder(filter) {
if (filter) {
switch (typeof filter) {
//查询字符串
case "string":
this._conditions = compileStringCondition(filter);
break;
//过滤方法
case "function":
this._filter = filter;
break;
//参数过滤
case "object":
this._params = filter;
break;
}
}
}
FilterBuilder.prototype = {
/**
* 生成条件参数,当使用查询字符串进行构建过滤器时,根据传入的参数值生产最终的带关系和操作过滤参数
* @method buildCondition
* @param {object} params 过滤的参数值
* @return {object} 条件参数
* @example
var str = "age > @age and (role = @sa or role = @coder) and project = @project";
var filter = st.filterBuilder(str);
//生成条件
var conditions = filter.buildCondition({
age : 20,
sa : 'sa',
coder : 'coder',
project : "smartjs"
})
log(conditions);
// 生成的conditions对象
// {"and":[
// {"field":"age","operation":">","param":20},
// {"or":[
// {"field":"role","operation":"=","param":"sa"},
// {"field":"role","operation":"=","param":"coder"}
// ]},
// {"field":"project","operation":"=","param":"smartjs"}
// ]}
*/
buildCondition: function(params) {
if (this._conditions)
return buildConditions(this._conditions, params);
},
/**
* 生成过滤方法
* @method buildFn
* @param [params] {object} 过滤的参数值
* @param [mergeFilter] {string|function|object} 需要合并的过滤条件;合并全部都为and
* @return {function} 过滤方法
* @example
* var data = [
* {name : 'sa1', role : 'sa', age : 33},
* {name : 'sa2', role : 'sa', age : 25}
* ];
*
* //创建role的过滤器
* var filter = st.filterBuilder("role = @role");
*
* //传入条件参数,并追加age的过滤
* var filterFn = filter.buildFn({role:"sa",age:30},"age > @age");
*
* var sa = data.filter(filterFn);
* expect(sa.length).toBe(1);
* expect(sa[0].name).toBe('sa1')
*
*/
buildFn: function(params, mergeFilter) {
var self = this,filterType,
conditions, fnFilter, mergeFilterFn;
//过滤方法模式
if (self._filter)
fnFilter = self._filter;
//条件生成模式
else if (self._conditions) {
conditions = this.buildCondition(params);
if (conditions)
fnFilter = buildFn(conditions)
} else if(!mergeFilter)
//参数过滤非合并参数模式下,根据参数创建过滤方法
fnFilter = compileObjectCondition(st.mix(params, self._params));
//存在合并过滤情况下,生成合并过滤方法
if (mergeFilter) {
filterType = typeof(mergeFilter);
if (filterType === 'string')
mergeFilterFn = (new FilterBuilder(mergeFilter)).buildFn(params);
else if(filterType === 'function')
mergeFilterFn = mergeFilter;
}
//合并过滤条件
return st.mergeFn(fnFilter, mergeFilterFn,true);
}
}
//将对象参数编译成过滤方法
function compileObjectCondition(obj) {
return function(item) {
var check = true;
st.each(obj, function(name, value) {
if (item[name] !== value) {
check = false;
return check;
}
})
return check;
}
}
//将过滤字符串编译成过滤条件对象
function compileStringCondition(filter) {
var groups = [],
deep = 0,
//条件关系链
chain = [groups];
filter.split('(').forEach(function(part, i) {
if (i === 0) {
compileGroup(chain, deep, part)
} else {
part.split(')').forEach(function(exp, i) {
i > 0 ? deep-- : deep++;
compileGroup(chain, deep, exp);
})
}
})
return groups.length ? groups : null;
//console.log(groups);
}
//编译查询条件组
function compileGroup(chain, deep, part) {
var group, arr, condition, len = chain.length - 1;
arr = part.split(/\s(or|and)\s/g);
//判断开始是否存在关系表达式
if (arr[0].length === 0 && Relations.indexOf(arr[1]) > -1) {
arr.shift();
chain[len].or = arr.shift() === Relations[1];
}
//深度大于关系链时,扩展关系链
if (deep > len)
group = chain[deep] = [];
else {
group = chain[deep];
//深度小于关系链时,将关系链最后一项清除,添加到当前group中
deep < len && group.push(chain.pop());
}
arr.forEach(function(item, i) {
if (item) {
//判断为关系参数时,设置条件关系
if (Relations.indexOf(item) > -1) {
condition.or = item === Relations[1];
} else {
condition = compileConditionStr(item);
group.push(condition);
}
}
})
}
//编译查询条件字符串
function compileConditionStr(condition) {
var arr, ignoreNull = false,
index;
//判断是否空忽略
if (condition.charAt(0) === NullIgnoreSign[0]) {
index = condition.lastIndexOf(NullIgnoreSign[1]);
if (index > 1) {
condition = condition.substring(1, index);
ignoreNull = true;
} else {
condition = condition.substring(1);
}
}
arr = condition.split(' ').filter(function(item) {
return item.length > 0;
});
return {
ignoreNull: ignoreNull,
field: arr[0],
operation: arr[1],
param: arr[2]
};
}
//根据过滤条件组生成过滤条件
function buildConditions(conditions, params) {
var unit, orGroup, lastOr, or, pass, newGroup,param, len = conditions.length - 1,
group = [],
chain = [group];
conditions.forEach(function(condition, i) {
//判断是否pass模式
if (pass) {
pass = false;
lastOr = condition.or;
return;
}
or = condition.or;
//判断是否为过滤条件组
if (isArray(condition)) {
unit = buildConditions(condition, params);
} else {
param = condition.param;
//判断为过滤参数还是过滤值
if (param.charAt(0) === '@')
param = st.getObj(params, param.substr(1));
//判断是否空忽略
if (condition.ignoreNull && param == null) {
or && (pass = true);
unit = null;
} else {
unit = {
field: condition.field,
operation: condition.operation,
param: param
};
}
}
if (unit) {
if (i === len) {
//最后一个条件和or关系下,将group设置到关系链开始
if (or)
group = chain[0];
}
//在上一个和当前关系不一致时
else if (i > 0 && !lastOr !== !or) {
//当前关系为or时,提升or级别,将原有and关系组置于or关系组下
if (or) {
//如果存在关系链,在加深一级,否则创建关系链
if (chain.length > 1) {
group = chain[0];
chain = [group];
} else {
chain[0] = group = [{
and: group
}];
}
} else {
//当前为and时,创建新组,添加到前一个or关系组
newGroup = [];
chain.push(newGroup);
group.push({
and: newGroup
});
group = newGroup;
}
}
group.push(unit);
}
if (or)
orGroup = true;
lastOr = or;
})
group = chain[0];
if (group.length)
return orGroup ? {
or: group
} : {
and: group
};
}
//根据条件关系生成过滤方法
function buildFn(conditions) {
return function(data) {
var result = true,
isOr;
st.each(conditions, function(relation, condition) {
if (!checkGroup(data, condition, relation === 'or')) {
result = false;
}
})
return result;
}
}
//验证关系组条件是否匹配
function checkGroup(data, condistions, isOr) {
var group, result;
condistions.some(function(condition, i) {
if (condition.field) {
result = compare(data, condition);
} else {
if (group = condition.or)
result = checkGroup(data, group, true);
else if (group = condition.and)
result = checkGroup(data, group, false);
}
if(isOr && result)
return true;
else if (!isOr && !result)
return true;
})
return result;
}
function getIndex(datas, data) {
if (data && datas && datas.length && datas.indexOf)
return datas.indexOf(String(data)) > -1;
return false;
}
function checkStartEnd(datas, data, endOf) {
if (data && datas && datas.length && datas.substr) {
data = String(data);
return (endOf ? datas.substr(datas.length - data.length) : datas.substr(0, data.length)) === data;
}
return false;
}
/**
* 针对过滤器条件字符串的条件过滤操作;预设了基础操作;另外可以通过<code>st.extendOperation(operation,checkFn)</code>进行扩展和重写
* @class Operations
*/
var Operations = {
/**
* 非判断,在判断操作符之前加入!,则将判断结果取非
* @property {operation} !
* @example
* //查询name不等于'roy'的数据
* var filter = "name != 'roy'"
*/
/**
* 等于判断
* @property {operation} =
*/
"=": function(data, param) {
return data === param;
},
/**
* 小于判断
* @property {operation} <
*/
"<": function(data, param) {
return data < param;
},
/**
* 小于等于判断
* @property {operation} <=
*/
"<=": function(data, param) {
return data <= param;
},
/**
* 大于判断
* @property {operation} >
*/
">": function(data, param) {
return data > param;
},
/**
* 大于等于判断
* @property {operation} >=
*/
">=": function(data, param) {
return data >= param;
},
/**
* 参数中包含数据
* @property {operation} in
*/
"in": function(data, param) {
return getIndex(param, data);
},
/**
* 数据中包含参数
* @property {operation} like
*/
"like": getIndex,
/**
* 以参数为开头
* @property {operation} startOf
*/
"startOf": checkStartEnd,
/**
* 以参数为结尾
* @property {operation} endOf
* @example
* //匹配以'es'结尾的name
* var filter = "name endOf 'es'";
*/
"endOf": function(data, param) {
return checkStartEnd(data, param, true);
}
};
function compare(data, condition) {
var operation = condition.operation,check,not,result;
//判断是否为非
if(operation.charAt(0) === '!'){
not = 1;
operation = operation.substring(1);
}
result = (check = Operations[operation]) ? check(st.getObj(data, condition.field), condition.param) : false;
return not ? !result : result;
}
return {
filterBuilder: function(filter) {
return new FilterBuilder(filter);
},
/**
扩展判断操作符,如:'='比较操作符,name = @name
@method extendOperation
@param {string} operation 操作名称
@param {function} checkFn 判断方法
@example
//添加大于操作符'>'
st.extendOperation('>',function(data, param) {
//data为数据,param为条件参数
return data > param;
});
*/
extendOperation : function(operation,checkFn){
Operations[operation] = checkFn;
}
};
})