Name

sn_ci_analytics.VAComparisonModelEncodedQueryUtils

Description

Utility to convert VA Comparison Model (JSON) to VA URI (STRING) and vice-versa. API decodeVAURIQuery({string} encodedString, {boolean} exposeSkeleton) - Converts VA Encoded String (encodedString) to VA Comparison JSON Model. To expose intermediate Node structure, use exposeSkeleton = true encodeVAURIQuery({object} jsonQuery) - Converts VA Comparison Model (jsonQuery) to VA Encoded String EXAMPLE decodeVAURIQuery() INPUT Self%20solved/matches/false/NEW_QUERY+Feedback/is%20one%20of/%5B%22Good%22%2C%22Bad%22%5D/AND+Events/is/Bot%20Output/AND>Prop1/equals/%7B%22event_trigger%22%3Atrue%2C%22api_key%22%3A%22ABDABMASBKJT7843BF7834YT8B78FB373%22%7D/IN_WHICH+Prop2/is/Completed/OR^+Duration/%3E/20000/AND OUTPUT { field Self solved , operator matches , conditionValue false, comparisonType NEW_QUERY , children }, { field Feedback , operator is one of , conditionValue Good , Bad , comparisonType AND , children }, { field Events , operator is , conditionValue Bot Output , comparisonType AND , children { field Prop1 , operator equals , conditionValue { event_trigger true, api_key ABDABMASBKJT7843BF7834YT8B78FB373 }, comparisonType IN_WHICH , children }, { field Prop2 , operator is , conditionValue Completed , comparisonType OR , children } }, { field Duration , operator > , conditionValue 20000, comparisonType AND , children } EXAMPLE encodeVAURIQuery() INPUT { field Self solved , operator matches , conditionValue false, comparisonType NEW_QUERY , children }, { field Feedback , operator is one of , conditionValue Good , Bad , comparisonType AND , children }, { field Events , operator is , conditionValue Bot Output , comparisonType AND , children { field Prop1 , operator equals , conditionValue { event_trigger true, api_key ABDABMASBKJT7843BF7834YT8B78FB373 }, comparisonType IN_WHICH , children }, { field Prop2 , operator is , conditionValue Completed , comparisonType OR , children } }, { field Duration , operator > , conditionValue 20000, comparisonType AND , children } OUTPUT Self%20solved/matches/false/NEW_QUERY+Feedback/is%20one%20of/%5B%22Good%22%2C%22Bad%22%5D/AND+Events/is/Bot%20Output/AND>Prop1/equals/%7B%22event_trigger%22%3Atrue%2C%22api_key%22%3A%22ABDABMASBKJT7843BF7834YT8B78FB373%22%7D/IN_WHICH+Prop2/is/Completed/OR^+Duration/%3E/20000/AND

Script

