Name

sn_ci_analytics.VAConditionBuilderHelper

Description

Helper methods to convert Now Condition Builder generated query to Appsee Query Builder and Funnel. The script is used by VAConditionBuilderUtil and internally uses VAConditionBuilderMap .

Script

var VAConditionBuilderHelper = Class.create();
VAConditionBuilderHelper.prototype = {
  initialize: function() {
      this.va_constants = new VAConditionBuilderMap();
  },
  fail: function(errorCode) {
      var _errors = {
          'invalid_query': 'INVALID_QUERY',
          'invalid_comparison_type': 'INVALID_COMPARISON_TYPE',
          'invalid_steps': 'INVALID_FUNNEL_BUILDER_QUERY'
      };
      return {
          'success': false,
          'query': null,
          'error': _errors[errorCode] ? _errors[errorCode] : null
      };
  },
  success: function(payload) {
      return {
          'success': true,
          'query': payload,
          'error': null
      };
  },
  _hasChildren: function(children) {
      if (!children) return false;
      if (Array.isArray(children) && children.length > 0) return true;
      return false;
  },
  _cloneObject: function(obj) {
      return JSON.parse(JSON.stringify(obj));
  },
  _dotWalk: function(q, dotWalkKeys) {
      dotWalkKeys.forEach(function(path) {
          //Regex to parse Array. Eg. and[0] => and, 0
          var hasArrayIndex = /(.*)\[(.*)\]$/g.exec(path);
          if (hasArrayIndex && hasArrayIndex[1] && hasArrayIndex[2]) {
              q = q[hasArrayIndex[1]][Number(hasArrayIndex[2])];
          } else {
              q = q[path];
          }
      });
      return q;
  },
  _assignOperatorAndValue: function(table, internalField, internalOperatorObject, conditionValue, onlyConditionValue) {
      var value = null;
      var internalOperatorType = internalOperatorObject.type;
      switch (internalOperatorType) {
          case 'Array':
              value = conditionValue ? (Array.isArray(conditionValue) ? conditionValue : Array(conditionValue)) : Array();
              break;
          case 'Boolean':
              value = conditionValue ? (conditionValue.toString() == 'false' ? false : Boolean(conditionValue)) : Boolean();
              break;
          case 'String':
              value = conditionValue ? String(conditionValue) : String();
              break;
          case 'Number':
              value = conditionValue ? Number(conditionValue) : Number();
              break;
          default:
              value = conditionValue;
      }

      return onlyConditionValue ? value : this._attachOperator(table, internalField, internalOperatorObject, value);
  },
  _attachOperator: function(table, internalField, internalOperatorObject, computedValue) {
      var _self = this;
      var operatorModifer = this.va_constants.OPERATOR_MODIFIERS[table] &&
          this.va_constants.OPERATOR_MODIFIERS[table][internalField] &&
          (this.va_constants.OPERATOR_MODIFIERS[table][internalField][internalOperatorObject.to] || this.va_constants.OPERATOR_MODIFIERS[table][internalField]);

      if (operatorModifer) {
          switch (typeof operatorModifer) {
              case 'function':
                  return operatorModifer(computedValue);
              case 'string':
                  var utilityModifier = _self.va_constants.UTILITY_FUNCTIONS()[operatorModifer];
                  return utilityModifier(computedValue, internalOperatorObject.to);
          }
      }
      var result = {};
      result[internalOperatorObject.to] = computedValue;
      return result;
  },


  /**
   * 
   * @param {array} vaComparisonRows 
   * @returns {string} Appsee internal comparison type
   *          returns default if vaComparisonRows.length = 1
   *          returns computed vaComparisonRows[1].comparisonType if vaComparisonRows.length > 1
   */
  _computeRowOperator: function(vaComparisonRows) {
      if (!vaComparisonRows || !Array.isArray(vaComparisonRows)) return null;
      try {
          return (vaComparisonRows.length <= 1 ?
              this.va_constants.COMPARISON_TYPE_DEFAULT() :
              this.va_constants.COMPARISON_TYPES[vaComparisonRows[1][this.va_constants.VA_KEY.comparison_type]]).to;
      } catch (e) {
          return null;
      }
  },


  /**
   * 
   * @param {string} internalField 
   * @param {string} table 
   * @returns {object} Preset pattern
   */
  _computePatternType: function(internalField, table) {
      try {
          var richPattern = this.va_constants.FIELDS[table][internalField];

          //Regex to extract pattern and appsee_key from
          //Eg. 'STANDARD_EQ#SessionLocale' => STANDARD_EQ, SessionLocale
          var extractPattern = /(^.*)[#](.*$)/g.exec(richPattern);
          if (!extractPattern || !extractPattern[1] || !extractPattern[2]) return {
              'pattern': richPattern,
              'appsee_key': null
          };
          return {
              'pattern': extractPattern[1],
              'appsee_key': extractPattern[2]
          };
      } catch (e) {
          return null;
      }
  },


  /**
   * 
   * @param {object} comparisonRow 
   * @param {string} table 
   * @returns {string} internalField (field || overriden field)
   */
  _getInternalFieldValue: function(comparisonRow, table) {
      var va_keys = this.va_constants.VA_KEY;
      var internalField = comparisonRow[va_keys.field];
      if (!internalField) return null;
      return this.va_constants.FIELD_ALIAS[table][internalField] || internalField;
  },


  /**
   * 
   * @param {object} comparisonRow 
   * @param {string} table 
   * @returns {object} internalField (field || overriden field)
   */
  _getInternalOperatorObject: function(comparisonRow) {
      var va_keys = this.va_constants.VA_KEY,
          _self = this;
      var comparisonModelOperator = comparisonRow[va_keys.operator];
      if (!comparisonModelOperator || !comparisonModelOperator[va_keys.operator_value] || !comparisonModelOperator[va_keys.operator_type])
          return null;
      return {
          'to': _self.va_constants.OPERATORS[comparisonModelOperator[va_keys.operator_value]] || comparisonModelOperator[va_keys.operator_value],
          'type': comparisonModelOperator[va_keys.operator_type]
      };
  },

  _getInternals: function(comparisonRow, table) {
      var internalField = this._getInternalFieldValue(comparisonRow, table);
      if (!internalField) return null;

      var internalOperatorObject = this._getInternalOperatorObject(comparisonRow);
      if (!internalOperatorObject || !internalOperatorObject.type) return null;

      return {
          field: internalField,
          operator_object: internalOperatorObject
      };
  },

  //overrides all the available keys with values from incomingObj to originalObj. Rest keys are un-touched
  _deepReplace: function(originalObj, incomingObj) {
      var _self = this;
      for (var key in incomingObj) {
          if (Array.isArray(incomingObj[key])) {
              if (incomingObj[key].length > 0) {
                  incomingObj[key].forEach(function(incomingChildObj, index) {
                      return _self._deepReplace(originalObj[key][index], incomingChildObj);
                  });
              } else {
                  originalObj[key] = incomingObj[key]; //to copy children []
              }
          } else {
              originalObj[key] = incomingObj[key];
          }

      }
      return originalObj;
  },
  _standardEqProcessor: function(patternObject, internalField, internalOperatorObject, conditionValue, table) {
      try {
          var query = {};
          query[patternObject.appsee_key] = {};
          query[patternObject.appsee_key] = this._assignOperatorAndValue(table, patternObject.appsee_key, internalOperatorObject, conditionValue);
          return query;
      } catch (e) {
          return null;
      }
  },
  _customEventsEqProcessor: function(patternType, internalField, internalOperatorObject, conditionValue, table, children) {
      var query = null,
          _self = this;
      var patternDefinitions = this.va_constants.APPSEE_QUERY_PATTERNS.definitions;
      var baseStructure = patternDefinitions[patternType].structure;
      query = this._cloneObject(baseStructure);
      var q = query,
          dotWalkKeys = patternDefinitions[patternType].dotwalk_path;
      q = this._dotWalk(q, dotWalkKeys);
      q.push({
          'Name': this._assignOperatorAndValue(table, internalField, internalOperatorObject, conditionValue)
      });
      if (this._hasChildren(children)) {
          var childrenComparisonType = this._computeRowOperator(children);
          var properties = {};
          properties[childrenComparisonType] = [];
          children.forEach(function(eachChild) {
              var internal = _self._getInternals(eachChild, table);
              var innerProps = {};
              innerProps[internal.field] = {};
              innerProps[internal.field] = _self._assignOperatorAndValue(table, internal.field, internal.operator_object, eachChild[_self.va_constants.VA_KEY.condition_value]);
              properties[childrenComparisonType].push(innerProps);
          });
          q.push(patternDefinitions[patternType]['properties']);
          q = query;
          dotWalkKeys = patternDefinitions[patternType].properties_dotwalk_path;
          q = this._dotWalk(q, dotWalkKeys);
          q.nested_match = properties;
      }
      return query;
  },
  _dynamicPropEqProcessor: function(patternType, internalField, internalOperatorObject, conditionValue, table) {
      try {
          var query = null;
          var patternDefinitions = this.va_constants.APPSEE_QUERY_PATTERNS.definitions;
          var baseStructure = patternDefinitions[patternType].structure;
          query = this._cloneObject(baseStructure);
          var q = query,
              dotWalkKeys = patternDefinitions[patternType].dotwalk_path;
          q = this._dotWalk(q, dotWalkKeys);
          q[internalField] = this._assignOperatorAndValue(table, internalField, internalOperatorObject, conditionValue);
          return query;
      } catch (e) {
          return null;
      }
  },
  _customTopicNodeEqProcessor: function(patternType, comparisonRow, table) {
      try {
          var query = null,
              va_keys = this.va_constants.VA_KEY;
          var patternDefinitions = this.va_constants.APPSEE_QUERY_PATTERNS.definitions;
          var baseStructure = patternDefinitions[patternType].structure;
          query = this._cloneObject(baseStructure);
          var q = query,
              dotWalkKeys = patternDefinitions[patternType].dotwalk_path;
          q = this._dotWalk(q, dotWalkKeys);
          var internal = this._getInternals(comparisonRow, table);
          if (!internal) return null;
          q.ScreenId = this._assignOperatorAndValue(table, internal.field, internal.operator_object, comparisonRow[va_keys.condition_value]);
          return query;
      } catch (e) {
          return null;
      }
  },
  _mixedTopicEqProcessor: function(patternType, internalField, internalOperatorObject, conditionValue, table) {
      try {
          var query = null;
          var patternDefinitions = this.va_constants.APPSEE_QUERY_PATTERNS.definitions[patternType];
          var _map = patternDefinitions.maps[internalField];
          var baseStructure = patternDefinitions[_map.structure];
          query = this._cloneObject(baseStructure);
          var q = query,
              dotWalkKeys = patternDefinitions[_map.dotwalk_path],
              addInternalfield = _map.add_internal_field,
              dotWalkLastKey = _map.dotwalk_last_key,
              mixers = patternDefinitions.mixers;
          q = this._dotWalk(q, dotWalkKeys);

          var result = {},
              r;

          switch (internalOperatorObject.to) {
              case mixers.closed:
                  r = this._assignOperatorAndValue(table, internalField, internalOperatorObject, conditionValue);
                  if (addInternalfield)
                      result[internalField] = r;
                  else
                      result = r;
                  break;
              case mixers.open:
                  var userValues = this._assignOperatorAndValue(table, internalField, internalOperatorObject, conditionValue, true);
                  var _self = this;
                  var mixed = [];
                  userValues.forEach(function(userValue) {
                      var _defaultOperator = _self.va_constants.DEFAULT_OPERATOR;
                      r = _self._assignOperatorAndValue(table, internalField, _defaultOperator, userValue);
                      result = {};
                      if (addInternalfield)
                          result[internalField] = r;
                      else
                          result = r;
                      if (dotWalkLastKey) {
                          q[dotWalkLastKey] = result;
                      } else {
                          q.and = [result];
                      }
                      mixed.push(_self._cloneObject(query));
                  });
                  return mixed;
              default:
                  return null;
          }
          if (dotWalkLastKey) {
              q[dotWalkLastKey] = result;
          } else {
              q.and.push(result);
          }
          return query;
      } catch (e) {
          return null;
      }
  },
  _customTopicEqProcessor: function(patternType, comparisonRow, table) {
      /* Compute Topic Type. Can be either of 4 types
       *  If Topic has no children == topic_only
       *  If Topic has 1 cascaded children and field = 'Last Node Visited' === topic_with_completion_status
       *  If Topic has 1 cascaded children and field = 'Topic Node' === topic_with_topic_node
       *  If Topic has 1 cascaded children and field = 'Last Visited Node Name' === topic_last_visited_node_name
       *  If Topic has 2 level cascaded children === topic_choice
       */
      var query = [],
          _self = this,
          va_keys = this.va_constants.VA_KEY;
      var patternDefinitions = this.va_constants.APPSEE_QUERY_PATTERNS.definitions[patternType];
      var preSetFields = patternDefinitions.preset_fields;
      var topicTypes = patternDefinitions.topic_types;
      var configType = null;
      if (this._hasChildren(comparisonRow[va_keys.children])) {
          var child = comparisonRow[va_keys.children][0];
          var internalField = this._getInternalFieldValue(child, table);
          if (this._hasChildren(child[va_keys.children])) {
              var grandChild = child[va_keys.children][0];
              var gcInternalField = this._getInternalFieldValue(grandChild, table);
              if (gcInternalField == preSetFields.selected_value) {
                  configType = topicTypes.topic_choice;
              }
          } else if (internalField == preSetFields.last_visited_node) {
              configType = topicTypes.topic_with_completion_status;
          } else if (internalField == preSetFields.topic_node) {
              configType = topicTypes.topic_with_topic_node;
          } else if (internalField == preSetFields.last_visited_node_name) {
              configType = topicTypes.topic_last_visited_node_name;
          }
      } else {
          configType = topicTypes.topic_only;
      }
      if (!configType) return null;

      var configs = patternDefinitions.configurations[configType];
      configs.forEach(function(config) {
          if (config)
              query.push(_self._processComparisonModelByType(_self._deepReplace(_self._cloneObject(comparisonRow), config), table));
      });
      return query;
  },
  _extractParentNodeId: function(comparisonRow) {
      try {
          var va_keys = this.va_constants.VA_KEY;
          return comparisonRow[va_keys.children][0][va_keys.condition_value].screenName[0].split('/')[0];
      } catch (e) {
          return '';
      }
  },
  _parseValue: function(value) {
      try {
          return JSON.parse(value);
      } catch (e) {
          return value;
      }
  },
  _customTopicChoiceEqProcessor: function(patternType, comparisonRow, table) {
      var query = null,
          va_keys = this.va_constants.VA_KEY;
      var patternDefinitions = this.va_constants.APPSEE_QUERY_PATTERNS.definitions[patternType];
      var replacers = patternDefinitions.replacer;

      //TODO: clean the mapping
      var clonedComparisonRow = this._cloneObject(comparisonRow);
      clonedComparisonRow.conditionValue = [clonedComparisonRow.children[0].children[0].conditionValue.topic_node_name];

      query = this._processComparisonModelByType(this._deepReplace(clonedComparisonRow, replacers), table);
      var q = query,
          dotWalkKeys = patternDefinitions.dotwalk_path;
      q = this._dotWalk(q, dotWalkKeys);
      q.and = [];

      //fetch the required values. No hard check required. If exception, handled in parent.
      var requiredKeys = patternDefinitions.prop_labels,
          requiredvalues = {};
      requiredvalues.type = clonedComparisonRow[va_keys.children][0][va_keys.condition_value].nodeType; //fetch the value from parent's conditionValue
      requiredvalues.selected_value = this._parseValue(clonedComparisonRow[va_keys.children][0][va_keys.children][0][va_keys.condition_value].selected_value);
      requiredvalues.topic_node_id = this._extractParentNodeId(clonedComparisonRow);
      for (var key in requiredKeys) {
          var keyLabel = requiredKeys[key],
              prop = {};
          prop[keyLabel] = {
              'eq': requiredvalues[key]
          };
          q.and.push(prop);
      }
      return query;
  },
  _conversationQueryEqProcessor: function(patternType, conversationComparisonRows, table) {
      var query = null;
      var patternDefinitions = this.va_constants.APPSEE_QUERY_PATTERNS.definitions[patternType];
      var baseStructure = patternDefinitions.structure;
      query = this._cloneObject(baseStructure);
      var q = query,
          dotWalkKeys = patternDefinitions.dotwalk_path;
      q = this._dotWalk(q, dotWalkKeys);
      var innerResults = this.transformConditionBuilder(conversationComparisonRows, table);
      q.child_match = innerResults;
      return query;
  },
  _setupFunnelAlternativeStructure: function(baseComparisonModel) {
      var _self = this,
          internalField = {},
          funnelMetadata = this.va_constants.FIELDS.va_funnels,
          va_keys = this.va_constants.VA_KEY;
      try {
          internalField = funnelMetadata.step_types[baseComparisonModel[va_keys.field]];
          if (!internalField) return null;
          var struct = _self._cloneObject(funnelMetadata.alternative_structure);
          struct.StepType = internalField.tagged_enum;
          if (internalField.has_value) {
              struct.Value = baseComparisonModel[va_keys.condition_value].toString();
          }
          if (baseComparisonModel[va_keys.children] && Array.isArray(baseComparisonModel[va_keys.children])) {
              baseComparisonModel[va_keys.children].forEach(function(baseComparisonModelChild, index) {
                  struct.ExtraValues[baseComparisonModelChild[va_keys.field]] = baseComparisonModelChild[va_keys.condition_value];
              });
          }
          return struct;
      } catch (e) {
          return null;
      }
  },
  _funnelBuilderEqProcessor: function(vaFunnelBuilderRow) {
      var steps = [],
          _self = this,
          funnelBase = {},
          funnelAlternative = null,
          funnelMetadata = this.va_constants.FIELDS.va_funnels,
          va_keys = this.va_constants.VA_KEY;
      try {
          funnelBase = _self._cloneObject(funnelMetadata.base_structure);
          funnelBase[va_keys.step_number] = parseInt(vaFunnelBuilderRow[va_keys.step_number]);
          vaFunnelBuilderRow.VAComparisonModel.forEach(function(VaComparisonModelRow, innerIndex) {
              funnelAlternative = _self._setupFunnelAlternativeStructure(VaComparisonModelRow);
              if (funnelAlternative)
                  funnelBase.Alternatives.push(funnelAlternative);
          });
          steps.push(funnelBase);
          return steps;
      } catch (e) {
          return null;
      }
  },


  /**
   * 
   * @param {object} comparisonRow 
   * @param {string} table 
   * @returns {object} appsee internal structure
   */
  _processComparisonModelByType: function(comparisonRow, table) {
      try {
          var va_keys = this.va_constants.VA_KEY,
              _self = this;
          var tables = this.va_constants.TABLES;
          switch (table) {
              case tables.va_conversations:
              case tables.va_users:
                  var internal = this._getInternals(comparisonRow, table);
                  if (!internal) return null;
                  var internalField = internal.field;
                  var internalOperatorObject = internal.operator_object;

                  var patternTypeObject = this._computePatternType(internalField, table);
                  if (!patternTypeObject) return null;

                  var patterns = this.va_constants.APPSEE_QUERY_PATTERNS.supported_patterns;
                  if (!patterns) return null;
                  switch (patternTypeObject.pattern) {
                      case patterns.custom_events_eq:
                          return _self._customEventsEqProcessor(patternTypeObject.pattern, internalField, internalOperatorObject, comparisonRow[va_keys.condition_value], table, comparisonRow[va_keys.children]);
                      case patterns.standard_eq:
                          return _self._standardEqProcessor(patternTypeObject, internalField, internalOperatorObject, comparisonRow[va_keys.condition_value], table);
                      case patterns.dynamic_prop_eq:
                          return _self._dynamicPropEqProcessor(patternTypeObject.pattern, internalField, internalOperatorObject, comparisonRow[va_keys.condition_value], table);
                      case patterns.mixed_topic_eq:
                          return _self._mixedTopicEqProcessor(patternTypeObject.pattern, internalField, internalOperatorObject, comparisonRow[va_keys.condition_value], table);
                      case patterns.custom_topic_eq:
                          return _self._customTopicEqProcessor(patternTypeObject.pattern, comparisonRow, table);
                      case patterns.custom_topic_node_eq:
                          return _self._customTopicNodeEqProcessor(patternTypeObject.pattern, comparisonRow[va_keys.children][0], table);
                      case patterns.conversation_query_eq:
                          return _self._conversationQueryEqProcessor(patternTypeObject.pattern, comparisonRow[va_keys.children], tables.va_conversations);
                      case patterns.custom_topic_choice_eq:
                          return _self._customTopicChoiceEqProcessor(patternTypeObject.pattern, comparisonRow, table);
                      default:
                          return null;
                  }
              case tables.va_funnels:
                  return _self._funnelBuilderEqProcessor(comparisonRow);
              default:
                  return null;
          }
      } catch (e) {
          return null;
      }
  },
  _processComparisonModel: function(vaComparisonRows, table) {
      var query = null,
          result = [],
          _self = this;
      vaComparisonRows.forEach(function(vaComparisonRow, vaComparisonRowIndex) {
          query = _self._processComparisonModelByType(vaComparisonRow, table);
          if (query) {
              if (Array.isArray(query)) {
                  query.forEach(function(_q) {
                      result.push(_q);
                  });
              } else {
                  result.push(query);
              }
          }
      });
      return result;
  },


  /**
   * 
   * @param {array} vaComparisonRows 
   * @param {string} table 
   * @returns {object} appsee query
   */
  transformConditionBuilder: function(vaComparisonRows, table) {
      var _self = this,
          tables = this.va_constants.TABLES;
      try {
          var comparisonType = _self._computeRowOperator(vaComparisonRows);
          var result = _self._processComparisonModel(vaComparisonRows, table);
          if (table == tables.va_funnels) return result;
          var response = {};
          response[comparisonType] = result;
          return response;
      } catch (e) {
          return null;
      }
  },
  type: 'VAConditionBuilderHelper'
};

Sys ID

9e9c7a801bfa20904129415bbc4bcb4f

Offical Documentation

Official Docs: