docs: private static void createFacetBase(ComponentFacet cf, int level,
        MtasDataCollector<?, ?> dataCollector,
        Map<Integer, Integer> positionsData,
        Map<MtasSpanQuery, Map<Integer, Integer>> spansNumberData,
        Map<String, SortedMap<String, int[]>> facetData, Integer[] docSet)
        throws IOException {
      for (MtasFunctionParserFunction function : cf.baseFunctionParserFunctions[level]) {
        if (function.needArgumentsNumber() > cf.spanQueries.length) {
          throw new IOException("function " + function + " expects (at least) "
              + function.needArgumentsNumber() + " queries");
        }
      }
      Map<String, int[]> list = facetData.get(cf.baseFields[level]);
      if (dataCollector != null) {
        MtasDataCollector<?, ?> subDataCollector = null;
        dataCollector.initNewList(1);
        if (cf.baseFunctionList[level] != null) {
          SubComponentFunction[] tmpList;
          if (!cf.baseFunctionList[level].containsKey(dataCollector)) {
            tmpList = new SubComponentFunction[cf.baseFunctionParserFunctions[level].length];
            cf.baseFunctionList[level].put(dataCollector, tmpList);
            for (int i = 0; i < cf.baseFunctionParserFunctions[level].length; i++) {
              try {
                tmpList[i] = new SubComponentFunction(
                    DataCollector.COLLECTOR_TYPE_LIST,
                    cf.baseFunctionKeys[level][i], cf.baseFunctionTypes[level][i],
                    cf.baseFunctionParserFunctions[level][i], null, null, 0,
                    Integer.MAX_VALUE, null, null);
  
              } catch (ParseException e) {
                throw new IOException(e.getMessage());
              }
            }
          } else {
            tmpList = cf.baseFunctionList[level].get(dataCollector);
          }
          for (SubComponentFunction function : tmpList) {
            function.dataCollector.initNewList(1);
          }
        }
        // check type
        if (dataCollector.getCollectorType()
            .equals(DataCollector.COLLECTOR_TYPE_LIST)) {
          dataCollector.setWithTotal();
          // only if documents and facets
          if (docSet.length > 0 && list.size() > 0) {
            HashMap<String, Integer[]> docLists = new HashMap<>();
            HashMap<String, String> groupedKeys = new HashMap<>();
            boolean documentsInFacets = false;
            // compute intersections
            for (Entry<String, int[]> entry : list.entrySet()) {
              // fill grouped keys
              if (!groupedKeys.containsKey(entry.getKey())) {
                groupedKeys.put(entry.getKey(), groupedKeyName(entry.getKey(),
                    cf.baseRangeSizes[level], cf.baseRangeBases[level]));
              }
              // intersect docSet with docList
              Integer[] docList = intersectedDocList(entry.getValue(), docSet);
              if (docList != null && docList.length > 0) {
                documentsInFacets = true;
              }
              // update docLists
              if (docLists.containsKey(groupedKeys.get(entry.getKey()))) {
                docLists.put(groupedKeys.get(entry.getKey()), mergeDocLists(
                    docLists.get(groupedKeys.get(entry.getKey())), docList));
              } else {
                docLists.put(groupedKeys.get(entry.getKey()), docList);
              }
            }
            // compute stats for each key
            if (documentsInFacets) {
              Map<Integer, long[]> args = computeArguments(spansNumberData,
                  cf.spanQueries, docSet);
              if (cf.baseDataTypes[level].equals(CodecUtil.DATA_TYPE_LONG)) {
                // check functions
                boolean applySumRule = false;
                if (cf.baseStatsTypes[level].equals(CodecUtil.STATS_BASIC)
                    && cf.baseParsers[level].sumRule()
                    && (cf.baseMinimumLongs[level] == null)
                    && (cf.baseMaximumLongs[level] == null)) {
                  applySumRule = true;
                  if (cf.baseFunctionList[level].get(dataCollector) != null) {
                    for (SubComponentFunction function : cf.baseFunctionList[level]
                        .get(dataCollector)) {
                      if (!function.statsType.equals(CodecUtil.STATS_BASIC)
                          || !function.parserFunction.sumRule()
                          || function.parserFunction.needPositions()) {
                        applySumRule = false;
                        break;
                      }
                    }
                  }
                }
                if (applySumRule) {
                  for (String key : new LinkedHashSet<String>(
                      groupedKeys.values())) {
                    if (docLists.get(key).length > 0) {
                      // initialise
                      Integer[] subDocSet = docLists.get(key);
                      int length = cf.baseParsers[level].needArgumentsNumber();
                      long[] valueSum = new long[length];
                      long valuePositions = 0;
                      // collect
                      if (subDocSet.length > 0) {
                        long[] tmpArgs;
                        for (int docId : subDocSet) {
                          tmpArgs = args.get(docId);
                          if (positionsData != null
                              && positionsData.containsKey(docId)
                              && positionsData.get(docId) != null) {
                            valuePositions += positionsData.get(docId)
                                .longValue();
                          }
                          if (tmpArgs != null) {
                            for (int i = 0; i < length; i++) {
                              valueSum[i] += tmpArgs[i];
                            }
                          }
                        }
                        long value;
                        try {
                          value = cf.baseParsers[level].getValueLong(valueSum,
                              valuePositions);
                          subDataCollector = dataCollector.add(key, value,
                              subDocSet.length);
                        } catch (IOException e) {
                          log.debug(e);
                          dataCollector.error(key, e.getMessage());
                          subDataCollector = null;
                        }
                        if (cf.baseFunctionList[level] != null
                            && cf.baseFunctionList[level]
                                .containsKey(dataCollector)) {
                          SubComponentFunction[] functionList = cf.baseFunctionList[level]
                              .get(dataCollector);
                          for (SubComponentFunction function : functionList) {
                            if (function.dataType
                                .equals(CodecUtil.DATA_TYPE_LONG)) {
                              try {
                                long valueLong = function.parserFunction
                                    .getValueLong(valueSum, valuePositions);
                                function.dataCollector.add(key, valueLong,
                                    subDocSet.length);
                              } catch (IOException e) {
                                log.debug(e);
                                function.dataCollector.error(key, e.getMessage());
                              }
                            } else if (function.dataType
                                .equals(CodecUtil.DATA_TYPE_DOUBLE)) {
                              try {
                                double valueDouble = function.parserFunction
                                    .getValueDouble(valueSum, valuePositions);
                                function.dataCollector.add(key, valueDouble,
                                    subDocSet.length);
                              } catch (IOException e) {
                                log.debug(e);
                                function.dataCollector.error(key, e.getMessage());
                              }
                            }
                          }
                        }
                        if (subDataCollector != null) {
                          createFacetBase(cf, (level + 1), subDataCollector,
                              positionsData, spansNumberData, facetData,
                              subDocSet);
                        }
                      }
                    }
                  }
                } else {
                  for (String key : new LinkedHashSet<String>(
                      groupedKeys.values())) {
                    if (docLists.get(key).length > 0) {
                      // initialise
                      Integer[] subDocSet = docLists.get(key);
                      // collect
                      if (subDocSet.length > 0 && cf.baseDataTypes[level]
                          .equals(CodecUtil.DATA_TYPE_LONG)) {
                        // check for functions
                        long[][] functionValuesLong = null;
                        double[][] functionValuesDouble = null;
                        int[] functionNumber = null;
                        SubComponentFunction[] functionList = null;
                        if (cf.baseFunctionList[level] != null
                            && cf.baseFunctionList[level]
                                .containsKey(dataCollector)) {
                          functionList = cf.baseFunctionList[level]
                              .get(dataCollector);
                          functionValuesLong = new long[functionList.length][];
                          functionValuesDouble = new double[functionList.length][];
                          functionNumber = new int[functionList.length];
                          for (int i = 0; i < functionList.length; i++) {
                            functionValuesLong[i] = new long[subDocSet.length];
                            functionValuesDouble[i] = new double[subDocSet.length];
                          }
                        }
                        // check main
                        int number = 0;
                        Integer[] restrictedSubDocSet = new Integer[subDocSet.length];
                        long[] values = new long[subDocSet.length];
                        for (int docId : subDocSet) {
                          long[] tmpArgs = args.get(docId);
                          int tmpPositions = (positionsData == null) ? 0
                              : (positionsData.get(docId) == null ? 0
                                  : positionsData.get(docId));
                          long value = cf.baseParsers[level].getValueLong(tmpArgs,
                              tmpPositions);
                          if ((cf.baseMinimumLongs[level] == null
                              || value >= cf.baseMinimumLongs[level])
                              && (cf.baseMaximumLongs[level] == null
                                  || value <= cf.baseMaximumLongs[level])) {
                            values[number] = value;
                            restrictedSubDocSet[number] = docId;
                            number++;
                            if (functionList != null) {
                              for (int i = 0; i < functionList.length; i++) {
                                SubComponentFunction function = functionList[i];
                                if (function.dataType
                                    .equals(CodecUtil.DATA_TYPE_LONG)) {
                                  try {
                                    functionValuesLong[i][functionNumber[i]] = function.parserFunction
                                        .getValueLong(tmpArgs, tmpPositions);
                                    functionNumber[i]++;
                                  } catch (IOException e) {
                                    log.debug(e);
                                    function.dataCollector.error(key,
                                        e.getMessage());
                                  }
                                } else if (function.dataType
                                    .equals(CodecUtil.DATA_TYPE_DOUBLE)) {
                                  try {
                                    functionValuesDouble[i][functionNumber[i]] = function.parserFunction
                                        .getValueDouble(tmpArgs, tmpPositions);
                                    functionNumber[i]++;
                                  } catch (IOException e) {
                                    log.debug(e);
                                    function.dataCollector.error(key,
                                        e.getMessage());
                                  }
                                }
                              }
                            }
                          }
                        }
                        if (number > 0) {
                          subDataCollector = dataCollector.add(key, values,
                              number);
                          if (cf.baseFunctionList[level] != null
                              && cf.baseFunctionList[level]
                                  .containsKey(dataCollector)) {
                            for (int i = 0; i < functionList.length; i++) {
                              SubComponentFunction function = functionList[i];
                              if (function.dataType
                                  .equals(CodecUtil.DATA_TYPE_LONG)) {
                                function.dataCollector.add(key,
                                    functionValuesLong[i], functionNumber[i]);
                              } else if (function.dataType
                                  .equals(CodecUtil.DATA_TYPE_DOUBLE)) {
                                function.dataCollector.add(key,
                                    functionValuesDouble[i], functionNumber[i]);
                              }
                            }
                          }
                          if (subDataCollector != null) {
                            createFacetBase(cf, (level + 1), subDataCollector,
                                positionsData, spansNumberData, facetData, Arrays
                                    .copyOfRange(restrictedSubDocSet, 0, number));
                          }
                        }
                      }
                    }
                  }
                }
              } else {
                throw new IOException(
                    "unexpected dataType " + cf.baseDataTypes[level]);
              }
            }
          }
        } else {
          throw new IOException(
              "unexpected type " + dataCollector.getCollectorType());
        }
        dataCollector.closeNewList();
        if (cf.baseFunctionList[level] != null
            && cf.baseFunctionList[level].containsKey(dataCollector)) {
          SubComponentFunction[] tmpList = cf.baseFunctionList[level]
              .get(dataCollector);
          for (SubComponentFunction function : tmpList) {
            function.dataCollector.closeNewList();
          }
        }
      }
  
    }