var VAComparisonModelEncodedQueryUtils = Class.create();
VAComparisonModelEncodedQueryUtils.prototype = {
  initialize: function() {
      this.nodeCounter = 0;
      this.currentNode = null;
      this.nodeStack = [];
      this.skeletonObject = {
          registers: [],
          graph: {},
          nodes: {},
          insertionCursor: {}
      };
  },
  PARSER_CONSTANTS: {
      symbols: ['/', '+', '>', '^', '\n'],
      orderOfWords: ['field', 'operator', 'conditionValue', 'comparisonType', 'children'],
      has_children: '=HAS_CHILDREN',
      children: 'children',
      field: 'field',
      comparison_type: 'comparisonType',
      signal: {
          'SIBLING_SEPERATOR': '+',
          'ELEMENT_SEPERATOR': '/',
          'STEP_INTO': '>',
          'STEP_OUT': '^',
          'EOS': '\n'
      },
      notations: ['NEW_QUERY', 'OR', 'AND', 'IN_WHICH', 'THAT_HAS']
  },
  _generateNodeID: function() {
      return 'NODE__' + (this.nodeCounter++);
  },
  _createNode: function(link) {
      var nodeId = this._generateNodeID();
      this.skeletonObject.registers.push({
          parent: link,
          node: nodeId
      });
      //create an array with order of words length elements
      //1. create it with one less element, fill with blank content
      //2. create the last element with an empty array (for children)

      //this.skeletonObject.nodes[nodeId] = Array(this.PARSER_CONSTANTS.orderOfWords.length - 1).fill(null);

      this.skeletonObject.nodes[nodeId] = [];
      for (var i = 0; i < this.PARSER_CONSTANTS.orderOfWords.length; i++) {
          i == this.PARSER_CONSTANTS.orderOfWords.length - 1 ? this.skeletonObject.nodes[nodeId].push([]) : this.skeletonObject.nodes[nodeId].push(null);

      }

      this.skeletonObject.graph[nodeId] = link;
      this.skeletonObject.insertionCursor[nodeId] = 0;
      this.nodeStack.push({
          parent: link,
          node: nodeId
      });
      return true;
  },
  _smartWordEvaluator: function(word, cursor) {
      word = decodeURIComponent(word);
      try {
          word = JSON.parse(word);
          if (cursor == this.PARSER_CONSTANTS.orderOfWords.indexOf(this.PARSER_CONSTANTS.comparison_type) && this.PARSER_CONSTANTS.notations.indexOf(word) == -1)
              return null;
          return typeof word == 'object' ? word : String(word);
      } catch (e) {
          if (cursor == this.PARSER_CONSTANTS.orderOfWords.indexOf(this.PARSER_CONSTANTS.comparison_type) && this.PARSER_CONSTANTS.notations.indexOf(word) == -1)
              return null;
          return typeof word == 'object' ? word : String(word);
      }

  },
  _registerToNode: function(nodeId, word) {
      if (this.skeletonObject.insertionCursor[nodeId] == (this.PARSER_CONSTANTS.orderOfWords.length)) return;
      var cursor = this.skeletonObject.insertionCursor[nodeId]++;
      this.skeletonObject.nodes[nodeId][cursor] = this._smartWordEvaluator(word, cursor);
  },
  _getCurrentNodeId: function(sendParentIdInstead) {
      if (this.nodeStack.length == 0) return null;
      return sendParentIdInstead ? this.nodeStack[this.nodeStack.length - 1].parent : this.nodeStack[this.nodeStack.length - 1].node;
  },
  _convertObjectToArray: function(object) {
      var responseArray = [];
      for (var key in object) {
          responseArray.push(object[key]);
      }
      return responseArray;
  },
  _cleanResponse: function(index, object) {
      if (index == object.length) {
          return object;
      }
      if (typeof(object[index].children) == 'object' && object[index].children != null) {
          object[index].children = this._convertObjectToArray(object[index].children);
          this._cleanResponse(0, object[index].children);
      }
      return this._cleanResponse(++index, object);
  },
  convertToVAComparisonModel: function() {
      var response = {},
          _self = this;
      _self.skeletonObject.registers.map(function(node, nodeIndex) {
          var _node = node.node,
              _parentNode = node.parent,
              _thisNodeStruct = {},
              val;
          _self.PARSER_CONSTANTS.orderOfWords.forEach(function(field, fieldIndex) {
              val = _self.skeletonObject.nodes[_node][fieldIndex] == undefined ? null : _self.skeletonObject.nodes[_node][fieldIndex];
              if (field == _self.PARSER_CONSTANTS.children && val == _self.PARSER_CONSTANTS.has_children) {
                  _thisNodeStruct[field] = {};
              } else if (field == _self.PARSER_CONSTANTS.field) {
                  _thisNodeStruct[field] = val;
              } else {
                  _thisNodeStruct[field] = val;
              }
          });

          if (_parentNode) { //to support nth level cascading
              var nodeTrackerPath = [_parentNode],
                  nodeTracker = 1,
                  cloneResponse = {};
              while (!response[_parentNode]) {
                  _parentNode = _self.skeletonObject.graph[_parentNode];
                  nodeTrackerPath.unshift(_parentNode);
              }
              cloneResponse = response[nodeTrackerPath[0]];
              while (nodeTracker < nodeTrackerPath.length) {
                  cloneResponse = cloneResponse.children[nodeTrackerPath[nodeTracker]];
                  nodeTracker++;
              }
              cloneResponse.children[_node] = _thisNodeStruct;
          } else {
              response[_node] = _thisNodeStruct;
          }

      });
      return this._cleanResponse(0, this._convertObjectToArray(JSON.parse(JSON.stringify(response))));
  },
  _encodeWord: function(word) {
      if (typeof word == 'string') return encodeURIComponent(word);
      return encodeURIComponent(JSON.stringify(word));
  },
  _encodeJSON: function(jsonQuery, startIndex, encodedString) {
      if (jsonQuery.length == startIndex) {
          return encodedString;
      }
      if (startIndex > 0) encodedString += this.PARSER_CONSTANTS.signal.SIBLING_SEPERATOR;
      var fieldValues = [];
      var _self = this;
      this.PARSER_CONSTANTS.orderOfWords.forEach(function(field, index) {
          if (field != _self.PARSER_CONSTANTS.children) {
              fieldValues.push(_self._encodeWord(jsonQuery[startIndex][field]));
          }
      });
      encodedString += fieldValues.join(this.PARSER_CONSTANTS.signal.ELEMENT_SEPERATOR);
      if (jsonQuery[startIndex].children && Array.isArray(jsonQuery[startIndex].children)) {
          var _children = this._encodeJSON(jsonQuery[startIndex].children, 0, '');
          if (_children)
              encodedString = encodedString +
              this.PARSER_CONSTANTS.signal.STEP_INTO +
              _children +
              this.PARSER_CONSTANTS.signal.STEP_OUT;
      }
      return this._encodeJSON(jsonQuery, ++startIndex, encodedString);
  },


  /**
   * Converts VA Comparison Model to VA Encoded String
   * @param {*} jsonQuery 
   * @returns String
   */

  //Currently used via client_script_include
  encodeVAURIQuery: function(jsonQuery) {
      if (!jsonQuery || typeof jsonQuery != 'object') return null;
      if (jsonQuery.length == 0) return null;
      return this._encodeJSON(jsonQuery, 0, '');
  },



  /**
   * Converts VA Encoded String to VA Comparison JSON Model
   * @param {String} encodedString 
   * @param {Boolean} exposeSkeleton 
   * @returns Object
   */
  decodeVAURIQuery: function(encodedString, exposeSkeleton) {
      if (!encodedString) return [];
      var word = '',
          parentNodeId = null;
      this._createNode(null);
      encodedString += this.PARSER_CONSTANTS.signal.EOS;
      for (var i = 0; i < encodedString.length; i++) {
          var ch = encodedString[i];
          if (this.PARSER_CONSTANTS.symbols.indexOf(ch) != -1) {
              this._registerToNode(this._getCurrentNodeId(), word);
              switch (ch) { // special operations for other markers
                  case this.PARSER_CONSTANTS.signal.STEP_INTO:
                      parentNodeId = this._getCurrentNodeId();
                      this._createNode(this._getCurrentNodeId());
                      this._registerToNode(parentNodeId, this.PARSER_CONSTANTS.has_children);
                      break;

                  case this.PARSER_CONSTANTS.signal.STEP_OUT:
                      this.nodeStack.pop();
                      break;

                  case this.PARSER_CONSTANTS.signal.SIBLING_SEPERATOR:
                      parentNodeId = this._getCurrentNodeId(true);
                      this.nodeStack.pop();
                      this._createNode(parentNodeId);
                      break;
                  default:
                      null;
              }
              word = '';
          } else
              word += ch;
      }
      var api = {
          'va_comparison_model': this.convertToVAComparisonModel()
      };
      if (exposeSkeleton) api['skeleton'] = this.skeletonObject;
      return api;
  },
  type: 'VAComparisonModelEncodedQueryUtils'
};

Sys ID

d9b9436fc30034104b8e88c7c840dd39

Offical Documentation

Official Docs: