mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-15 12:58:33 +00:00
# Conflicts: # .github/workflows/build-community.yaml # .github/workflows/build-enterprise.yaml # .github/workflows/gor-signoz-community.yaml # cmd/community/Dockerfile # cmd/community/Dockerfile.multi-arch # cmd/community/main.go # cmd/community/server.go # cmd/enterprise/Dockerfile # cmd/enterprise/Dockerfile.integration # cmd/enterprise/Dockerfile.with-web.integration # cmd/server/metastore.go # cmd/zap.go # conf/prometheus.yml # deploy/docker-swarm/generator/hotrod/docker-compose.yaml # deploy/docker-swarm/generator/infra/docker-compose.yaml # deploy/docker-swarm/generator/infra/otel-agent-config.yaml # deploy/docker-swarm/generator/infra/otel-metrics-config.yaml # deploy/docker/generator/hotrod/docker-compose.yaml # deploy/docker/generator/infra/docker-compose.yaml # deploy/docker/generator/infra/otel-collector-config.yaml # docs/contributing/go/integration.md # ee/anomaly/hourly.go # ee/anomaly/params.go # ee/anomaly/seasonal.go # ee/authn/callbackauthn/oidccallbackauthn/authn.go # ee/authz/openfgaauthz/provider.go # ee/authz/openfgaschema/base.fga # ee/authz/openfgaserver/server.go # ee/licensing/config.go # ee/licensing/httplicensing/provider.go # ee/modules/dashboard/impldashboard/module.go # ee/querier/handler.go # ee/query-service/anomaly/seasonal.go # ee/query-service/app/api/cloudIntegrations.go # ee/query-service/app/api/featureFlags.go # ee/query-service/app/api/license.go # ee/query-service/app/api/queryrange.go # ee/query-service/rules/anomaly.go # ee/query-service/rules/anomaly_test.go # ee/query-service/rules/manager.go # ee/query-service/rules/manager_test.go # ee/query-service/usage/manager.go # ee/sqlschema/postgressqlschema/provider.go # ee/sqlstore/postgressqlstore/dialect.go # ee/sqlstore/postgressqlstore/provider.go # ee/zeus/config.go # ee/zeus/httpzeus/provider.go # frontend/.eslintrc.cjs # frontend/e2e/test-plan/README.md # frontend/e2e/test-plan/validation-report.md # frontend/e2e/tests/settings/account-settings/account-settings-settings.spec.ts # frontend/e2e/tests/settings/api-keys/api-keys-settings.spec.ts # frontend/playwright.config.ts # frontend/public/Images/signoz-hero-image.webp # frontend/public/Logos/signoz-brand-logo.svg # frontend/scripts/generate-permissions-type.cjs # frontend/src/assets/Images/o11y-hero-image.webp # frontend/src/assets/Images/signoz-hero-image.webp # frontend/src/assets/Logos/hanzo-brand-logo.svg # frontend/src/assets/Logos/hanzo-icon.svg # frontend/src/assets/Logos/o11y-brand-logo.svg # frontend/src/assets/Logos/signoz-brand-logo.svg # frontend/src/assets/SigNoz-dark.svg # frontend/src/assets/SigNoz-white.svg # frontend/src/assets/signoz.svg # frontend/src/components/DetailsDrawer/DetailsDrawer.styles.scss # frontend/src/components/HostMetricsDetail/HostMetricTraces/HostMetricTraces.styles.scss # frontend/src/components/HostMetricsDetail/HostMetricTraces/constants.ts # frontend/src/components/HostMetricsDetail/HostMetricsDetail.styles.scss # frontend/src/components/HostMetricsDetail/HostMetricsDetails.tsx # frontend/src/components/HostMetricsDetail/HostMetricsLogs/NoLogsContainer.tsx # frontend/src/components/HostMetricsDetail/HostMetricsLogs/constants.ts # frontend/src/components/LogsDownloadOptionsMenu/LogsDownloadOptionsMenu.styles.scss # frontend/src/components/WelcomeLeftContainer/index.tsx # frontend/src/container/APIKeys/APIKeys.styles.scss # frontend/src/container/APIKeys/APIKeys.test.tsx # frontend/src/container/APIKeys/APIKeys.tsx # frontend/src/container/CloudIntegrationPage/Header/Header.tsx # frontend/src/container/CloudIntegrationPage/HeroSection/HeroSection.tsx # frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountActions.style.scss # frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountActions.tsx # frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountSettingsModal.style.scss # frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountSettingsModal.tsx # frontend/src/container/CloudIntegrationPage/HeroSection/components/CloudAccountSetupModal.style.scss # frontend/src/container/CloudIntegrationPage/HeroSection/components/RemoveIntegrationAccount.tsx # frontend/src/container/CloudIntegrationPage/ServicesSection/ConfigureServiceModal.tsx # frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesTabs.style.scss # frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesTabs.tsx # frontend/src/container/DashboardContainer/DashboardSettings/General/AddTags/AddTags.styles.scss # frontend/src/container/DashboardContainer/DashboardSettings/General/GeneralSettings.styles.scss # frontend/src/container/FormAlertRules/ChQuerySection/ChQuerySection.tsx # frontend/src/container/FormAlertRules/UserGuide/index.tsx # frontend/src/container/InfraMonitoringHosts/HostsEmptyOrIncorrectMetrics.tsx # frontend/src/container/InfraMonitoringHosts/InfraMonitoring.styles.scss # frontend/src/container/InfraMonitoringHosts/__tests__/HostsEmptyOrIncorrectMetrics.test.tsx # frontend/src/container/InfraMonitoringK8s/Clusters/ClusterDetails/ClusterDetails.tsx # frontend/src/container/InfraMonitoringK8s/Clusters/utils.tsx # frontend/src/container/InfraMonitoringK8s/DaemonSets/DaemonSetDetails/DaemonSetDetails.tsx # frontend/src/container/InfraMonitoringK8s/DaemonSets/utils.tsx # frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.tsx # frontend/src/container/InfraMonitoringK8s/Deployments/utils.tsx # frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityEvents/entityEvents.styles.scss # frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityTraces/entityTraces.styles.scss # frontend/src/container/InfraMonitoringK8s/InfraMonitoringK8s.styles.scss # frontend/src/container/InfraMonitoringK8s/Jobs/JobDetails/JobDetails.tsx # frontend/src/container/InfraMonitoringK8s/Jobs/utils.tsx # frontend/src/container/InfraMonitoringK8s/K8sEmptyOrIncorrectMetrics.tsx # frontend/src/container/InfraMonitoringK8s/K8sFiltersSidePanel/K8sFiltersSidePanel.styles.scss # frontend/src/container/InfraMonitoringK8s/Namespaces/NamespaceDetails/NamespaceDetails.styles.scss # frontend/src/container/InfraMonitoringK8s/Namespaces/NamespaceDetails/NamespaceDetails.tsx # frontend/src/container/InfraMonitoringK8s/Namespaces/utils.tsx # frontend/src/container/InfraMonitoringK8s/Nodes/NodeDetails/NodeDetails.tsx # frontend/src/container/InfraMonitoringK8s/Nodes/utils.tsx # frontend/src/container/InfraMonitoringK8s/Pods/PodDetails/PodDetails.tsx # frontend/src/container/InfraMonitoringK8s/StatefulSets/StatefulSetDetails/StatefulSetDetails.tsx # frontend/src/container/InfraMonitoringK8s/StatefulSets/utils.tsx # frontend/src/container/InfraMonitoringK8s/Volumes/VolumeDetails/VolumeDetails.tsx # frontend/src/container/InfraMonitoringK8s/Volumes/utils.tsx # frontend/src/container/ListAlertRules/AlertsEmptyState/AlertsEmptyState.styles.scss # frontend/src/container/ListAlertRules/ListAlert.tsx # frontend/src/container/LogsContextList/styles.ts # frontend/src/container/LogsExplorerList/ColumnView/ColumnView.tsx # frontend/src/container/LogsExplorerViews/QueryStatus.tsx # frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexTraces.tsx # frontend/src/container/MetricsExplorer/Explorer/RelatedMetrics.tsx # frontend/src/container/MetricsExplorer/Explorer/useGetRelatedMetricsGraphs.ts # frontend/src/container/MetricsExplorer/Summary/MetricTypeSearch.tsx # frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/ClickHouse/index.tsx # frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/ClickHouse/query.tsx # frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/index.tsx # frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/query.tsx # frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/datastore/index.tsx # frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/datastore/query.tsx # frontend/src/container/OnboardingQuestionaire/OnboardingFooter/OnboardingFooter.tsx # frontend/src/container/OnboardingQuestionaire/OnboardingHeader/OnboardingHeader.styles.scss # frontend/src/container/OnboardingQuestionaire/OnboardingHeader/OnboardingHeader.tsx # frontend/src/container/OnboardingV2Container/onboarding-configs/onboarding-config-with-links.json # frontend/src/container/QueryBuilder/QueryBuilder.tsx # frontend/src/container/QueryBuilder/components/Query/Query.tsx # frontend/src/container/RolesSettings/__tests__/utils.test.ts # frontend/src/container/ServiceTable/SkipOnBoardModal/index.tsx # frontend/src/container/TraceDetail/Missingtrace.tsx # frontend/src/container/TraceDetail/SelectedSpanDetails/config.ts # frontend/src/lib/uPlotV2/components/Tooltip/Tooltip.styles.scss # frontend/src/mocks-server/__mockdata__/apiKeys.ts # frontend/src/pages/AlertDetails/AlertHeader/AlertStatus/AlertStatus.tsx # frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.styles.scss # frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.tsx # frontend/src/pages/Integrations/IntegrationsList.tsx # frontend/src/pages/Integrations/utils.ts # frontend/src/types/api/integrations/aws.ts # pkg/alertmanager/o11yalertmanager/handler.go # pkg/apiserver/o11yapiserver/alertmanager.go # pkg/apiserver/o11yapiserver/cloudintegration.go # pkg/apiserver/o11yapiserver/inframonitoring.go # pkg/apiserver/o11yapiserver/llmpricingrule.go # pkg/apiserver/o11yapiserver/rawdataexport.go # pkg/apiserver/o11yapiserver/registry.go # pkg/apiserver/o11yapiserver/ruler.go # pkg/apiserver/o11yapiserver/ruler_examples.go # pkg/apiserver/o11yapiserver/ruler_examples_test.go # pkg/apiserver/o11yapiserver/rulestatehistory.go # pkg/apiserver/o11yapiserver/spanmapper.go # pkg/apiserver/o11yapiserver/tracedetail.go # pkg/authn/callbackauthn/googlecallbackauthn/authn.go # pkg/authz/openfgaauthz/provider.go # pkg/authz/openfgaschema/base.fga # pkg/authz/openfgaserver/server.go # pkg/authz/openfgaserver/server_test.go # pkg/authz/openfgaserver/sqlstore.go # pkg/http/middleware/api_key.go # pkg/http/middleware/authn.go # pkg/instrumentation/metric.go # pkg/modules/user/impluser/module.go # pkg/prometheus/clickhouseprometheus/client.go # pkg/prometheus/clickhouseprometheus/client_query_test.go # pkg/prometheus/clickhouseprometheus/provider.go # pkg/prometheus/config.go # pkg/prometheus/engine.go # pkg/prometheus/label.go # pkg/prometheus/prometheus.go # pkg/prometheus/prometheustest/provider.go # pkg/prometheus/prometheustest/utils_test.go # pkg/querier/promql_query.go # pkg/query-service/app/cloudintegrations/Readme.md # pkg/query-service/app/cloudintegrations/accountsRepo.go # pkg/query-service/app/cloudintegrations/constants.go # pkg/query-service/app/cloudintegrations/controller.go # pkg/query-service/app/cloudintegrations/models.go # pkg/query-service/app/cloudintegrations/serviceconfig_db.go # pkg/query-service/app/cloudintegrations/services/definitions/aws/eks/assets/dashboards/containerinsights.json # pkg/query-service/app/cloudintegrations/services/definitions/aws/eks/assets/dashboards/containerinsights_dot.json # pkg/query-service/app/cloudintegrations/services/definitions/aws/eks/assets/dashboards/overview.json # pkg/query-service/app/cloudintegrations/services/definitions/aws/eks/assets/dashboards/overview_dot.json # pkg/query-service/app/cloudintegrations/services/models.go # pkg/query-service/app/cloudintegrations/services/services.go # pkg/query-service/app/cloudintegrations/services/services_test.go # pkg/query-service/app/integrations/builtin_integrations/clickhouse/assets/dashboards/overview_dot.json # pkg/query-service/app/integrations/builtin_integrations/datastore/assets/dashboards/overview_dot.json # pkg/query-service/app/metricsexplorer/parser.go # pkg/query-service/app/metricsexplorer/summary.go # pkg/query-service/app/querier/querier_test.go # pkg/query-service/app/querier/v2/querier_test.go # pkg/query-service/app/summary.go # pkg/query-service/model/metrics_explorer/summary.go # pkg/query-service/rules/base_rule_test.go # pkg/query-service/rules/manager_test.go # pkg/query-service/rules/prom_rule.go # pkg/query-service/rules/prom_rule_task.go # pkg/query-service/rules/prom_rule_test.go # pkg/query-service/rules/threshold_rule_test.go # pkg/querybuilder/resourcefilter/tables.go # pkg/queryparser/queryfilterextractor/promql.go # pkg/ruler/o11yruler/handler.go # pkg/sqlmigration/088_migrate_ii_dashboards/clickhouse/overview.json # pkg/telemetrymetadata/metadata_test.go # pkg/telemetrystore/datastoretelemetrystore/telemetry.go # pkg/types/authtypes/name.go # pkg/types/authtypes/object.go # pkg/types/authtypes/selector.go # pkg/types/authtypes/typeable.go # pkg/types/authtypes/typeable_anonymous.go # pkg/types/authtypes/typeable_metaresource.go # pkg/types/authtypes/typeable_metaresources.go # pkg/types/authtypes/typeable_organization.go # pkg/types/authtypes/typeable_role.go # pkg/types/authtypes/typeable_user.go # pkg/types/authtypes/uuid.go # pkg/types/ctxtypes/auth.go # pkg/types/factor_api_key.go # pkg/types/integrationtypes/integration.go # pkg/types/roletypes/store.go # pkg/types/ruletypes/maintenance.go # pkg/types/serviceaccounttypes/service_account_role.go # pkg/types/serviceaccounttypes/store.go
285 lines
11 KiB
Go
285 lines
11 KiB
Go
package querybuilder
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
qbtypes "github.com/hanzoai/o11y/pkg/types/querybuildertypes/querybuildertypesv5"
|
|
"github.com/hanzoai/o11y/pkg/types/telemetrytypes"
|
|
)
|
|
|
|
// AdjustDuplicateKeys adjusts duplicate keys in the query by removing specific context and data type
|
|
// if the same key appears with different contexts or data types across SelectFields, GroupBy, and OrderBy.
|
|
// This ensures that each key is unique and generic enough to cover all its usages in the query.
|
|
func AdjustDuplicateKeys[T any](query *qbtypes.QueryBuilderQuery[T]) []string {
|
|
|
|
// Create a map to track unique keys across SelectFields, GroupBy, and OrderBy
|
|
globalUniqueKeysMap := map[string]telemetrytypes.TelemetryFieldKey{}
|
|
|
|
// for recording modifications
|
|
actions := []string{}
|
|
|
|
// SelectFields
|
|
for _, key := range query.SelectFields {
|
|
deduplicateKeys(key, globalUniqueKeysMap, &actions)
|
|
}
|
|
|
|
// GroupBy
|
|
for _, key := range query.GroupBy {
|
|
deduplicateKeys(key.TelemetryFieldKey, globalUniqueKeysMap, &actions)
|
|
}
|
|
|
|
// OrderBy
|
|
for _, key := range query.Order {
|
|
deduplicateKeys(key.Key.TelemetryFieldKey, globalUniqueKeysMap, &actions)
|
|
}
|
|
|
|
// Reconstruct SelectFields slice
|
|
|
|
newSelectFields := make([]telemetrytypes.TelemetryFieldKey, 0, len(query.SelectFields))
|
|
|
|
seen := map[string]bool{}
|
|
for _, key := range query.SelectFields {
|
|
if !seen[key.Name] {
|
|
newSelectFields = append(newSelectFields, globalUniqueKeysMap[key.Name])
|
|
seen[key.Name] = true
|
|
} else {
|
|
actions = append(actions, fmt.Sprintf("Skipped duplicate SelectField key %s", key))
|
|
}
|
|
}
|
|
query.SelectFields = newSelectFields
|
|
|
|
// Reconstruct GroupBy slice
|
|
newGroupBy := make([]qbtypes.GroupByKey, 0, len(query.GroupBy))
|
|
seen = map[string]bool{}
|
|
for _, key := range query.GroupBy {
|
|
if !seen[key.Name] {
|
|
newGroupBy = append(newGroupBy, qbtypes.GroupByKey{TelemetryFieldKey: globalUniqueKeysMap[key.Name]})
|
|
seen[key.Name] = true
|
|
} else {
|
|
actions = append(actions, fmt.Sprintf("Skipped duplicate GroupBy key %s", key))
|
|
}
|
|
}
|
|
query.GroupBy = newGroupBy
|
|
|
|
// Reconstruct OrderBy slice
|
|
// NOTE: 1 Edge case here is that if there are two order by on same key with different directions,
|
|
// we will only keep one of them (the first one encountered). This is acceptable as such queries
|
|
// don't make much sense.
|
|
newOrderBy := make([]qbtypes.OrderBy, 0, len(query.Order))
|
|
seen = map[string]bool{}
|
|
for _, key := range query.Order {
|
|
if !seen[key.Key.Name] {
|
|
newOrderBy = append(newOrderBy, qbtypes.OrderBy{Key: qbtypes.OrderByKey{TelemetryFieldKey: globalUniqueKeysMap[key.Key.Name]}, Direction: key.Direction})
|
|
seen[key.Key.Name] = true
|
|
} else {
|
|
actions = append(actions, fmt.Sprintf("Skipped duplicate OrderBy key %s", key.Key))
|
|
}
|
|
}
|
|
query.Order = newOrderBy
|
|
|
|
return actions
|
|
}
|
|
|
|
func deduplicateKeys(key telemetrytypes.TelemetryFieldKey, keysMap map[string]telemetrytypes.TelemetryFieldKey, actions *[]string) {
|
|
if existingKey, ok := keysMap[key.Name]; !ok {
|
|
keysMap[key.Name] = key
|
|
} else {
|
|
if existingKey.FieldContext != key.FieldContext && existingKey.FieldContext != telemetrytypes.FieldContextUnspecified {
|
|
// remove field context in the map to make it generic
|
|
*actions = append(*actions, fmt.Sprintf("Removed field context from %s for duplicate key %s", existingKey, key))
|
|
existingKey.FieldContext = telemetrytypes.FieldContextUnspecified
|
|
}
|
|
if existingKey.FieldDataType != key.FieldDataType && existingKey.FieldDataType != telemetrytypes.FieldDataTypeUnspecified {
|
|
// remove field data type in the map to make it generic
|
|
*actions = append(*actions, fmt.Sprintf("Removed field data type from %s for duplicate key %s", existingKey, key))
|
|
existingKey.FieldDataType = telemetrytypes.FieldDataTypeUnspecified
|
|
}
|
|
// Update the map with the modified key
|
|
keysMap[key.Name] = existingKey
|
|
}
|
|
}
|
|
|
|
func AdjustKey(key *telemetrytypes.TelemetryFieldKey, keys map[string][]*telemetrytypes.TelemetryFieldKey, intrinsicOrCalculatedField *telemetrytypes.TelemetryFieldKey) []string {
|
|
|
|
// for recording modifications
|
|
actions := []string{}
|
|
|
|
if intrinsicOrCalculatedField != nil {
|
|
/*
|
|
Check if it also matches with any of the metadata keys
|
|
|
|
For example, lets consider trace_id exists in attributes and is also an intrinsic field
|
|
|
|
Now if user is using trace_id, we don't know if they mean intrinsic field or attribute.trace_id
|
|
So we cannot take a call here (we'll leave this upto query builder to decide).
|
|
|
|
However, if user is using attribute.trace_id, we can safely assume they mean attribute field
|
|
and not intrinsic field.
|
|
Similarly, if user is using trace_id with a field context or data type that doesn't match
|
|
the intrinsic field, and there is no matching key in the metadata with the same name,
|
|
we can safely assume they mean the intrinsic field and override the context and data type.
|
|
|
|
*/
|
|
|
|
// Check if there is any matching key in the metadata with the same name and it is not the same intrinsic/calculated field
|
|
match := false
|
|
for _, mapKey := range keys[key.Name] {
|
|
// Either field context is unspecified or matches
|
|
// and
|
|
// Either field data type is unspecified or matches
|
|
if (key.FieldContext == telemetrytypes.FieldContextUnspecified || mapKey.FieldContext == key.FieldContext) &&
|
|
(key.FieldDataType == telemetrytypes.FieldDataTypeUnspecified || mapKey.FieldDataType == key.FieldDataType) &&
|
|
!mapKey.Equal(intrinsicOrCalculatedField) {
|
|
match = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// NOTE: if a user is highly opinionated and use attribute.duration_nano:string
|
|
// It will be defaulted to intrinsic field duration_nano as the actual attribute might be attribute.duration_nano:number
|
|
|
|
// We don't have a match, then it doesn't exist in attribute or resource attribute
|
|
// use the intrinsic/calculated field
|
|
if !match {
|
|
// This is the case where user is using an intrinsic/calculated field
|
|
// with a context or data type that may or may not match the intrinsic/calculated field
|
|
// and there is no matching key in the metadata with the same name
|
|
// So we can safely override the context and data type
|
|
|
|
actions = append(actions, fmt.Sprintf("Overriding key: %s to %s", key, intrinsicOrCalculatedField))
|
|
key.OverrideMetadataFrom(intrinsicOrCalculatedField)
|
|
return actions
|
|
|
|
}
|
|
}
|
|
|
|
// This means that the key provided by the user cannot be overridden to a single field because of ambiguity
|
|
// So we need to look into metadata keys to find the best match
|
|
|
|
// check if all the keys for the given field with matching context and data type
|
|
matchingKeys := []*telemetrytypes.TelemetryFieldKey{}
|
|
for _, metadataKey := range keys[key.Name] {
|
|
// Only consider keys that match the context and data type (if specified)
|
|
if (key.FieldContext == telemetrytypes.FieldContextUnspecified || key.FieldContext == metadataKey.FieldContext) &&
|
|
(key.FieldDataType == telemetrytypes.FieldDataTypeUnspecified || key.FieldDataType == metadataKey.FieldDataType) {
|
|
matchingKeys = append(matchingKeys, metadataKey)
|
|
}
|
|
}
|
|
|
|
// Also consider if context is actually part of the key name
|
|
contextPrefixedMatchingKeys := []*telemetrytypes.TelemetryFieldKey{}
|
|
if key.FieldContext != telemetrytypes.FieldContextUnspecified {
|
|
for _, metadataKey := range keys[key.FieldContext.StringValue()+"."+key.Name] {
|
|
// Since we prefixed the context in the name, we only need to match data type
|
|
if key.FieldDataType == telemetrytypes.FieldDataTypeUnspecified || key.FieldDataType == metadataKey.FieldDataType {
|
|
contextPrefixedMatchingKeys = append(contextPrefixedMatchingKeys, metadataKey)
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(matchingKeys)+len(contextPrefixedMatchingKeys) == 0 {
|
|
// we do not have any matching keys, most likely user made a mistake, let downstream query builder handle it
|
|
// Set materialized to false explicitly to avoid QB looking for materialized column
|
|
key.Materialized = false
|
|
} else if len(matchingKeys)+len(contextPrefixedMatchingKeys) == 1 {
|
|
// only one matching key, use it
|
|
var matchingKey *telemetrytypes.TelemetryFieldKey
|
|
if len(matchingKeys) == 1 {
|
|
matchingKey = matchingKeys[0]
|
|
} else {
|
|
matchingKey = contextPrefixedMatchingKeys[0]
|
|
}
|
|
|
|
if !key.Equal(matchingKey) {
|
|
actions = append(actions, fmt.Sprintf("Adjusting key %s to %s", key, matchingKey))
|
|
}
|
|
|
|
key.Name = matchingKey.Name
|
|
key.OverrideMetadataFrom(matchingKey)
|
|
|
|
return actions
|
|
} else {
|
|
// multiple matching keys, set materialized only if all the keys are materialized
|
|
// TODO: This could all be redundant if it is not, it should be.
|
|
// Downstream query builder should handle multiple matching keys with their own metadata
|
|
// and not rely on this function to do so.
|
|
materialized := true
|
|
indexes := []telemetrytypes.TelemetryFieldKeySkipIndex{}
|
|
fieldContextsSeen := map[telemetrytypes.FieldContext]bool{}
|
|
dataTypesSeen := map[telemetrytypes.FieldDataType]bool{}
|
|
for _, matchingKey := range matchingKeys {
|
|
materialized = materialized && matchingKey.Materialized
|
|
fieldContextsSeen[matchingKey.FieldContext] = true
|
|
dataTypesSeen[matchingKey.FieldDataType] = true
|
|
indexes = append(indexes, matchingKey.Indexes...)
|
|
}
|
|
for _, matchingKey := range contextPrefixedMatchingKeys {
|
|
materialized = materialized && matchingKey.Materialized
|
|
fieldContextsSeen[matchingKey.FieldContext] = true
|
|
dataTypesSeen[matchingKey.FieldDataType] = true
|
|
indexes = append(indexes, matchingKey.Indexes...)
|
|
}
|
|
key.Materialized = materialized
|
|
if len(indexes) > 0 {
|
|
key.Indexes = indexes
|
|
}
|
|
|
|
if len(fieldContextsSeen) == 1 && key.FieldContext == telemetrytypes.FieldContextUnspecified {
|
|
// all matching keys have same field context, use it
|
|
for context := range fieldContextsSeen {
|
|
actions = append(actions, fmt.Sprintf("Adjusting key %s to have field context %s", key, context.StringValue()))
|
|
key.FieldContext = context
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(dataTypesSeen) == 1 && key.FieldDataType == telemetrytypes.FieldDataTypeUnspecified {
|
|
// all matching keys have same data type, use it
|
|
for dt := range dataTypesSeen {
|
|
actions = append(actions, fmt.Sprintf("Adjusting key %s to have data type %s", key, dt.StringValue()))
|
|
key.FieldDataType = dt
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return actions
|
|
}
|
|
|
|
func AdjustKeysForAliasExpressions[T any](query *qbtypes.QueryBuilderQuery[T], requestType qbtypes.RequestType) []string {
|
|
/*
|
|
For example, if user is using `body.count` as an alias for aggregation and
|
|
Uses it in orderBy, upstream code will convert it to just `count` with fieldContext as Body
|
|
But we need to adjust it back to `body.count` with fieldContext as unspecified
|
|
*/
|
|
actions := []string{}
|
|
if requestType != qbtypes.RequestTypeRaw && requestType != qbtypes.RequestTypeRawStream {
|
|
aliasExpressions := map[string]bool{}
|
|
for _, agg := range query.Aggregations {
|
|
switch v := any(agg).(type) {
|
|
case qbtypes.LogAggregation:
|
|
if v.Alias != "" {
|
|
aliasExpressions[v.Alias] = true
|
|
}
|
|
case qbtypes.TraceAggregation:
|
|
if v.Alias != "" {
|
|
aliasExpressions[v.Alias] = true
|
|
}
|
|
default:
|
|
continue
|
|
}
|
|
}
|
|
if len(aliasExpressions) > 0 {
|
|
for idx := range query.Order {
|
|
contextPrefixedKeyName := fmt.Sprintf("%s.%s", query.Order[idx].Key.FieldContext.StringValue(), query.Order[idx].Key.Name)
|
|
if aliasExpressions[contextPrefixedKeyName] {
|
|
actions = append(actions, fmt.Sprintf("Adjusting OrderBy key %s to %s", query.Order[idx].Key, contextPrefixedKeyName))
|
|
query.Order[idx].Key.FieldContext = telemetrytypes.FieldContextUnspecified
|
|
query.Order[idx].Key.Name = contextPrefixedKeyName
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return actions
|
|
}
|