var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { SQL, Statement } from "../Utils/AnalyticsDatabase/Statement";
import { getQuerySettings } from "../Utils/AnalyticsDatabase/QuerySettingsHelper";
import LogDatabaseService from "./LogService";
import TableColumnType from "../../Types/AnalyticsDatabase/TableColumnType";
import BadDataException from "../../Types/Exception/BadDataException";
import Includes from "../../Types/BaseDatabase/Includes";
import AnalyticsTableName from "../../Types/AnalyticsDatabase/AnalyticsTableName";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
import ServiceType from "../../Types/Telemetry/ServiceType";
export class LogAggregationService {
    static async getHistogram(request) {
        const statement = LogAggregationService.buildHistogramStatement(request);
        const dbResult = await LogDatabaseService.executeQuery(statement);
        const response = await dbResult.json();
        const rows = response.data || [];
        return rows.map((row) => {
            return {
                time: String(row["bucket"] || ""),
                severity: String(row["severityText"] || "Unspecified"),
                count: Number(row["cnt"] || 0),
            };
        });
    }
    static async getFacetValues(request) {
        const statement = LogAggregationService.buildFacetStatement(request);
        const dbResult = await LogDatabaseService.executeQuery(statement);
        const response = await dbResult.json();
        const rows = response.data || [];
        return rows
            .map((row) => {
            return {
                value: String(row["val"] || ""),
                count: Number(row["cnt"] || 0),
            };
        })
            .filter((facet) => {
            return facet.value.length > 0;
        });
    }
    static buildHistogramStatement(request) {
        const intervalSeconds = request.bucketSizeInMinutes * 60;
        /*
         * Two-stage aggregation mirroring TraceAggregationService.getHistogram.
         * The inner query groups by toStartOfInterval(time, INTERVAL 1 MINUTE) —
         * the exact key expression of the proj_severity_histogram projection
         * (projectId, severityText, minute) — and filters the window on that same
         * expression rather than raw `time`. A raw `time` predicate references a
         * column the aggregate projection does not store, so ClickHouse rejects
         * the projection and full-scans (verified: 2.1M rows / ~46ms vs 978 rows /
         * ~7ms with this form). The outer query re-buckets the tiny minute-level
         * result to the requested size. Window edges round to the minute, which is
         * consistent with the minute-bucketed output and only shifts the first/last
         * bucket by the partial boundary minute when the range is not minute-aligned.
         *
         * A non-projection filter (primaryEntityId, entityKeys, traceId, spanId,
         * attributes) makes the inner query transparently fall back to a
         * base-table scan — same cost as before, still correct.
         */
        const statement = SQL `
      SELECT
        toStartOfInterval(minute, INTERVAL ${{
            type: TableColumnType.Number,
            value: intervalSeconds,
        }} SECOND) AS bucket,
        severityText,
        sum(cnt_minute) AS cnt
      FROM (
        SELECT
          toStartOfInterval(time, INTERVAL 1 MINUTE) AS minute,
          severityText,
          count() AS cnt_minute
        FROM ${LogAggregationService.TABLE_NAME}
        WHERE projectId = ${{
            type: TableColumnType.ObjectID,
            value: request.projectId,
        }}
          AND toStartOfInterval(time, INTERVAL 1 MINUTE) >= toStartOfInterval(${{
            type: TableColumnType.Date,
            value: request.startTime,
        }}, INTERVAL 1 MINUTE)
          AND toStartOfInterval(time, INTERVAL 1 MINUTE) <= toStartOfInterval(${{
            type: TableColumnType.Date,
            value: request.endTime,
        }}, INTERVAL 1 MINUTE)
    `;
        LogAggregationService.appendCommonFilters(statement, request);
        statement.append(" GROUP BY minute, severityText ) GROUP BY bucket, severityText ORDER BY bucket ASC");
        /*
         * Defense in depth: cap histogram runtime below nginx's 60s
         * proxy_read_timeout. 'break' returns partial aggregated results
         * rather than throwing, which is acceptable for a density viz.
         * Explicitly enable projection use.
         */
        statement.append(getQuerySettings({
            maxExecutionTimeInSeconds: 45,
            timeoutOverflowMode: "break",
            additionalSettings: { optimize_use_projections: 1 },
        }));
        return statement;
    }
    static buildFacetStatement(request) {
        var _a;
        // Pre-rename alias from stale clients; the V3 column is primaryEntityId.
        if (request.facetKey === "serviceId") {
            request.facetKey = "primaryEntityId";
        }
        const limit = (_a = request.limit) !== null && _a !== void 0 ? _a : LogAggregationService.DEFAULT_FACET_LIMIT;
        LogAggregationService.validateFacetKey(request.facetKey);
        const resourceServiceType = LogAggregationService.RESOURCE_FACET_KEYS.get(request.facetKey);
        const isResourceFacet = resourceServiceType !== undefined;
        const isTopLevelColumn = isResourceFacet ||
            LogAggregationService.isTopLevelColumn(request.facetKey);
        const statement = new Statement();
        if (isResourceFacet) {
            /*
             * Virtual facet — group primaryEntityId values whose row carries the
             * matching ServiceType discriminator (Host / DockerHost /
             * KubernetesCluster).
             */
            statement.append(SQL `SELECT toString(primaryEntityId) AS val, count() AS cnt FROM ${LogAggregationService.TABLE_NAME}`);
        }
        else if (isTopLevelColumn) {
            statement.append(SQL `SELECT toString(${request.facetKey}) AS val, count() AS cnt FROM ${LogAggregationService.TABLE_NAME}`);
        }
        else {
            // attributes is Map(String, String) — subscript access, not JSON functions.
            statement.append(SQL `SELECT attributes[${{
                type: TableColumnType.Text,
                value: request.facetKey,
            }}] AS val, count() AS cnt FROM ${LogAggregationService.TABLE_NAME}`);
        }
        statement.append(SQL ` WHERE projectId = ${{
            type: TableColumnType.ObjectID,
            value: request.projectId,
        }} AND time >= ${{
            type: TableColumnType.Date,
            value: request.startTime,
        }} AND time <= ${{
            type: TableColumnType.Date,
            value: request.endTime,
        }}`);
        if (isResourceFacet) {
            statement.append(SQL ` AND primaryEntityType = ${{
                type: TableColumnType.Text,
                value: resourceServiceType,
            }}`);
        }
        else if (request.facetKey === "primaryEntityId") {
            /*
             * Constrain the canonical Services facet to rows that actually
             * belong to a Service. NULL / empty primaryEntityType covers legacy
             * rows ingested before the discriminator existed.
             */
            statement.append(SQL ` AND (primaryEntityType = '' OR primaryEntityType = ${{
                type: TableColumnType.Text,
                value: ServiceType.OpenTelemetry,
            }})`);
        }
        else if (!isTopLevelColumn) {
            statement.append(SQL ` AND mapContains(attributes, ${{
                type: TableColumnType.Text,
                value: request.facetKey,
            }})`);
        }
        statement.append(LogAggregationService.RETENTION_FILTER);
        LogAggregationService.appendCommonFilters(statement, request);
        statement.append(SQL ` GROUP BY val ORDER BY cnt DESC LIMIT ${{
            type: TableColumnType.Number,
            value: limit,
        }}`);
        /*
         * Defense in depth: cap individual facet query runtime below nginx's
         * 60s proxy_read_timeout so a slow facet never starves the endpoint.
         */
        statement.append(getQuerySettings({
            maxExecutionTimeInSeconds: 45,
            timeoutOverflowMode: "break",
        }));
        return statement;
    }
    static async getAnalyticsTimeseries(request) {
        const statement = LogAggregationService.buildAnalyticsTimeseriesStatement(request);
        const dbResult = await LogDatabaseService.executeQuery(statement);
        const response = await dbResult.json();
        const rows = response.data || [];
        const groupByKeys = request.groupBy || [];
        return rows.map((row) => {
            const groupValues = {};
            for (const key of groupByKeys) {
                const alias = LogAggregationService.groupByAlias(key);
                groupValues[key] = String(row[alias] || "");
            }
            return {
                time: String(row["bucket"] || ""),
                count: Number(row["cnt"] || 0),
                groupValues,
            };
        });
    }
    static async getAnalyticsTopList(request) {
        if (!request.groupBy || request.groupBy.length === 0) {
            throw new BadDataException("groupBy with at least one dimension is required for top list");
        }
        const statement = LogAggregationService.buildAnalyticsTopListStatement(request);
        const dbResult = await LogDatabaseService.executeQuery(statement);
        const response = await dbResult.json();
        const rows = response.data || [];
        return rows
            .map((row) => {
            return {
                value: String(row["val"] || ""),
                count: Number(row["cnt"] || 0),
            };
        })
            .filter((item) => {
            return item.value.length > 0;
        });
    }
    static async getAnalyticsTable(request) {
        if (!request.groupBy || request.groupBy.length === 0) {
            throw new BadDataException("groupBy with at least one dimension is required for table");
        }
        const statement = LogAggregationService.buildAnalyticsTableStatement(request);
        const dbResult = await LogDatabaseService.executeQuery(statement);
        const response = await dbResult.json();
        const rows = response.data || [];
        const groupByKeys = request.groupBy;
        return rows.map((row) => {
            const groupValues = {};
            for (const key of groupByKeys) {
                const alias = LogAggregationService.groupByAlias(key);
                groupValues[key] = String(row[alias] || "");
            }
            return {
                groupValues,
                count: Number(row["cnt"] || 0),
            };
        });
    }
    static groupByAlias(key) {
        if (LogAggregationService.isTopLevelColumn(key)) {
            return key;
        }
        // For attribute keys, use a sanitized alias
        return `attr_${key.replace(/[^a-zA-Z0-9_]/g, "_")}`;
    }
    static appendGroupBySelect(statement, groupByKeys) {
        for (const key of groupByKeys) {
            LogAggregationService.validateFacetKey(key);
            if (LogAggregationService.isTopLevelColumn(key)) {
                statement.append(`, toString(${key}) AS ${key}`);
            }
            else {
                const alias = LogAggregationService.groupByAlias(key);
                statement.append(SQL `, attributes[${{
                    type: TableColumnType.Text,
                    value: key,
                }}] AS ${alias}`);
            }
        }
    }
    static appendGroupByClause(statement, groupByKeys) {
        for (const key of groupByKeys) {
            if (LogAggregationService.isTopLevelColumn(key)) {
                statement.append(`, ${key}`);
            }
            else {
                const alias = LogAggregationService.groupByAlias(key);
                statement.append(`, ${alias}`);
            }
        }
    }
    static getAggregationExpression(request) {
        if (request.aggregation === "unique" && request.aggregationField) {
            LogAggregationService.validateFacetKey(request.aggregationField);
            if (LogAggregationService.isTopLevelColumn(request.aggregationField)) {
                return `uniqExact(${request.aggregationField})`;
            }
            return `uniqExact(attributes['${request.aggregationField.replace(/'/g, "\\'")}'])`;
        }
        return "count()";
    }
    static validateGroupBy(groupBy) {
        if (!groupBy) {
            return;
        }
        if (groupBy.length > LogAggregationService.MAX_GROUP_BY_DIMENSIONS) {
            throw new BadDataException(`groupBy supports at most ${LogAggregationService.MAX_GROUP_BY_DIMENSIONS} dimensions`);
        }
        for (const key of groupBy) {
            LogAggregationService.validateFacetKey(key);
        }
    }
    static buildAnalyticsTimeseriesStatement(request) {
        LogAggregationService.validateGroupBy(request.groupBy);
        const intervalSeconds = request.bucketSizeInMinutes * 60;
        const aggExpr = LogAggregationService.getAggregationExpression(request);
        const statement = SQL `
      SELECT
        toStartOfInterval(time, INTERVAL ${{
            type: TableColumnType.Number,
            value: intervalSeconds,
        }} SECOND) AS bucket`;
        statement.append(`, ${aggExpr} AS cnt`);
        if (request.groupBy && request.groupBy.length > 0) {
            LogAggregationService.appendGroupBySelect(statement, request.groupBy);
        }
        statement.append(SQL `
      FROM ${LogAggregationService.TABLE_NAME}
      WHERE projectId = ${{
            type: TableColumnType.ObjectID,
            value: request.projectId,
        }}
        AND time >= ${{
            type: TableColumnType.Date,
            value: request.startTime,
        }}
        AND time <= ${{
            type: TableColumnType.Date,
            value: request.endTime,
        }}`);
        statement.append(LogAggregationService.RETENTION_FILTER);
        LogAggregationService.appendCommonFilters(statement, request);
        statement.append(" GROUP BY bucket");
        if (request.groupBy && request.groupBy.length > 0) {
            LogAggregationService.appendGroupByClause(statement, request.groupBy);
        }
        statement.append(" ORDER BY bucket ASC");
        /*
         * Defense in depth: cap runtime below the client's 58s request_timeout
         * (matches the histogram / facet paths above). 'break' returns partial
         * aggregated results rather than holding a pool connection.
         */
        statement.append(getQuerySettings({
            maxExecutionTimeInSeconds: 45,
            timeoutOverflowMode: "break",
        }));
        return statement;
    }
    static buildAnalyticsTopListStatement(request) {
        var _a;
        const groupByKey = request.groupBy[0];
        LogAggregationService.validateFacetKey(groupByKey);
        const limit = (_a = request.limit) !== null && _a !== void 0 ? _a : LogAggregationService.DEFAULT_ANALYTICS_LIMIT;
        const aggExpr = LogAggregationService.getAggregationExpression(request);
        const isTopLevel = LogAggregationService.isTopLevelColumn(groupByKey);
        const statement = new Statement();
        if (isTopLevel) {
            statement.append(`SELECT toString(${groupByKey}) AS val, ${aggExpr} AS cnt FROM ${LogAggregationService.TABLE_NAME}`);
        }
        else {
            statement.append(`SELECT attributes[`);
            statement.append(SQL `${{
                type: TableColumnType.Text,
                value: groupByKey,
            }}`);
            statement.append(`] AS val, ${aggExpr} AS cnt FROM ${LogAggregationService.TABLE_NAME}`);
        }
        statement.append(SQL ` WHERE projectId = ${{
            type: TableColumnType.ObjectID,
            value: request.projectId,
        }} AND time >= ${{
            type: TableColumnType.Date,
            value: request.startTime,
        }} AND time <= ${{
            type: TableColumnType.Date,
            value: request.endTime,
        }}`);
        if (!isTopLevel) {
            statement.append(SQL ` AND mapContains(attributes, ${{
                type: TableColumnType.Text,
                value: groupByKey,
            }})`);
        }
        statement.append(LogAggregationService.RETENTION_FILTER);
        LogAggregationService.appendCommonFilters(statement, request);
        statement.append(SQL ` GROUP BY val ORDER BY cnt DESC LIMIT ${{
            type: TableColumnType.Number,
            value: limit,
        }}`);
        /*
         * Cap runtime below the client's 58s request_timeout; 'break' returns
         * partial results (matches the histogram / facet paths).
         */
        statement.append(getQuerySettings({
            maxExecutionTimeInSeconds: 45,
            timeoutOverflowMode: "break",
        }));
        return statement;
    }
    static buildAnalyticsTableStatement(request) {
        var _a;
        LogAggregationService.validateGroupBy(request.groupBy);
        const groupByKeys = request.groupBy;
        const limit = (_a = request.limit) !== null && _a !== void 0 ? _a : LogAggregationService.DEFAULT_ANALYTICS_LIMIT;
        const aggExpr = LogAggregationService.getAggregationExpression(request);
        const statement = new Statement();
        statement.append(`SELECT ${aggExpr} AS cnt`);
        LogAggregationService.appendGroupBySelect(statement, groupByKeys);
        statement.append(SQL `
      FROM ${LogAggregationService.TABLE_NAME}
      WHERE projectId = ${{
            type: TableColumnType.ObjectID,
            value: request.projectId,
        }}
        AND time >= ${{
            type: TableColumnType.Date,
            value: request.startTime,
        }}
        AND time <= ${{
            type: TableColumnType.Date,
            value: request.endTime,
        }}`);
        statement.append(LogAggregationService.RETENTION_FILTER);
        LogAggregationService.appendCommonFilters(statement, request);
        // Build GROUP BY from aliases
        const aliases = groupByKeys.map((key) => {
            if (LogAggregationService.isTopLevelColumn(key)) {
                return key;
            }
            return LogAggregationService.groupByAlias(key);
        });
        statement.append(` GROUP BY ${aliases.join(", ")}`);
        statement.append(SQL ` ORDER BY cnt DESC LIMIT ${{
            type: TableColumnType.Number,
            value: limit,
        }}`);
        /*
         * Cap runtime below the client's 58s request_timeout; 'break' returns
         * partial results (matches the histogram / facet paths).
         */
        statement.append(getQuerySettings({
            maxExecutionTimeInSeconds: 45,
            timeoutOverflowMode: "break",
        }));
        return statement;
    }
    static appendCommonFilters(statement, request) {
        if (request.serviceIds && request.serviceIds.length > 0) {
            statement.append(SQL ` AND primaryEntityId IN (${{
                type: TableColumnType.ObjectID,
                value: new Includes(request.serviceIds.map((id) => {
                    return id.toString();
                })),
            }})`);
        }
        if (request.entityKeys && request.entityKeys.length > 0) {
            statement.append(SQL ` AND hasAny(entityKeys, ${{
                type: TableColumnType.ArrayText,
                value: request.entityKeys,
            }})`);
        }
        if (request.severityTexts && request.severityTexts.length > 0) {
            statement.append(SQL ` AND severityText IN (${{
                type: TableColumnType.Text,
                value: new Includes(request.severityTexts),
            }})`);
        }
        if (request.traceIds && request.traceIds.length > 0) {
            statement.append(SQL ` AND traceId IN (${{
                type: TableColumnType.Text,
                value: new Includes(request.traceIds),
            }})`);
        }
        if (request.spanIds && request.spanIds.length > 0) {
            statement.append(SQL ` AND spanId IN (${{
                type: TableColumnType.Text,
                value: new Includes(request.spanIds),
            }})`);
        }
        if (request.bodySearchText && request.bodySearchText.trim().length > 0) {
            statement.append(SQL ` AND body ILIKE ${{
                type: TableColumnType.Text,
                value: `%${request.bodySearchText.trim()}%`,
            }}`);
        }
        if (request.attributes && Object.keys(request.attributes).length > 0) {
            for (const [attrKey, attrValue] of Object.entries(request.attributes)) {
                LogAggregationService.validateFacetKey(attrKey);
                /*
                 * Match attribute keys case-insensitively — keys in the data come
                 * from many sources (OTEL conventions are dot.lowercase, app code
                 * often uses camelCase like `requestId`), and forcing users to
                 * remember the exact casing is a poor experience. The user-supplied
                 * key is validated above.
                 */
                statement.append(SQL ` AND arrayExists((k, v) -> lowerUTF8(k) = lowerUTF8(${{
                    type: TableColumnType.Text,
                    value: attrKey,
                }}) AND v = ${{
                    type: TableColumnType.Text,
                    value: attrValue,
                }}, mapKeys(attributes), mapValues(attributes))`);
            }
        }
    }
    static async getExportLogs(request) {
        const maxLimit = Math.min(request.limit || 10000, 10000);
        const statement = SQL `
      SELECT
        time,
        primaryEntityId,
        severityText,
        severityNumber,
        body,
        traceId,
        spanId,
        attributes
      FROM ${LogAggregationService.TABLE_NAME}
      WHERE projectId = ${{
            type: TableColumnType.ObjectID,
            value: request.projectId,
        }}
        AND time >= ${{
            type: TableColumnType.Date,
            value: request.startTime,
        }}
        AND time <= ${{
            type: TableColumnType.Date,
            value: request.endTime,
        }}
    `;
        statement.append(LogAggregationService.RETENTION_FILTER);
        LogAggregationService.appendCommonFilters(statement, request);
        statement.append(SQL ` ORDER BY time DESC LIMIT ${{
            type: TableColumnType.Number,
            value: maxLimit,
        }}`);
        /*
         * Cap runtime below the client's 58s request_timeout; 'break' returns
         * partial rows rather than holding a pool connection on a large export.
         */
        statement.append(getQuerySettings({
            maxExecutionTimeInSeconds: 45,
            timeoutOverflowMode: "break",
        }));
        const dbResult = await LogDatabaseService.executeQuery(statement);
        const response = await dbResult.json();
        return response.data || [];
    }
    static async getLogContext(request) {
        const count = Math.min(request.count || 5, 20);
        const beforeStatement = SQL `
      SELECT
        _id,
        time,
        timeUnixNano,
        primaryEntityId,
        severityText,
        severityNumber,
        body,
        traceId,
        spanId,
        attributes
      FROM ${LogAggregationService.TABLE_NAME}
      WHERE projectId = ${{
            type: TableColumnType.ObjectID,
            value: request.projectId,
        }}
        AND primaryEntityId = ${{
            type: TableColumnType.ObjectID,
            value: request.primaryEntityId,
        }}
        AND time <= ${{
            type: TableColumnType.Date,
            value: request.time,
        }}
        AND _id != ${{
            type: TableColumnType.Text,
            value: request.logId,
        }}
        AND retentionDate >= now()
      ORDER BY time DESC, timeUnixNano DESC
      LIMIT ${{
            type: TableColumnType.Number,
            value: count,
        }}
    `;
        const afterStatement = SQL `
      SELECT
        _id,
        time,
        timeUnixNano,
        primaryEntityId,
        severityText,
        severityNumber,
        body,
        traceId,
        spanId,
        attributes
      FROM ${LogAggregationService.TABLE_NAME}
      WHERE projectId = ${{
            type: TableColumnType.ObjectID,
            value: request.projectId,
        }}
        AND primaryEntityId = ${{
            type: TableColumnType.ObjectID,
            value: request.primaryEntityId,
        }}
        AND time >= ${{
            type: TableColumnType.Date,
            value: request.time,
        }}
        AND _id != ${{
            type: TableColumnType.Text,
            value: request.logId,
        }}
        AND retentionDate >= now()
      ORDER BY time ASC, timeUnixNano ASC
      LIMIT ${{
            type: TableColumnType.Number,
            value: count,
        }}
    `;
        const [beforeResult, afterResult] = await Promise.all([
            LogDatabaseService.executeQuery(beforeStatement),
            LogDatabaseService.executeQuery(afterStatement),
        ]);
        const beforeResponse = await beforeResult.json();
        const afterResponse = await afterResult.json();
        const beforeRows = (beforeResponse.data || []).reverse();
        const afterRows = afterResponse.data || [];
        return { before: beforeRows, after: afterRows };
    }
    static async getDropFilterEstimate(request) {
        var _a, _b;
        // Get total count
        const totalStatement = SQL `
      SELECT count() AS cnt
      FROM ${LogAggregationService.TABLE_NAME}
      WHERE projectId = ${{
            type: TableColumnType.ObjectID,
            value: request.projectId,
        }}
        AND time >= ${{
            type: TableColumnType.Date,
            value: request.startTime,
        }}
        AND time <= ${{
            type: TableColumnType.Date,
            value: request.endTime,
        }}
    `;
        totalStatement.append(LogAggregationService.RETENTION_FILTER);
        LogAggregationService.appendCommonFilters(totalStatement, request);
        /*
         * Cap the count scan below the client's 58s request_timeout; 'break'
         * returns a partial (lower-bound) count, acceptable for an estimate.
         */
        totalStatement.append(getQuerySettings({
            maxExecutionTimeInSeconds: 45,
            timeoutOverflowMode: "break",
        }));
        // Get matching count using the filter query as body search
        const matchStatement = SQL `
      SELECT count() AS cnt
      FROM ${LogAggregationService.TABLE_NAME}
      WHERE projectId = ${{
            type: TableColumnType.ObjectID,
            value: request.projectId,
        }}
        AND time >= ${{
            type: TableColumnType.Date,
            value: request.startTime,
        }}
        AND time <= ${{
            type: TableColumnType.Date,
            value: request.endTime,
        }}
    `;
        matchStatement.append(LogAggregationService.RETENTION_FILTER);
        LogAggregationService.appendCommonFilters(matchStatement, Object.assign(Object.assign({}, request), { bodySearchText: request.filterQuery }));
        /*
         * Cap the count scan below the client's 58s request_timeout; 'break'
         * returns a partial (lower-bound) count, acceptable for an estimate.
         */
        matchStatement.append(getQuerySettings({
            maxExecutionTimeInSeconds: 45,
            timeoutOverflowMode: "break",
        }));
        const [totalResult, matchResult] = await Promise.all([
            LogDatabaseService.executeQuery(totalStatement),
            LogDatabaseService.executeQuery(matchStatement),
        ]);
        const totalResponse = await totalResult.json();
        const matchResponse = await matchResult.json();
        const totalData = totalResponse.data || [];
        const matchData = matchResponse.data || [];
        const totalLogs = Number(((_a = totalData[0]) === null || _a === void 0 ? void 0 : _a["cnt"]) || 0);
        const matchingLogs = Number(((_b = matchData[0]) === null || _b === void 0 ? void 0 : _b["cnt"]) || 0);
        const estimatedReductionPercent = totalLogs > 0 ? Math.round((matchingLogs / totalLogs) * 100) : 0;
        return { totalLogs, matchingLogs, estimatedReductionPercent };
    }
    static isTopLevelColumn(key) {
        return LogAggregationService.TOP_LEVEL_COLUMNS.has(key);
    }
    static validateFacetKey(facetKey) {
        if (typeof facetKey !== "string") {
            throw new BadDataException("Invalid facetKey");
        }
        if (facetKey.length === 0 ||
            facetKey.length > LogAggregationService.MAX_FACET_KEY_LENGTH) {
            throw new BadDataException("Invalid facetKey");
        }
        if (LogAggregationService.isTopLevelColumn(facetKey) ||
            LogAggregationService.RESOURCE_FACET_KEYS.has(facetKey)) {
            return;
        }
        if (!LogAggregationService.ATTRIBUTE_KEY_PATTERN.test(facetKey)) {
            throw new BadDataException("Invalid facetKey");
        }
    }
}
LogAggregationService.DEFAULT_FACET_LIMIT = 500;
LogAggregationService.TABLE_NAME = AnalyticsTableName.Log;
LogAggregationService.TOP_LEVEL_COLUMNS = new Set([
    "severityText",
    "primaryEntityId",
    "traceId",
    "spanId",
]);
/*
 * Virtual facet keys that don't correspond to real ClickHouse columns —
 * they all read out of `primaryEntityId` filtered by `primaryEntityType`.
 * The discriminator was added so host / docker host / k8s cluster
 * telemetry could reuse the `primaryEntityId` slot instead of synthesising
 * phantom Service rows; these facets surface each resource type
 * independently.
 */
