<commit_before>define([
  'angular',
  'camunda-bpm-sdk'
], function(
  angular,
  camSDK
) {
  'use strict';
  return [
    '$scope',
    '$rootScope',
    '$timeout',
    'search',
    '$location',
  function(
    $scope,
    $rootScope,
    $timeout,
    search,
    $location
  ) {
    function parseSearch(search, query) {
      var searchRegEx = /^\s*(.*?)\s*(!=|<=|>=|like|[=<>])\s*(.*?)\s*$/;
      var match = searchRegEx.exec(query);
      if(match && match.length === 4) {
        search.name = match[1];
        search.operator = match[2];
        search.value = match[3];
        search.operators = getOperators(getType(parseValue(search.value)));
        return true;
      }
      return false;
    }
    function createSearchObj(type) {
      return {
        type : type,
        operator: "=",
        operators: getOperators()
      };
    }
    function getOperators(varType) {
      switch(varType) {
        case 'date':    return ["BEFORE", "AFTER"];
        case 'boolean':
        case 'object':  return ["=", "!="];
        case 'number':  return ["=", "!=", "<", ">", "<=", ">="];
        default:        return ["=", "!=", "<", ">", "<=", ">=", "like"];
      }
    }

    var dateRegex = /(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(?:.(\d\d\d)| )?$/;
    function getType(value) {
      if(value && typeof value === "string" && value.match(dateRegex)) {
        return "date";
      }
      return typeof value;
    }

    $scope.types = ["Process Variable", "Task Variable", "Case Variable"];
    $scope.dropdownOpen = false;

    $scope.deleteSearch = function(idx) {
      $scope.searches.splice(idx,1);
      updateQuery();
    };

    $scope.createSearch = function(type){
      var search = createSearchObj(type);
      if(!parseSearch(search, $scope.inputQuery)) {
        search.value = $scope.inputQuery;
        search.operators = getOperators(getType(parseValue(search.value)));
      }
      $scope.searches.push(search);
      updateQuery();

      // need to use timeout, because jQuery initiates an apply cycle
      // while the current apply cycle is still in progress
      $timeout(function(){angular.element('.search-container > input').blur();});
      $scope.dropdownOpen = false;
      $scope.inputQuery = "";
    };

    function getDefaultOperator(valueType) {
      switch(valueType) {
        case 'date': return "AFTER";
        default:     return "=";
      }
    }

    $scope.changeSearch = function(idx, field, value) {
      var search = $scope.searches[idx];
      search[field] = value;
      var valueType = getType(parseValue(search.value));
      search.operators = getOperators(valueType);
      if(search.operators.indexOf(search.operator) === -1) {
        // if the current value type does not allow the selected operator,
        // fall back to default operator
        search.operator = getDefaultOperator(valueType);
      }
      updateQuery();
    };

    $scope.selectType = function(type) {
      $scope.createSearch(type);
    };

    $scope.onFocus = function(){
      $scope.dropdownOpen = true;
    };

    $scope.onBlur = function(){
      $scope.dropdownOpen = false;
    };

    $scope.dropdownHandler = function(evt) {
      evt.preventDefault();
    };

    function isValid(search) {
      return $scope.types.indexOf(search.type) !== -1 &&
         search.operators.indexOf(search.operator) !== -1 &&
         search.name &&
         search.value;
    }
    function parseValue(value) {
      if(!isNaN(value)) {
        // value must be transformed to number
        return +value;
      }
      if(value === "true") {
        return true;
      }
      if(value === "false") {
        return false;
      }
      if(value === "NULL") {
        return null;
      }
      return value;
    }

    var tasklistData = $scope.tasklistData.newChild($scope);
    function updateQuery() {
      var outArray = [];
      angular.forEach($scope.searches, function(search) {
        if(isValid(search)) {
          outArray.push({
            name: search.name,
            operator: search.operator,
            value: parseValue(search.value),
            type: search.type
          });
        }
      });
      search.updateSilently({
        query: JSON.stringify(outArray)
      });

      tasklistData.changed('taskListQuery');
    }

     function getPropertyFromLocation(property) {
       var search = $location.search() || {};
       return search[property] || null;
     }
     tasklistData.observe('taskListQuery', function(taskListQuery) {
       angular.forEach($scope.searches, function(search, i) {
         if(isValid(search)) {
            $scope.searches.splice(i, 1);
         }
       });
       var searches = JSON.parse(getPropertyFromLocation("query"));
       if(searches) {
         for(var i=0; i < searches.length; i++) {
           var search = searches[i];
           search.operators = getOperators(getType(search.value));
           search.value = search.value.toString();
           search.type = search.type;
           $scope.searches.push(search);
         }
       }
     });
  }];
});
<commit_msg>fix(search): prevent exponential search query growth<commit_after>define([
  'angular',
  'camunda-bpm-sdk'
], function(
  angular,
  camSDK
) {
  'use strict';
  return [
    '$scope',
    '$rootScope',
    '$timeout',
    'search',
    '$location',
  function(
    $scope,
    $rootScope,
    $timeout,
    search,
    $location
  ) {
    function parseSearch(search, query) {
      var searchRegEx = /^\s*(.*?)\s*(!=|<=|>=|like|[=<>])\s*(.*?)\s*$/;
      var match = searchRegEx.exec(query);
      if(match && match.length === 4) {
        search.name = match[1];
        search.operator = match[2];
        search.value = match[3];
        search.operators = getOperators(getType(parseValue(search.value)));
        return true;
      }
      return false;
    }
    function createSearchObj(type) {
      return {
        type : type,
        operator: "=",
        operators: getOperators()
      };
    }
    function getOperators(varType) {
      switch(varType) {
        case 'date':    return ["BEFORE", "AFTER"];
        case 'boolean':
        case 'object':  return ["=", "!="];
        case 'number':  return ["=", "!=", "<", ">", "<=", ">="];
        default:        return ["=", "!=", "<", ">", "<=", ">=", "like"];
      }
    }

    var dateRegex = /(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(?:.(\d\d\d)| )?$/;
    function getType(value) {
      if(value && typeof value === "string" && value.match(dateRegex)) {
        return "date";
      }
      return typeof value;
    }

    $scope.types = ["Process Variable", "Task Variable", "Case Variable"];
    $scope.dropdownOpen = false;

    $scope.deleteSearch = function(idx) {
      $scope.searches.splice(idx,1);
      updateQuery();
    };

    $scope.createSearch = function(type){
      var search = createSearchObj(type);
      if(!parseSearch(search, $scope.inputQuery)) {
        search.value = $scope.inputQuery;
        search.operators = getOperators(getType(parseValue(search.value)));
      }
      $scope.searches.push(search);
      updateQuery();

      // need to use timeout, because jQuery initiates an apply cycle
      // while the current apply cycle is still in progress
      $timeout(function(){angular.element('.search-container > input').blur();});
      $scope.dropdownOpen = false;
      $scope.inputQuery = "";
    };

    function getDefaultOperator(valueType) {
      switch(valueType) {
        case 'date': return "AFTER";
        default:     return "=";
      }
    }

    $scope.changeSearch = function(idx, field, value) {
      var search = $scope.searches[idx];
      search[field] = value;
      var valueType = getType(parseValue(search.value));
      search.operators = getOperators(valueType);
      if(search.operators.indexOf(search.operator) === -1) {
        // if the current value type does not allow the selected operator,
        // fall back to default operator
        search.operator = getDefaultOperator(valueType);
      }
      updateQuery();
    };

    $scope.selectType = function(type) {
      $scope.createSearch(type);
    };

    $scope.onFocus = function(){
      $scope.dropdownOpen = true;
    };

    $scope.onBlur = function(){
      $scope.dropdownOpen = false;
    };

    $scope.dropdownHandler = function(evt) {
      evt.preventDefault();
    };

    function isValid(search) {
      return $scope.types.indexOf(search.type) !== -1 &&
         search.operators.indexOf(search.operator) !== -1 &&
         search.name &&
         search.value;
    }
    function parseValue(value) {
      if(!isNaN(value)) {
        // value must be transformed to number
        return +value;
      }
      if(value === "true") {
        return true;
      }
      if(value === "false") {
        return false;
      }
      if(value === "NULL") {
        return null;
      }
      return value;
    }

    var tasklistData = $scope.tasklistData.newChild($scope);
    function updateQuery() {
      var outArray = [];
      angular.forEach($scope.searches, function(search) {
        if(isValid(search)) {
          outArray.unshift({
            name: search.name,
            operator: search.operator,
            value: parseValue(search.value),
            type: search.type
          });
        }
      });
      search.updateSilently({
        query: JSON.stringify(outArray)
      });

      tasklistData.changed('taskListQuery');
    }

     function getPropertyFromLocation(property) {
       var search = $location.search() || {};
       return search[property] || null;
     }
     tasklistData.observe('taskListQuery', function(taskListQuery) {
       var search, i;
       for(i = 0; i < $scope.searches.length; i++) {
         search = $scope.searches[i];
         if(isValid(search)) {
            $scope.searches.splice(i, 1);
            i--;
         }
       }

       var searches = JSON.parse(getPropertyFromLocation("query"));
       if(searches) {
         for(i=0; i < searches.length; i++) {
           search = searches[i];
           search.operators = getOperators(getType(search.value));
           search.value = search.value.toString();
           search.type = search.type;
           $scope.searches.unshift(search);
         }
       }
     });
  }];
});

{'fraction_non_alphanumeric': 0.1622222222222222, 'fraction_numerical': 0.002028985507246377, 'mean_word_length': 2.1722341403616303, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3107513', 'n_tokens_mistral': 3173, 'n_tokens_neox': 3135, 'n_words': 583}