Something went wrong on our end
-
move rebuilding all to serverProcess since it will allways be executed via an asyncronous serverProcess
move rebuilding all to serverProcess since it will allways be executed via an asyncronous serverProcess
process.js 11.91 KiB
import("system.indexsearch");
import("system.neon");
import("system.text");
/**
* provides static methods for special handling of entities in JDito-Processes
* do not create an instance of this
*
* @class
*/
function IndexsearchUtils() {}
/**
* Searches within an affectedInfoContainer for an id value <br/>
* This id value can be used in the affectedIds-process of an index-recordContainer for example <br/>
*
* Depending on the provided action the given ID-field is searched in: <br/>
* <ul>
* <li>the newValues (action: I), no other values are yet present</li>
* <li>the oldValues (action: D), other values are not anymore present</li>
* <li>another source by calling a function (action: U), in the update case only values are provided that acutally changed so
* another source (usually the dattabase) is needed to perform the lookup</li>
* <li>Otherwise an empty array is given (action X for example)</li>
* </ul>
*
* @param {String} fieldname <p> Name of the field that shall be searched within the infocontainer
* @param {Object} affectedInfoContainer <p> affectedInfoContainer like it is provided by the IndexsearchUtils.createAffectedInfoContainer-function
*
* @param {Function} updateFn <p> a callback function that is called when an update-action is provided in the affectedInfoContianer
* (which means there are no [useful] newvalues/oldvalues)
*
* @return {Array} <p> 1D-array of Strings with all affectedIds that where found depeding on the information in the
* affectedInfoContainer
* @example
* //example for an affectedIds-procces for a indexer that is built on top of the database table OFFER
* //explanations of the example are written as code-comments
* ...imports, variables and more code here...
* //this is always the uid of the record that has been changed, so if an OFFER-entry has been changed the OFFERID is returned, if an OFFERITEM-entry
* //has been changed the OFFERITEMID is returned and so on
* var idValue = vars.get("$local.idvalue");
* var infoContainer = ...code for creating a infoContainer...
*
* switch (tableName)//the tableName is always the name of the DB table that has been changed (changed = inserted, updated or deleted)
* {
* //if the OFFER itself has been changed it's very easy because it's possible to return the OFFERID directly
* case "OFFER":
* res = [idValue];
* break;
* //for other items (like the OFFERITEM in this exmaple) however it is not that easy, because the index-UID is needed as result in the
* //affectedIdsProcess, so we have find the correct OFFERID for the OFFERITEM that has been changed
* // therefore we have to define the columnname of the index-UID we are searching within our table (here: OFFER_ID) and we have to define a
* //callback function that returns the index-UID with for the given ID of the record that has been changed [to see why we need a function see the
* //descriptiontext of this function]
* case "OFFERITEM":
* res = IndexsearchUtils.getAffectedIdValues("OFFER_ID", infoContainer, function (offerItemId){
* return newSelect("OFFERITEM.OFFER_ID")
* .from("OFFERITEM")
* .where("OFFERITEM.OFFERITEMID", offerItemId)
* .arrayColumn();
* });
* break;
* }
*
* //this needed as a savegate, because the Index is being rebuilt by the indexer if nothing gets returned => do a final check here!
* if (res)
* result.object(res);
* else
* result.object([]);
*/
IndexsearchUtils.getAffectedIdValues = function(fieldname, affectedInfoContainer, updateFn) {
var affectedIds;
switch (affectedInfoContainer.action)
{
case "I":
affectedIds = affectedInfoContainer.newValues[affectedInfoContainer.columns.indexOf(fieldname)];
affectedIds = affectedIds ? [affectedIds] : [];//check if affectedIds are present because otherwise we may would return [null]
break;
case "U":
affectedIds = updateFn.call(null, affectedInfoContainer.id);
break;
case "D":
affectedIds = affectedInfoContainer.oldValues[affectedInfoContainer.columns.indexOf(fieldname)];
affectedIds = affectedIds ? [affectedIds] : [];//check if affectedIds are present because otherwise we may would return [null]
break;
}
if(affectedIds)
return affectedIds;
else
return [];
}
/**
* Builds an object with various properties and returns the newly created object.
* The object that is retruned is so called affectedInfoContainer, but the type and prototype is still object.<br/>
* <br/>
* Each object represents one audited change in a db table with a sepcific type (insert, update, delete, etc.) for one row.
* The object is basically a unified blueprint for such a change.<br/>
* <br/>
* Main purpose of the object is to make the handling of given values within a affectedIdProcess in an entity-indexRecordContainer easier.
* However it could be used in other cases too, for example in the auditProcess.
*
* @param {String} changedIdValue <p> UID of the row where the change has been audited
* @param {String} changedTable <p> name of the db table where the change has been audited
* @param {String} action <p> action type of the audited change in short form, for exmaple: I, U, D, ...
* @param {Function} columnsFn <p> callbackFunction that has to return an array with the column names of the audited table
* @param {Function} oldValueFn <p> callbackFunction that has to return an array with the new values that exist after the audited change
* @param {Function} newValueFn <p> callbackFunction that has to return an array with the old values that have existed before the audited change
*
* @return {Object} object with the following properties (values depend on the parameters):
* <ul>
* <li>id: uid of the changed record</li>
* <li>table: name of the database table of the changed record</li>
* <li>action: type of the change, this is usually "I" for insert, "U" for update, "D" for delete and "X" for unknown</li>
* <li>columns: array of column names of the table that have been audited (if present)</li>
* <li>oldValues: detemined values before the change (if present)</li>
* <li>newValues: detemined values after the change (if present)</li>
* </ul>
* <br/>
* Note that this only what the object SHOULD contain, the actual values depend on the input parameters.
*
*/
IndexsearchUtils.createAffectedInfoContainer = function(changedIdValue, changedTable, action, columnsFn, oldValueFn, newValueFn) {
var res, internalStorage;
internalStorage = {};
res = {
id: changedIdValue
,table: changedTable
,action: action
,columns: null //null for autocomplete in the ADITO-designer
,oldValues: null
,newValues: null
};
Object.defineProperty(res, "columns", {
get: function(){
if (internalStorage["columns"] == undefined)
internalStorage["columns"] = columnsFn.call(null);
return internalStorage["columns"];
}
,set: function (v){
internalStorage["columns"] = v;
}
});
Object.defineProperty(res, "oldValues", {
get: function(){
if (internalStorage["oldValues"] == undefined)
internalStorage["oldValues"] = oldValueFn.call(null);
return internalStorage["oldValues"];
}
,set: function (v){
internalStorage["oldValues"] = v;
}
});
Object.defineProperty(res, "newValues", {
get: function(){
if (internalStorage["newValues"] == undefined)
internalStorage["newValues"] = newValueFn.call(null);
return internalStorage["newValues"];
}
,set: function (v){
internalStorage["newValues"] = v;
}
});
return res;
}
/**
* Static utility class for constructing an indexsearch pattern
*/
function IndexsearchFilterUtils() {}
/**
* Creates an indexsearch pattern from a filter
*
* @param {object} pFilter the filter in json representation
*
* @returns {IndexsearchFilterGroup|IndexsearchFilterRow} the IndexSearchFilter object
*/
IndexsearchFilterUtils.fromFilter = function(pFilter)
{
if(pFilter["type"] == "group")
{
return IndexsearchFilterGroup.fromFilter(pFilter["childs"], pFilter["operator"]);
}
else if(pFilter["type"] == "row")
{
return IndexsearchFilterRow.fromFilter(pFilter["name"], pFilter["operator"], pFilter["value"]);
}
throw new Error("Unknown filter node type: " + pFilter["type"]);
}
/**
* The IndexsearchFilterGroup object represents the a filter group
* and is able to generate the corrosponding indexsearch pattern
*
* @param {(IndexsearchFilterGroup|IndexsearchFilterRow)[]} pChilds the child filterrows/filtergroups
* @param {string} pOperator the operator for the group can either be 'AND' or 'OR'
*/
function IndexsearchFilterGroup(pChilds, pOperator)
{
this.childs = pChilds;
this.operator = pOperator;
}
/**
* Returns all fiels as Set witch are required for this filter
*
* @returns {Set<string>} The fields as Set
*/
IndexsearchFilterGroup.prototype.getFields = function()
{
var fields = new Set();
for(let i = 0; i < this.childs.length; i++)
{
this.childs[i].getFields().forEach(fields.add, fields);
}
return fields;
}
/**
* Builds the index pattern and returns it as string
*
* @param {Record<string, string>} pFieldValues the field name value pair
*/
IndexsearchFilterGroup.prototype.buildQuery = function(pFieldValues)
{
return this.childs.map(function(curr) { return curr.buildQuery(pFieldValues); })
.filter(function(curr) { return curr != null; })
.join(" " + this.operator + " ");
}
/**
* Creates a new IndexsearchFilterGroup object
*
* @param {(IndexsearchFilterGroup|IndexsearchFilterRow)[]} pChilds the child filterrows/filtergroups
* @param {string} pOperator the operator for the group can either be 'AND' or 'OR'
*/
IndexsearchFilterGroup.fromFilter = function(pChilds, pOperator)
{
return new IndexsearchFilterGroup(
pChilds.map(function(curr)
{
return IndexsearchFilterUtils.fromFilter(curr);
}),
pOperator
);
}
function IndexsearchFilterRow(pName, pEmpty, pExclude)
{
this.name = pName;
this.empty = pEmpty;
this.exclude = pExclude;
}
/**
* Returns all fiels as Set witch are required for this filter
*
* @returns {Set<string>} The fields as Set
*/
IndexsearchFilterRow.prototype.getFields = function()
{
return new Set([this.name]);
}
/**
* Builds the index pattern and returns it as string
*
* @param {Record<string, string>} pFieldValues the field name value pair
*/
IndexsearchFilterRow.prototype.buildQuery = function(pFieldValues)
{
var fieldValue = pFieldValues[this.name] ? pFieldValues[this.name] : "";
if(!this.empty && fieldValue == "")
{
return null;
}
var valueStr = fieldValue;
for(let i = 0; i < this.exclude.length; i++)
{
valueStr = valueStr.replace(new RegExp(this.exclude[i], "gi"), "");
}
return this.name.toLowerCase() + ':("' + indexsearch.escapeString(valueStr.trim().replace(/\s+/g, "")) + '")';
}
/**
* Creates a new IndexsearchFilterGroup object
*
* @param {string} pName the row field name
* @param {string} pOperator the operator for the group can either be 'AND' or 'OR'
* @param {string} pValue the field valze
*/
IndexsearchFilterRow.fromFilter = function(pName, pOperator, pValue)
{
if(pOperator == "NOT_EQUAL" || pOperator == "CONTAINSNOT")
{
return new IndexsearchFilterRow(pName, true, JSON.parse(pValue));
}
else if(pOperator == "ISNOTNULL")
{
return new IndexsearchFilterRow(pName, false, []);
}
throw new Error("Unknown filterrow operator: " + pOperator);
}