@Override
  public void parse(XContentParser parser, SearchContext context) throws Exception {
    XContentParser.Token token;
    String currentFieldName = null;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        String fieldName = currentFieldName;
        ScriptParameterParser scriptParameterParser = new ScriptParameterParser();
        String script = null;
        ScriptService.ScriptType scriptType = null;
        Map<String, Object> params = null;
        boolean ignoreException = false;
        while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
          if (token == XContentParser.Token.FIELD_NAME) {
            currentFieldName = parser.currentName();
          } else if (token == XContentParser.Token.START_OBJECT) {
            params = parser.map();
          } else if (token.isValue()) {
            if ("ignore_failure".equals(currentFieldName)) {
              ignoreException = parser.booleanValue();
            } else {
              scriptParameterParser.token(currentFieldName, token, parser);
            }
          }
        }

        ScriptParameterValue scriptValue = scriptParameterParser.getDefaultScriptParameterValue();
        if (scriptValue != null) {
          script = scriptValue.script();
          scriptType = scriptValue.scriptType();
        }
        SearchScript searchScript =
            context
                .scriptService()
                .search(
                    context.lookup(),
                    new Script(scriptParameterParser.lang(), script, scriptType, params),
                    ScriptContext.Standard.SEARCH);
        context
            .scriptFields()
            .add(new ScriptFieldsContext.ScriptField(fieldName, searchScript, ignoreException));
      }
    }
  }
  @Override
  public AggregatorFactory parse(
      String aggregationName, XContentParser parser, SearchContext context) throws IOException {
    Script initScript = null;
    Script mapScript = null;
    Script combineScript = null;
    Script reduceScript = null;
    Map<String, Object> params = null;
    Map<String, Object> reduceParams = null;
    XContentParser.Token token;
    String currentFieldName = null;
    Set<String> scriptParameters = new HashSet<>();
    scriptParameters.add(INIT_SCRIPT);
    scriptParameters.add(MAP_SCRIPT);
    scriptParameters.add(COMBINE_SCRIPT);
    scriptParameters.add(REDUCE_SCRIPT);
    ScriptParameterParser scriptParameterParser = new ScriptParameterParser(scriptParameters);

    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if (INIT_SCRIPT_FIELD.match(currentFieldName)) {
          initScript = Script.parse(parser);
        } else if (MAP_SCRIPT_FIELD.match(currentFieldName)) {
          mapScript = Script.parse(parser);
        } else if (COMBINE_SCRIPT_FIELD.match(currentFieldName)) {
          combineScript = Script.parse(parser);
        } else if (REDUCE_SCRIPT_FIELD.match(currentFieldName)) {
          reduceScript = Script.parse(parser);
        } else if (PARAMS_FIELD.match(currentFieldName)) {
          params = parser.map();
        } else if (REDUCE_PARAMS_FIELD.match(currentFieldName)) {
          reduceParams = parser.map();
        } else {
          throw new SearchParseException(
              context,
              "Unknown key for a "
                  + token
                  + " in ["
                  + aggregationName
                  + "]: ["
                  + currentFieldName
                  + "].",
              parser.getTokenLocation());
        }
      } else if (token.isValue()) {
        if (!scriptParameterParser.token(currentFieldName, token, parser)) {
          throw new SearchParseException(
              context,
              "Unknown key for a "
                  + token
                  + " in ["
                  + aggregationName
                  + "]: ["
                  + currentFieldName
                  + "].",
              parser.getTokenLocation());
        }
      } else {
        throw new SearchParseException(
            context,
            "Unexpected token " + token + " in [" + aggregationName + "].",
            parser.getTokenLocation());
      }
    }

    if (initScript
        == null) { // Didn't find anything using the new API so try using the old one instead
      ScriptParameterValue scriptValue = scriptParameterParser.getScriptParameterValue(INIT_SCRIPT);
      if (scriptValue != null) {
        initScript =
            new Script(
                scriptValue.script(),
                scriptValue.scriptType(),
                scriptParameterParser.lang(),
                params);
      }
    } else if (initScript.getParams() != null) {
      throw new SearchParseException(
          context,
          "init_script params are not supported. Parameters for the init_script must be specified in the params field on the scripted_metric aggregator not inside the init_script object",
          parser.getTokenLocation());
    }

    if (mapScript
        == null) { // Didn't find anything using the new API so try using the old one instead
      ScriptParameterValue scriptValue = scriptParameterParser.getScriptParameterValue(MAP_SCRIPT);
      if (scriptValue != null) {
        mapScript =
            new Script(
                scriptValue.script(),
                scriptValue.scriptType(),
                scriptParameterParser.lang(),
                params);
      }
    } else if (mapScript.getParams() != null) {
      throw new SearchParseException(
          context,
          "map_script params are not supported. Parameters for the map_script must be specified in the params field on the scripted_metric aggregator not inside the map_script object",
          parser.getTokenLocation());
    }

    if (combineScript
        == null) { // Didn't find anything using the new API so try using the old one instead
      ScriptParameterValue scriptValue =
          scriptParameterParser.getScriptParameterValue(COMBINE_SCRIPT);
      if (scriptValue != null) {
        combineScript =
            new Script(
                scriptValue.script(),
                scriptValue.scriptType(),
                scriptParameterParser.lang(),
                params);
      }
    } else if (combineScript.getParams() != null) {
      throw new SearchParseException(
          context,
          "combine_script params are not supported. Parameters for the combine_script must be specified in the params field on the scripted_metric aggregator not inside the combine_script object",
          parser.getTokenLocation());
    }

    if (reduceScript
        == null) { // Didn't find anything using the new API so try using the old one instead
      ScriptParameterValue scriptValue =
          scriptParameterParser.getScriptParameterValue(REDUCE_SCRIPT);
      if (scriptValue != null) {
        reduceScript =
            new Script(
                scriptValue.script(),
                scriptValue.scriptType(),
                scriptParameterParser.lang(),
                reduceParams);
      }
    }

    if (mapScript == null) {
      throw new SearchParseException(
          context,
          "map_script field is required in [" + aggregationName + "].",
          parser.getTokenLocation());
    }
    return new ScriptedMetricAggregator.Factory(
        aggregationName, initScript, mapScript, combineScript, reduceScript, params);
  }