docs: private void prepareSpans(ResponseBuilder rb, ComponentFields mtasFields)
        throws IOException {
      SortedSet<String> ids = MtasSolrResultUtil
          .getIdsFromParameters(rb.req.getParams(), PARAM_MTAS_STATS_SPANS);
      if (!ids.isEmpty()) {
        int tmpCounter = 0;
        String[] fields = new String[ids.size()];
        String[] keys = new String[ids.size()];
        String[] minima = new String[ids.size()];
        String[] maxima = new String[ids.size()];
        String[] types = new String[ids.size()];
        String[][] functionExpressions = new String[ids.size()][];
        String[][] functionKeys = new String[ids.size()][];
        String[][] functionTypes = new String[ids.size()][];
        String[][] queryTypes = new String[ids.size()][];
        String[][] queryValues = new String[ids.size()][];
        String[][] queryIgnores = new String[ids.size()][];
        String[][] queryMaximumIgnoreLengths = new String[ids.size()][];
        String[][] queryPrefixes = new String[ids.size()][];
        HashMap<String, String[]>[][] queryVariables = new HashMap[ids.size()][];
        Boolean[] expand = new Boolean[ids.size()];
        for (String id : ids) {
          fields[tmpCounter] = rb.req.getParams().get(PARAM_MTAS_STATS_SPANS + "."
              + id + "." + NAME_MTAS_STATS_SPANS_FIELD, null);
          keys[tmpCounter] = rb.req.getParams().get(
              PARAM_MTAS_STATS_SPANS + "." + id + "." + NAME_MTAS_STATS_SPANS_KEY,
              String.valueOf(tmpCounter)).trim();
          minima[tmpCounter] = rb.req.getParams().get(PARAM_MTAS_STATS_SPANS + "."
              + id + "." + NAME_MTAS_STATS_SPANS_MINIMUM, null);
          maxima[tmpCounter] = rb.req.getParams().get(PARAM_MTAS_STATS_SPANS + "."
              + id + "." + NAME_MTAS_STATS_SPANS_MAXIMUM, null);
          types[tmpCounter] = rb.req.getParams().get(PARAM_MTAS_STATS_SPANS + "."
              + id + "." + NAME_MTAS_STATS_SPANS_TYPE, null);
          Set<String> functionIds = MtasSolrResultUtil
              .getIdsFromParameters(rb.req.getParams(), PARAM_MTAS_STATS_SPANS
                  + "." + id + "." + NAME_MTAS_STATS_SPANS_FUNCTION);
          functionExpressions[tmpCounter] = new String[functionIds.size()];
          functionKeys[tmpCounter] = new String[functionIds.size()];
          functionTypes[tmpCounter] = new String[functionIds.size()];
          int tmpSubCounter = 0;
          for (String functionId : functionIds) {
            functionKeys[tmpCounter][tmpSubCounter] = rb.req.getParams()
                .get(
                    PARAM_MTAS_STATS_SPANS + "." + id + "."
                        + NAME_MTAS_STATS_SPANS_FUNCTION + "." + functionId + "."
                        + SUBNAME_MTAS_STATS_SPANS_FUNCTION_KEY,
                    String.valueOf(tmpSubCounter))
                .trim();
            functionExpressions[tmpCounter][tmpSubCounter] = rb.req.getParams()
                .get(PARAM_MTAS_STATS_SPANS + "." + id + "."
                    + NAME_MTAS_STATS_SPANS_FUNCTION + "." + functionId + "."
                    + SUBNAME_MTAS_STATS_SPANS_FUNCTION_EXPRESSION, null);
            functionTypes[tmpCounter][tmpSubCounter] = rb.req.getParams()
                .get(PARAM_MTAS_STATS_SPANS + "." + id + "."
                    + NAME_MTAS_STATS_SPANS_FUNCTION + "." + functionId + "."
                    + SUBNAME_MTAS_STATS_SPANS_FUNCTION_TYPE, null);
            tmpSubCounter++;
          }
  
          Set<String> qIds = MtasSolrResultUtil
              .getIdsFromParameters(rb.req.getParams(), PARAM_MTAS_STATS_SPANS
                  + "." + id + "." + NAME_MTAS_STATS_SPANS_QUERY);
          if (!qIds.isEmpty()) {
            int tmpQCounter = 0;
            queryTypes[tmpCounter] = new String[qIds.size()];
            queryValues[tmpCounter] = new String[qIds.size()];
            queryIgnores[tmpCounter] = new String[qIds.size()];
            queryMaximumIgnoreLengths[tmpCounter] = new String[qIds.size()];
            queryPrefixes[tmpCounter] = new String[qIds.size()];
            queryVariables[tmpCounter] = new HashMap[qIds.size()];
            for (String qId : qIds) {
              queryTypes[tmpCounter][tmpQCounter] = rb.req.getParams()
                  .get(PARAM_MTAS_STATS_SPANS + "." + id + "."
                      + NAME_MTAS_STATS_SPANS_QUERY + "." + qId + "."
                      + SUBNAME_MTAS_STATS_SPANS_QUERY_TYPE, null);
              queryValues[tmpCounter][tmpQCounter] = rb.req.getParams()
                  .get(PARAM_MTAS_STATS_SPANS + "." + id + "."
                      + NAME_MTAS_STATS_SPANS_QUERY + "." + qId + "."
                      + SUBNAME_MTAS_STATS_SPANS_QUERY_VALUE, null);
              queryIgnores[tmpCounter][tmpQCounter] = rb.req.getParams()
                  .get(PARAM_MTAS_STATS_SPANS + "." + id + "."
                      + NAME_MTAS_STATS_SPANS_QUERY + "." + qId + "."
                      + SUBNAME_MTAS_STATS_SPANS_QUERY_IGNORE, null);
              queryMaximumIgnoreLengths[tmpCounter][tmpQCounter] = rb.req
                  .getParams().get(
                      PARAM_MTAS_STATS_SPANS + "." + id + "."
                          + NAME_MTAS_STATS_SPANS_QUERY + "." + qId + "."
                          + SUBNAME_MTAS_STATS_SPANS_QUERY_MAXIMUM_IGNORE_LENGTH,
                      null);
              queryPrefixes[tmpCounter][tmpQCounter] = rb.req.getParams()
                  .get(PARAM_MTAS_STATS_SPANS + "." + id + "."
                      + NAME_MTAS_STATS_SPANS_QUERY + "." + qId + "."
                      + SUBNAME_MTAS_STATS_SPANS_QUERY_PREFIX, null);
              Set<String> vIds = MtasSolrResultUtil.getIdsFromParameters(
                  rb.req.getParams(),
                  PARAM_MTAS_STATS_SPANS + "." + id + "."
                      + NAME_MTAS_STATS_SPANS_QUERY + "." + qId + "."
                      + SUBNAME_MTAS_STATS_SPANS_QUERY_VARIABLE);
              queryVariables[tmpCounter][tmpQCounter] = new HashMap<>();
              if (!vIds.isEmpty()) {
                HashMap<String, ArrayList<String>> tmpVariables = new HashMap<>();
                for (String vId : vIds) {
                  String name = rb.req.getParams()
                      .get(PARAM_MTAS_STATS_SPANS + "." + id + "."
                          + NAME_MTAS_STATS_SPANS_QUERY + "." + qId + "."
                          + SUBNAME_MTAS_STATS_SPANS_QUERY_VARIABLE + "." + vId
                          + "." + SUBNAME_MTAS_STATS_SPANS_QUERY_VARIABLE_NAME,
                          null);
                  if (name != null) {
                    if (!tmpVariables.containsKey(name)) {
                      tmpVariables.put(name, new ArrayList<String>());
                    }
                    String value = rb.req.getParams()
                        .get(PARAM_MTAS_STATS_SPANS + "." + id + "."
                            + NAME_MTAS_STATS_SPANS_QUERY + "." + qId + "."
                            + SUBNAME_MTAS_STATS_SPANS_QUERY_VARIABLE + "." + vId
                            + "." + SUBNAME_MTAS_STATS_SPANS_QUERY_VARIABLE_VALUE,
                            null);
                    if (value != null) {
                      ArrayList<String> list = new ArrayList<>();
                      String[] subList = value.split("(?<!\\\\),");
                      for (int i = 0; i < subList.length; i++) {
                        list.add(
                            subList[i].replace("\\,", ",").replace("\\\\", "\\"));
                      }
                      tmpVariables.get(name).addAll(list);
                    }
                  }
                }
                for (Entry<String, ArrayList<String>> entry : tmpVariables
                    .entrySet()) {
                  queryVariables[tmpCounter][tmpQCounter].put(entry.getKey(),
                      entry.getValue()
                          .toArray(new String[entry.getValue().size()]));
                }
              }
              tmpQCounter++;
            }
          } else {
            throw new IOException("no " + NAME_MTAS_STATS_SPANS_QUERY
                + " for mtas stats span " + id);
          }
          if (rb.req.getParams().getBool(PARAM_MTAS_STATS_SPANS + "." + id + "."
              + NAME_MTAS_STATS_SPANS_EXPAND, false)) {
            expand[tmpCounter] = true;
          } else {
            expand[tmpCounter] = false;
          }
          tmpCounter++;
        }
        String uniqueKeyField = rb.req.getSchema().getUniqueKeyField().getName();
        mtasFields.doStats = true;
        mtasFields.doStatsSpans = true;
        rb.setNeedDocSet(true);
        for (String field : fields) {
          if (field == null || field.isEmpty()) {
            throw new IOException("no (valid) field in mtas stats spans");
          } else if (!mtasFields.list.containsKey(field)) {
            mtasFields.list.put(field, new ComponentField(uniqueKeyField));
          }
        }
        MtasSolrResultUtil.compareAndCheck(keys, fields,
            NAME_MTAS_STATS_SPANS_KEY, NAME_MTAS_STATS_SPANS_FIELD, true);
        MtasSolrResultUtil.compareAndCheck(minima, fields,
            NAME_MTAS_STATS_SPANS_MINIMUM, NAME_MTAS_STATS_SPANS_FIELD, false);
        MtasSolrResultUtil.compareAndCheck(maxima, fields,
            NAME_MTAS_STATS_SPANS_MAXIMUM, NAME_MTAS_STATS_SPANS_FIELD, false);
        MtasSolrResultUtil.compareAndCheck(types, fields,
            NAME_MTAS_STATS_SPANS_TYPE, NAME_MTAS_STATS_SPANS_FIELD, false);
        MtasSolrResultUtil.compareAndCheck(types, fields,
            NAME_MTAS_STATS_SPANS_FUNCTION, NAME_MTAS_STATS_SPANS_FIELD, false);
  
        for (int i = 0; i < fields.length; i++) {
          ComponentField cf = mtasFields.list.get(fields[i]);
          int queryNumber = queryValues[i].length;
          MtasSpanQuery[] ql = new MtasSpanQuery[queryNumber];
          for (int j = 0; j < queryNumber; j++) {
            Integer maximumIgnoreLength = (queryMaximumIgnoreLengths[i][j] == null)
                ? null : Integer.parseInt(queryMaximumIgnoreLengths[i][j]);
            MtasSpanQuery q = MtasSolrResultUtil.constructQuery(queryValues[i][j],
                queryTypes[i][j], queryPrefixes[i][j], queryVariables[i][j],
                fields[i], queryIgnores[i][j], maximumIgnoreLength);
            // minimize number of queries
            if (cf.spanQueryList.contains(q)) {
              q = cf.spanQueryList.get(cf.spanQueryList.indexOf(q));
            } else {
              cf.spanQueryList.add(q);
            }
            ql[j] = q;
          }
          Double minimum = (minima[i] == null) || (minima[i].isEmpty()) ? null
              : Double.parseDouble(minima[i]);
          Double maximum = (maxima[i] == null) || (maxima[i].isEmpty()) ? null
              : Double.parseDouble(maxima[i]);
          String key = (keys[i] == null) || (keys[i].isEmpty())
              ? String.valueOf(i) + ":" + fields[i] + ":" + queryNumber
              : keys[i].trim();
          String type = (types[i] == null) || (types[i].isEmpty()) ? null
              : types[i].trim();
          String[] functionKey = functionKeys[i];
          String[] functionExpression = functionExpressions[i];
          String[] functionType = functionTypes[i];
          try {
            mtasFields.list.get(fields[i]).statsSpanList
                .add(new ComponentSpan(ql, key, minimum, maximum, type,
                    functionKey, functionExpression, functionType));
          } catch (ParseException e) {
            throw new IOException(e.getMessage());
          }
          if (expand[i]) {
            HashMap<String, String[]>[][] expandedQueryVariables = expandedQueryVariables(
                queryVariables[i]);
            for (int e = 0; e < expandedQueryVariables.length; e++) {
              MtasSpanQuery[] eql = new MtasSpanQuery[queryNumber];
              for (int j = 0; j < queryNumber; j++) {
                Integer maximumIgnoreLength = (queryMaximumIgnoreLengths[i][j] == null)
                    ? null : Integer.parseInt(queryMaximumIgnoreLengths[i][j]);
                MtasSpanQuery q = MtasSolrResultUtil.constructQuery(
                    queryValues[i][j], queryTypes[i][j], queryPrefixes[i][j],
                    expandedQueryVariables[e][j], fields[i], queryIgnores[i][j],
                    maximumIgnoreLength);
                // minimize number of queries
                if (cf.spanQueryList.contains(q)) {
                  q = cf.spanQueryList.get(cf.spanQueryList.indexOf(q));
                } else {
                  cf.spanQueryList.add(q);
                }
                eql[j] = q;
              }
              String newKey = generateKey(key + " (" + e + ")",
                  expandedQueryVariables[e]);
              try {
                mtasFields.list.get(fields[i]).statsSpanList
                    .add(new ComponentSpan(eql, newKey, minimum, maximum, type,
                        functionKey, functionExpression, functionType));
              } catch (ParseException ee) {
                throw new IOException(ee.getMessage());
              }
            }
          }
        }
      } else {
        throw new IOException("missing parameters stats spans");
      }
    }