LogAggregationService.RESOURCE_FACET_KEYS = new Map([
    ["hostId", ServiceType.Host],
    ["dockerHostId", ServiceType.DockerHost],
    ["kubernetesClusterId", ServiceType.KubernetesCluster],
    ["serverlessFunctionId", ServiceType.ServerlessFunction],
    ["cloudResourceId", ServiceType.CloudResource],
    ["rumApplicationId", ServiceType.RealUserMonitor],
]);
LogAggregationService.ATTRIBUTE_KEY_PATTERN = /^[a-zA-Z0-9._:/-]+$/;
LogAggregationService.MAX_FACET_KEY_LENGTH = 256;
/*
 * Read-side retention filter (mirrors
 * AnalyticsDatabaseService.getRetentionReadFilter): rows past their
 * per-service retention stay queryable until their whole part drops
 * (ttl_only_drop_parts), so raw-table reads exclude them explicitly.
 * Deliberately NOT applied to projection-shaped queries (the severity
 * histogram): an aggregate projection cannot evaluate a predicate on a
 * column it does not store, so adding it would silently force a full
 * base-table scan.
 */
LogAggregationService.RETENTION_FILTER = " AND retentionDate >= now()";
LogAggregationService.DEFAULT_ANALYTICS_LIMIT = 10;
LogAggregationService.MAX_GROUP_BY_DIMENSIONS = 2;
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], LogAggregationService, "getHistogram", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], LogAggregationService, "getFacetValues", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], LogAggregationService, "getAnalyticsTimeseries", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], LogAggregationService, "getAnalyticsTopList", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], LogAggregationService, "getAnalyticsTable", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], LogAggregationService, "getExportLogs", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], LogAggregationService, "getLogContext", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], LogAggregationService, "getDropFilterEstimate", null);
export default LogAggregationService;
//# sourceMappingURL=LogAggregationService.js.map