static getSessions (req, res) {
    let map = {};
    let graph = {};

    let options = {};
    if (req.query.cancelId) {
      options.cancelId = `${req.user.userId}::${req.query.cancelId}`;
    }
    options = ViewerUtils.addCluster(req.query.cluster, options);
    options.arkime_unflatten = parseInt(req.query.flatten) !== 1;

    const response = {
      data: [],
      map: {},
      graph: {},
      recordsTotal: 0,
      recordsFiltered: 0
    };

    SessionAPIs.buildSessionQuery(req, (bsqErr, query, indices) => {
      if (bsqErr) {
        response.error = bsqErr.toString();
        return res.send(response);
      }

      let addMissing = false;
      if (req.query.fields) {
        query._source = false;
        query.fields = ViewerUtils.queryValueToArray(req.query.fields);
        ['node', 'source.ip', 'source.port', 'destination.ip', 'destination.port'].forEach((item) => {
          if (query.fields.indexOf(item) === -1) {
            query.fields.push(item);
          }
        });
      } else {
        addMissing = true;
        query._source = false;
        query.fields = [
          'ipProtocol', 'rootId', 'totDataBytes', 'client.bytes',
          'server.bytes', 'firstPacket', 'lastPacket', 'source.ip', 'source.port',
          'destination.ip', 'destination.port', 'network.packets', 'source.packets', 'destination.packets',
          'network.bytes', 'source.bytes', 'destination.bytes', 'node', 'http.uri', 'source.geo.country_iso_code',
          'destination.geo.country_iso_code', 'email.subject', 'email.src', 'email.dst', 'email.filename',
          'dns.host', 'cert', 'irc.channel', 'http.xffGEO'
        ];
      }

      if (query.aggregations && query.aggregations.dbHisto) {
        graph.interval = query.aggregations.dbHisto.histogram.interval;
      }

      if (Config.debug) {
        console.log(`/api/sessions ${indices} query`, JSON.stringify(query, null, 1));
      }

      let hideTags = req.user.settings.hideTags?.split(',').map(tag => tag.trim()).filter(tag => tag.length > 0);
      if (hideTags?.length === 0) {
        hideTags = undefined;
      }

      Promise.all([
        Db.searchSessions(indices, query, options),
        Db.numberOfDocuments(Db.getSessionIndices(), options.cluster ? { cluster: options.cluster } : {})
      ]).then(([sessions, total]) => {
        if (Config.debug) {
          console.log('/api/sessions result', util.inspect(sessions, false, 50));
        }

        if (sessions.error) { throw sessions.err; }

        map = ViewerUtils.mapMerge(sessions.aggregations);
        graph = ViewerUtils.graphMerge(req, query, sessions.aggregations);

        const results = { total: sessions.hits.total, results: [] };
        async.eachSeries(sessions.hits.hits, (hit, hitCb) => {
          const fields = hit.fields;
          if (fields === undefined) {
            return hitCb(null);
          }

          if (hideTags && fields.tags) {
            // remove all entries from hideTags
            fields.tags = fields.tags.filter((tag) => !hideTags.includes(tag));
          }

          fields.id = Db.session2Sid(hit);

          if (addMissing) {
            if (options.arkime_unflatten) {
              [['source', 'packets'], ['destination', 'packets'], ['source', 'bytes'], ['destination', 'bytes'], ['client', 'bytes'], ['server', 'bytes']].forEach((item) => {
                if (fields[item[0]] === undefined) {
                  fields[item[0]] = {};
                }
                if (fields[item[0]][item[1]] === undefined) {
                  fields[item[0]][item[1]] = -1;
                }
              });
            } else {
              ['source.packets', 'destination.packets', 'source.bytes', 'destination.bytes', 'client.bytes', 'server.bytes'].forEach((item) => {
                if (fields[item] === undefined) {
                  fields[item] = -1;
                }
              });
            }
            results.results.push(fields);
            return hitCb();
          } else {
            ViewerUtils.fixFields(fields, () => {
              results.results.push(fields);
              return hitCb();
            });
          }
        }, () => {
          try {
            response.map = map;
            response.graph = graph;
            response.data = (results ? results.results : []);
            response.recordsTotal = total.count;
            response.recordsFiltered = (results ? results.total : 0);
            res.logCounts(response.data.length, response.recordsFiltered, response.recordsTotal);
            return res.send(response);
          } catch (e) {
            console.trace(`ERROR - ${req.method} /api/sessions`, ArkimeUtil.sanitizeStr(e.stack));
            response.error = e.toString();
            return res.send(response);
          }
        });
      }).catch((err) => {
        console.log(`ERROR - ${req.method} /api/sessions`, util.inspect(err, false, 50));
        response.error = err.toString();
        return res.send(response);
      });
    });
  }
POST/GET - /api/sessions OR /sessions.json

Return all the JSON formatted session data based on the query parameters.

@name /sessions
@param {SessionsQuery} See_List - This API supports a common set of parameters documented in the SessionsQuery section
@returns {object} map - The data to populate the sessions map
@returns {object} graph - The data to populate the sessions timeline graph
@returns {array} data - The list of sessions with the requested fields
@returns {number} recordsTotal - The total number of sessions Arkime knows about
@returns {number} recordsFiltered - The number of sessions matching query
@example
  Returns all the sessions with the source IP of 1.2.3.4
  curl -v 'http://localhost:8005/api/sessions?date=-1&expression=ip.src%3D%3D1.2.3.4'