Skip to content
Snippets Groups Projects
Commit e7141f75 authored by Johannes Goderbauer's avatar Johannes Goderbauer
Browse files

fix #1045256: filtering in jdito does work again when no custom checker...

fix #1045256: filtering in jdito does work again when no custom checker funciton is defined for a column
parent 40d5a7f0
No related branches found
No related tags found
No related merge requests found
import("Sql_lib");
//private scope to make only JditoFilterUtils public
var JditoFilterUtils = (function ()
{
/**
* object for filtering records
*
* @param {Array} pColumns the column names
* @param {Object} pFilter the filter object
* @param {Object} pCustomCheckFns
* @param {Object} pCheckFnThisArg
*
* @private
*/
function JditoFilterHelper (pColumns, pFilter, pCustomCheckFns, pCheckFnThisArg)
{
var columnMap = {};
for (let i = 0, l = pColumns.length; i < l; i++)
{
let col = pColumns[i];
if (col)
columnMap[col] = {
index : i,
checkFn : pCustomCheckFns[col] || {}
};
}
this.columnMap = columnMap;
this.filter = pFilter;
this.checkFnThisArg = pCheckFnThisArg || null;
}
/**
* tests the given row if it matches the filter
*
* @param {Array} pRow one record
*
* @return {boolean} true, if it matches the condition
*/
JditoFilterHelper.prototype.checkRecord = function (pRow)
{
if (this.filter.length == 0)
return true;
return _testRecord.call(this, this.filter);
/**
* recursive function to test the row against the condition
*/
function _testRecord (pCondition)
{
if (pCondition.type == "row")
{
let value = pRow[this.columnMap[pCondition.name].index];
let testFn = this.columnMap[pCondition.name].checkFn || _testValue;
return testFn.call(this.checkFnThisArg, value, (pCondition.key || pCondition.value), pCondition.operator, pRow);
}
else if (pCondition.type == "group")
{
if (pCondition.operator == "AND")
return pCondition.childs.every(_testRecord, this);
return pCondition.childs.some(_testRecord, this);
}
return true;
}
/**
* compares two values with the given operator
*/
function _testValue (pRowValue, pFilterValue, pOperator)
{
switch (pOperator)
{
case "CONTAINS":
return (new RegExp(pFilterValue)).test(pRowValue);
case "CONTAINSNOT":
return !(new RegExp(pFilterValue)).test(pRowValue);
case "STARTSWITH":
return (new RegExp("^" + pFilterValue)).test(pRowValue);
case "ENDSWITH":
return (new RegExp(pFilterValue + "$")).test(pRowValue);
case "EQUAL":
return (new RegExp("^" + pFilterValue + "$")).test(pRowValue);
case "NOT_EQUAL":
return !(new RegExp("^" + pFilterValue + "$")).test(pRowValue);
case "LESS":
return pRowValue < pFilterValue;
case "LESS_OR_EQUAL":
return pRowValue <= pFilterValue;
case "GREATER":
return pRowValue > pFilterValue;
case "GREATER_OR_EQUAL":
return pRowValue >= pFilterValue;
case "ISNULL":
return pRowValue == "";
case "ISNOTNULL":
return pRowValue != "";
}
}
}
/**
* Provides functions for using the filter with jdito recordcontainers
*
* Do not instanciate this!
*
* @class
*/
function JditoFilterUtils () {}
JditoFilterUtils.getFilterFields = function (pFilterJsonRootNode)
{
let filterFields = [];
for(var filterChildNode in pFilterJsonRootNode)
{
var currentNode = pFilterJsonRootNode[filterChildNode];
if(currentNode.type == "row")
{
let fieldName = currentNode.name;
filterFields.push(fieldName);
}
else
{
JditoFilterUtils.getFilterFields(filterChildNode);
}
}
return filterFields;
}
/**
* Filters the given records manually. If you get the records using a sql query, you might consider using
* JditoFilterUtils.getSqlCondition instead for better performance.
*
* @param {Array} pColumns one dimensional array with all column names (only the columns with the idValue, displayValue columns should be null or ""),
* the order has to match the columns of the recordFields property in the recordcontainer
* @param {Array} pRecords two dimensional array with all records
* @param {String|Object} pFilter the value of vars.get($local.filter).filter
* @param {Object} [pCustomCheckFns] Object of custom functions that should be used to ckeck records. The key has to be the name of the fields you want to
* check. The provided function will be called with the arguments (recordValue, filterValue, operator, record) and should return a boolean (or something truthy or falsy).
* @param {Object} [pCheckFnThisArg] The object that should be the this-context when the custom check functions are called. This can be useful if you
* need some kind of storage for extra data you need in your function that you don't have to load for every record.
*
* @return {Array} the filtered records
*/
JditoFilterUtils.filterRecords = function (pColumns, pRecords, pFilter, pCustomCheckFns, pCheckFnThisArg)
{
if (!pFilter)
return pRecords;
var filter = new JditoFilterHelper(pColumns, pFilter, pCustomCheckFns, pCheckFnThisArg);
return pRecords.filter(function (row)
{
return this.checkRecord(row);
}, filter);
}
/**
* builds an sql condition from the given filter
*
* @param {Object} pFilter the filter object
* @param {String} pTable the database table
* @param {String} [pTableAlias=null] the database table alias
* @param {Object} [pColumnOrFnMap=null] Custom mapping for the fields to the DB columns or functions that return a SqlCondition, this is necessary
* if the fields are from different tables. Structure has to be like this:
* {
* FIELD1 : "TABLE1.COLUMN1",
* FIELD2 : ["TABLE2", "COLUMN2", "TABLE2ALIAS"], //do it like this if the table has an alias
* FIELD3 : function (pValue, pOperator) //function if you need to build a special condition
* {
* var cond = new SqlCondition();
* ...
* return cond;
* }
* }
*
* @example
* var condition = SqlCondition.begin();
* if (vars.exists("$local.filter") && vars.get("$local.filter"))
* {
* var filter = vars.get("$local.filter");
* if (filter.filter)
* condition.andSqlCondition((JditoFilterUtils.getSqlCondition(filter.filter, "AB_ATTRIBUTE")));
* }
* var attributeSql = condition.buildSql("select AB_ATTRIBUTEID from AB_ATTRIBUTE");
*
* @return {SqlCondition} the SqlCondition object
*/
JditoFilterUtils.getSqlCondition = function (pFilter, pTable, pTableAlias, pColumnOrFnMap)
{
var condition = new SqlCondition();
if (!pFilter)
return condition;
if (!pColumnOrFnMap)
pColumnOrFnMap = {};
_addCondition.call(condition, pFilter, pFilter.operator);
return condition;
//recursive function (for compatibility with a condition tree) that
//builds the SqlCondition
function _addCondition (pCondition, pOperator)
{
if (pCondition.type == "row")
{
if (pCondition.name in pColumnOrFnMap)
pCondition.name = pColumnOrFnMap[pCondition.name];
else if (pTable && pTableAlias)
pCondition.name = [pTable, pCondition.name, pTableAlias];
else if (pTable)
pCondition.name = pTable + "." + pCondition.name;
pCondition.value = (pCondition.key || pCondition.value);
var condition;
if (typeof(pCondition.name) === "function")
{
condition = pCondition.name.call(null, pCondition.value, pCondition.operator);
if (pOperator == "AND")
this.andSqlCondition(condition);
else if (pOperator == "OR")
this.orSqlCondition(condition);
}
else
{
condition = _getCondition.call(pCondition, pCondition.value, pCondition.operator);
if (pOperator == "AND")
this.andPrepare(pCondition.name, pCondition.value, condition);
else if (pOperator == "OR")
this.orPrepare(pCondition.name, pCondition.value, condition);
}
}
else if (pCondition.type == "group")
{
let subCondition = new SqlCondition();
let operator = pCondition.operator;
pCondition.childs.forEach(function (cond)
{
_addCondition.call(subCondition, cond, operator);
});
if (pOperator == "AND")
this.andSqlCondition(subCondition);
else if (pOperator == "OR")
this.orSqlCondition(subCondition);
}
}
//returns the condition depending on the operator and
//adds wildcards to the value if necessary
function _getCondition (pValue, pOperator)
{
switch (pOperator)
{
case "CONTAINS":
this.value = "%" + pValue + "%";
return "# like ?";
case "CONTAINSNOT":
this.value = "%" + pValue + "%";
return "# not like ?";
case "STARTSWITH":
this.value = pValue + "%";
return "# like ?";
case "ENDSWITH":
this.value = "%" + pValue;
return "# like ?";
case "EQUAL":
return "# = ?";
case "NOT_EQUAL":
return "# != ?";
case "LESS":
return "# < ?";
case "LESS_OR_EQUAL":
return "# <= ?";
case "GREATER":
return "# > ?";
case "GREATER_OR_EQUAL":
return "# >= ?";
case "ISNULL":
return "# is null";
case "ISNOTNULL":
return "# is not null";
}
}
}
return JditoFilterUtils; //return only functions that should be public
import("Sql_lib");
//private scope to make only JditoFilterUtils public
var JditoFilterUtils = (function ()
{
/**
* object for filtering records
*
* @param {Array} pColumns the column names
* @param {Object} pFilter the filter object
* @param {Object} pCustomCheckFns
* @param {Object} pCheckFnThisArg
*
* @private
*/
function JditoFilterHelper (pColumns, pFilter, pCustomCheckFns, pCheckFnThisArg)
{
var columnMap = {};
for (let i = 0, l = pColumns.length; i < l; i++)
{
let col = pColumns[i];
if (col)
columnMap[col] = {
index : i,
checkFn : pCustomCheckFns[col]//or nothing when there is no custom function
};
}
this.columnMap = columnMap;
this.filter = pFilter;
this.checkFnThisArg = pCheckFnThisArg || null;
}
/**
* tests the given row if it matches the filter
*
* @param {Array} pRow one record
*
* @return {boolean} true, if it matches the condition
*/
JditoFilterHelper.prototype.checkRecord = function (pRow)
{
if (this.filter.length == 0)
return true;
return _testRecord.call(this, this.filter);
/**
* recursive function to test the row against the condition
*/
function _testRecord (pCondition)
{
if (pCondition.type == "row")
{
let value = pRow[this.columnMap[pCondition.name].index];
let testFn = this.columnMap[pCondition.name].checkFn || _testValue;
return testFn.call(this.checkFnThisArg, value, (pCondition.key || pCondition.value), pCondition.operator, pRow);
}
else if (pCondition.type == "group")
{
if (pCondition.operator == "AND")
return pCondition.childs.every(_testRecord, this);
return pCondition.childs.some(_testRecord, this);
}
return true;
}
/**
* compares two values with the given operator
*/
function _testValue (pRowValue, pFilterValue, pOperator)
{
switch (pOperator)
{
case "CONTAINS":
return (new RegExp(pFilterValue)).test(pRowValue);
case "CONTAINSNOT":
return !(new RegExp(pFilterValue)).test(pRowValue);
case "STARTSWITH":
return (new RegExp("^" + pFilterValue)).test(pRowValue);
case "ENDSWITH":
return (new RegExp(pFilterValue + "$")).test(pRowValue);
case "EQUAL":
return (new RegExp("^" + pFilterValue + "$")).test(pRowValue);
case "NOT_EQUAL":
return !(new RegExp("^" + pFilterValue + "$")).test(pRowValue);
case "LESS":
return pRowValue < pFilterValue;
case "LESS_OR_EQUAL":
return pRowValue <= pFilterValue;
case "GREATER":
return pRowValue > pFilterValue;
case "GREATER_OR_EQUAL":
return pRowValue >= pFilterValue;
case "ISNULL":
return pRowValue == "";
case "ISNOTNULL":
return pRowValue != "";
}
}
}
/**
* Provides functions for using the filter with jdito recordcontainers
*
* Do not instanciate this!
*
* @class
*/
function JditoFilterUtils () {}
JditoFilterUtils.getFilterFields = function (pFilterJsonRootNode)
{
let filterFields = [];
for(var filterChildNode in pFilterJsonRootNode)
{
var currentNode = pFilterJsonRootNode[filterChildNode];
if(currentNode.type == "row")
{
let fieldName = currentNode.name;
filterFields.push(fieldName);
}
else
{
JditoFilterUtils.getFilterFields(filterChildNode);
}
}
return filterFields;
}
/**
* Filters the given records manually. If you get the records using a sql query, you might consider using
* JditoFilterUtils.getSqlCondition instead for better performance.
*
* @param {Array} pColumns one dimensional array with all column names (only the columns with the idValue, displayValue columns should be null or ""),
* the order has to match the columns of the recordFields property in the recordcontainer
* @param {Array} pRecords two dimensional array with all records
* @param {String|Object} pFilter the value of vars.get($local.filter).filter
* @param {Object} [pCustomCheckFns] Object of custom functions that should be used to ckeck records. The key has to be the name of the fields you want to
* check. The provided function will be called with the arguments (recordValue, filterValue, operator, record) and should return a boolean (or something truthy or falsy).
* @param {Object} [pCheckFnThisArg] The object that should be the this-context when the custom check functions are called. This can be useful if you
* need some kind of storage for extra data you need in your function that you don't have to load for every record.
*
* @return {Array} the filtered records
*/
JditoFilterUtils.filterRecords = function (pColumns, pRecords, pFilter, pCustomCheckFns, pCheckFnThisArg)
{
if (!pFilter)
return pRecords;
var filter = new JditoFilterHelper(pColumns, pFilter, pCustomCheckFns, pCheckFnThisArg);
return pRecords.filter(function (row)
{
return this.checkRecord(row);
}, filter);
}
/**
* builds an sql condition from the given filter
*
* @param {Object} pFilter the filter object
* @param {String} pTable the database table
* @param {String} [pTableAlias=null] the database table alias
* @param {Object} [pColumnOrFnMap=null] Custom mapping for the fields to the DB columns or functions that return a SqlCondition, this is necessary
* if the fields are from different tables. Structure has to be like this:
* {
* FIELD1 : "TABLE1.COLUMN1",
* FIELD2 : ["TABLE2", "COLUMN2", "TABLE2ALIAS"], //do it like this if the table has an alias
* FIELD3 : function (pValue, pOperator) //function if you need to build a special condition
* {
* var cond = new SqlCondition();
* ...
* return cond;
* }
* }
*
* @example
* var condition = SqlCondition.begin();
* if (vars.exists("$local.filter") && vars.get("$local.filter"))
* {
* var filter = vars.get("$local.filter");
* if (filter.filter)
* condition.andSqlCondition((JditoFilterUtils.getSqlCondition(filter.filter, "AB_ATTRIBUTE")));
* }
* var attributeSql = condition.buildSql("select AB_ATTRIBUTEID from AB_ATTRIBUTE");
*
* @return {SqlCondition} the SqlCondition object
*/
JditoFilterUtils.getSqlCondition = function (pFilter, pTable, pTableAlias, pColumnOrFnMap)
{
var condition = new SqlCondition();
if (!pFilter)
return condition;
if (!pColumnOrFnMap)
pColumnOrFnMap = {};
_addCondition.call(condition, pFilter, pFilter.operator);
return condition;
//recursive function (for compatibility with a condition tree) that
//builds the SqlCondition
function _addCondition (pCondition, pOperator)
{
if (pCondition.type == "row")
{
if (pCondition.name in pColumnOrFnMap)
pCondition.name = pColumnOrFnMap[pCondition.name];
else if (pTable && pTableAlias)
pCondition.name = [pTable, pCondition.name, pTableAlias];
else if (pTable)
pCondition.name = pTable + "." + pCondition.name;
pCondition.value = (pCondition.key || pCondition.value);
var condition;
if (typeof(pCondition.name) === "function")
{
condition = pCondition.name.call(null, pCondition.value, pCondition.operator);
if (pOperator == "AND")
this.andSqlCondition(condition);
else if (pOperator == "OR")
this.orSqlCondition(condition);
}
else
{
condition = _getCondition.call(pCondition, pCondition.value, pCondition.operator);
if (pOperator == "AND")
this.andPrepare(pCondition.name, pCondition.value, condition);
else if (pOperator == "OR")
this.orPrepare(pCondition.name, pCondition.value, condition);
}
}
else if (pCondition.type == "group")
{
let subCondition = new SqlCondition();
let operator = pCondition.operator;
pCondition.childs.forEach(function (cond)
{
_addCondition.call(subCondition, cond, operator);
});
if (pOperator == "AND")
this.andSqlCondition(subCondition);
else if (pOperator == "OR")
this.orSqlCondition(subCondition);
}
}
//returns the condition depending on the operator and
//adds wildcards to the value if necessary
function _getCondition (pValue, pOperator)
{
switch (pOperator)
{
case "CONTAINS":
this.value = "%" + pValue + "%";
return "# like ?";
case "CONTAINSNOT":
this.value = "%" + pValue + "%";
return "# not like ?";
case "STARTSWITH":
this.value = pValue + "%";
return "# like ?";
case "ENDSWITH":
this.value = "%" + pValue;
return "# like ?";
case "EQUAL":
return "# = ?";
case "NOT_EQUAL":
return "# != ?";
case "LESS":
return "# < ?";
case "LESS_OR_EQUAL":
return "# <= ?";
case "GREATER":
return "# > ?";
case "GREATER_OR_EQUAL":
return "# >= ?";
case "ISNULL":
return "# is null";
case "ISNOTNULL":
return "# is not null";
}
}
}
return JditoFilterUtils; //return only functions that should be public
})();
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment