Compare commits

...

2 Commits

Author SHA1 Message Date
srikanthccv
fdefce8150 feat(semconv): enable the rollout families and exact field resolution
The overlay enables the database, RPC, messaging, container, code, and
HTTP attribute families. `exact(key)` in a filter expression, or
`fieldResolution: exact` on a structured key, reads one stored spelling.
Product defaults and emitters use the current names; a migration renames
stored quick filters. The generator lints product code for old names.

Assisted-by: Claude Fable 5.1
2026-10-02 03:31:57 +05:30
srikanthccv
04021842f0 feat(semconv): add migration product surfaces
Product constants and defaults take the current semantic-convention
names. Span-metrics label keys keep the collector's spelling until the
metrics rename ships. The related-values table holds no metrics rows, so
the migration report covers traces and logs.

Assisted-by: Claude Fable 5.1
2026-10-02 02:34:45 +05:30
139 changed files with 4886 additions and 1295 deletions

View File

@@ -66,7 +66,7 @@ jobs:
uses: actions/setup-go@v5
with:
go-version: "1.24"
- name: check-semconv-generated-files
- name: check-semconv-generated-files-and-product-literals
run: make semconv-check
build:
if: |

View File

@@ -263,8 +263,8 @@ semconv-generate: ## Regenerate semantic-convention families for Go and TypeScri
@go run ./scripts/semconv
.PHONY: semconv-check
semconv-check: ## Fail if the generated semantic-convention files are stale
@go run ./scripts/semconv -check
semconv-check: ## Verify generated semantic-convention files and reject old-name product literals
@go run ./scripts/semconv -check -lint
.PHONY: gen-mocks
gen-mocks:

View File

@@ -7792,6 +7792,8 @@ components:
$ref: '#/components/schemas/TelemetrytypesFieldContext'
fieldDataType:
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
fieldResolution:
$ref: '#/components/schemas/TelemetrytypesFieldResolution'
meta:
properties:
unit:
@@ -7836,6 +7838,10 @@ components:
rowsScanned:
minimum: 0
type: integer
semconvResolutions:
items:
$ref: '#/components/schemas/Querybuildertypesv5SemconvResolution'
type: array
stepIntervals:
additionalProperties:
minimum: 0
@@ -7902,6 +7908,8 @@ components:
$ref: '#/components/schemas/TelemetrytypesFieldContext'
fieldDataType:
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
fieldResolution:
$ref: '#/components/schemas/TelemetrytypesFieldResolution'
name:
type: string
signal:
@@ -7989,6 +7997,8 @@ components:
$ref: '#/components/schemas/TelemetrytypesFieldContext'
fieldDataType:
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
fieldResolution:
$ref: '#/components/schemas/TelemetrytypesFieldResolution'
name:
type: string
signal:
@@ -8565,6 +8575,20 @@ components:
stepInterval:
$ref: '#/components/schemas/Querybuildertypesv5Step'
type: object
Querybuildertypesv5SemconvResolution:
properties:
current:
type: string
kind:
type: string
members:
items:
type: string
nullable: true
type: array
requested:
type: string
type: object
Querybuildertypesv5Step:
description: Step interval. Accepts a Go duration string (e.g., "60s", "1m",
"1h") or a number representing seconds (e.g., 60).
@@ -10347,6 +10371,11 @@ components:
- number
- ""
type: string
TelemetrytypesFieldResolution:
enum:
- exact
- ""
type: string
TelemetrytypesGettableFieldKeys:
properties:
complete:
@@ -10372,6 +10401,42 @@ components:
- values
- complete
type: object
TelemetrytypesGettableSemconvMigrationReport:
properties:
endUnixMilli:
format: int64
type: integer
entries:
items:
$ref: '#/components/schemas/TelemetrytypesSemconvMigrationReportEntry'
nullable: true
type: array
startUnixMilli:
format: int64
type: integer
required:
- entries
type: object
TelemetrytypesSemconvMigrationReportEntry:
properties:
current:
type: string
lastSeenUnixMilli:
format: int64
type: integer
old:
type: string
resourceSets:
minimum: 0
type: integer
services:
items:
type: string
nullable: true
type: array
signal:
type: string
type: object
TelemetrytypesSignal:
enum:
- traces
@@ -10392,6 +10457,8 @@ components:
$ref: '#/components/schemas/TelemetrytypesFieldContext'
fieldDataType:
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
fieldResolution:
$ref: '#/components/schemas/TelemetrytypesFieldResolution'
name:
type: string
signal:
@@ -12739,6 +12806,65 @@ paths:
tags:
- fields
x-signoz-stability: alpha
/api/v1/fields/semconv-migration:
get:
deprecated: false
description: Returns services that still emit old semantic-convention names
without the current family name
operationId: GetSemconvMigrationReport
parameters:
- in: query
name: startUnixMilli
schema:
format: int64
type: integer
- in: query
name: endUnixMilli
schema:
format: int64
type: integer
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/TelemetrytypesGettableSemconvMigrationReport'
status:
type: string
required:
- status
- data
type: object
description: OK
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- VIEWER
- tokenizer:
- VIEWER
summary: Get semantic-convention migration report
tags:
- fields
x-signoz-stability: alpha
/api/v1/fields/values:
get:
deprecated: false
@@ -21261,8 +21387,8 @@ paths:
alert: Payments-api error log rate above 1%
alertType: LOGS_BASED_ALERT
annotations:
description: Error log rate in {{$deployment.environment}} is
{{$value}}%
description: Error log rate in {{$deployment.environment.name}}
is {{$value}}%
summary: Payments-api error rate above {{$threshold}}%
condition:
compositeQuery:
@@ -21278,7 +21404,7 @@ paths:
groupBy:
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: logs
stepInterval: 60
@@ -21292,14 +21418,14 @@ paths:
groupBy:
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: B
signal: logs
stepInterval: 60
type: builder_query
- spec:
expression: (A / B) * 100
legend: '{{deployment.environment}}'
legend: '{{deployment.environment.name}}'
name: F1
type: builder_formula
queryType: builder
@@ -21325,7 +21451,7 @@ paths:
team: payments
notificationSettings:
groupBy:
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -21344,7 +21470,7 @@ paths:
alertType: LOGS_BASED_ALERT
annotations:
description: '{{$k8s.pod.name}} emitted {{$value}} panic log(s)
in {{$deployment.environment}}.'
in {{$deployment.environment.name}}.'
summary: Payments service panic
condition:
compositeQuery:
@@ -21362,8 +21488,8 @@ paths:
name: k8s.pod.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
name: deployment.environment.name
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
name: A
signal: logs
stepInterval: 60
@@ -21392,7 +21518,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -21462,8 +21588,9 @@ paths:
version: v5
metric_promql:
description: PromQL expression instead of the builder. Dotted OTEL
resource attributes are quoted ("deployment.environment"). Useful
for queries that combine series with group_right or other Prom operators.
resource attributes are quoted ("deployment.environment.name").
Useful for queries that combine series with group_right or other
Prom operators.
summary: Metric threshold PromQL rule
value:
alert: Kafka consumer group lag above 1000
@@ -21479,9 +21606,9 @@ paths:
- spec:
legend: '{{topic}}/{{partition}} ({{group}})'
name: A
query: (max by(topic, partition, "deployment.environment")(kafka_log_end_offset)
- on(topic, partition, "deployment.environment") group_right
max by(group, topic, partition, "deployment.environment")(kafka_consumer_committed_offset))
query: (max by(topic, partition, "deployment.environment.name")(kafka_log_end_offset)
- on(topic, partition, "deployment.environment.name")
group_right max by(group, topic, partition, "deployment.environment.name")(kafka_consumer_committed_offset))
> 0
type: promql
queryType: promql
@@ -21617,7 +21744,7 @@ paths:
alertType: METRIC_BASED_ALERT
annotations:
description: Pod {{$k8s.pod.name}} CPU is at {{$value}} of request
in {{$deployment.environment}}.
in {{$deployment.environment.name}}.
summary: Pod CPU above {{$threshold}} of request
condition:
compositeQuery:
@@ -21636,8 +21763,8 @@ paths:
name: k8s.pod.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
name: deployment.environment.name
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
name: A
signal: metrics
stepInterval: 60
@@ -21668,7 +21795,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -21692,7 +21819,7 @@ paths:
alert: API 5xx error rate above 1%
alertType: TRACES_BASED_ALERT
annotations:
description: '{{$service.name}} 5xx rate in {{$deployment.environment}}
description: '{{$service.name}} 5xx rate in {{$deployment.environment.name}}
is {{$value}}%.'
summary: API service error rate elevated
condition:
@@ -21704,7 +21831,7 @@ paths:
- expression: count()
disabled: true
filter:
expression: service.name CONTAINS 'api' AND http.status_code
expression: service.name CONTAINS 'api' AND http.response.status_code
>= 500
groupBy:
- fieldContext: resource
@@ -21712,7 +21839,7 @@ paths:
name: service.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: traces
stepInterval: 60
@@ -21729,14 +21856,14 @@ paths:
name: service.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: B
signal: traces
stepInterval: 60
type: builder_query
- spec:
expression: (A / B) * 100
legend: '{{service.name}} ({{deployment.environment}})'
legend: '{{service.name}} ({{deployment.environment.name}})'
name: F1
type: builder_formula
queryType: builder
@@ -21764,7 +21891,7 @@ paths:
notificationSettings:
groupBy:
- service.name
- deployment.environment
- deployment.environment.name
newGroupEvalDelay: 2m
renotify:
alertStates:
@@ -22213,8 +22340,8 @@ paths:
alert: Payments-api error log rate above 1%
alertType: LOGS_BASED_ALERT
annotations:
description: Error log rate in {{$deployment.environment}} is
{{$value}}%
description: Error log rate in {{$deployment.environment.name}}
is {{$value}}%
summary: Payments-api error rate above {{$threshold}}%
condition:
compositeQuery:
@@ -22230,7 +22357,7 @@ paths:
groupBy:
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: logs
stepInterval: 60
@@ -22244,14 +22371,14 @@ paths:
groupBy:
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: B
signal: logs
stepInterval: 60
type: builder_query
- spec:
expression: (A / B) * 100
legend: '{{deployment.environment}}'
legend: '{{deployment.environment.name}}'
name: F1
type: builder_formula
queryType: builder
@@ -22277,7 +22404,7 @@ paths:
team: payments
notificationSettings:
groupBy:
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -22296,7 +22423,7 @@ paths:
alertType: LOGS_BASED_ALERT
annotations:
description: '{{$k8s.pod.name}} emitted {{$value}} panic log(s)
in {{$deployment.environment}}.'
in {{$deployment.environment.name}}.'
summary: Payments service panic
condition:
compositeQuery:
@@ -22314,8 +22441,8 @@ paths:
name: k8s.pod.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
name: deployment.environment.name
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
name: A
signal: logs
stepInterval: 60
@@ -22344,7 +22471,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -22414,8 +22541,9 @@ paths:
version: v5
metric_promql:
description: PromQL expression instead of the builder. Dotted OTEL
resource attributes are quoted ("deployment.environment"). Useful
for queries that combine series with group_right or other Prom operators.
resource attributes are quoted ("deployment.environment.name").
Useful for queries that combine series with group_right or other
Prom operators.
summary: Metric threshold PromQL rule
value:
alert: Kafka consumer group lag above 1000
@@ -22431,9 +22559,9 @@ paths:
- spec:
legend: '{{topic}}/{{partition}} ({{group}})'
name: A
query: (max by(topic, partition, "deployment.environment")(kafka_log_end_offset)
- on(topic, partition, "deployment.environment") group_right
max by(group, topic, partition, "deployment.environment")(kafka_consumer_committed_offset))
query: (max by(topic, partition, "deployment.environment.name")(kafka_log_end_offset)
- on(topic, partition, "deployment.environment.name")
group_right max by(group, topic, partition, "deployment.environment.name")(kafka_consumer_committed_offset))
> 0
type: promql
queryType: promql
@@ -22569,7 +22697,7 @@ paths:
alertType: METRIC_BASED_ALERT
annotations:
description: Pod {{$k8s.pod.name}} CPU is at {{$value}} of request
in {{$deployment.environment}}.
in {{$deployment.environment.name}}.
summary: Pod CPU above {{$threshold}} of request
condition:
compositeQuery:
@@ -22588,8 +22716,8 @@ paths:
name: k8s.pod.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
name: deployment.environment.name
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
name: A
signal: metrics
stepInterval: 60
@@ -22620,7 +22748,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -22644,7 +22772,7 @@ paths:
alert: API 5xx error rate above 1%
alertType: TRACES_BASED_ALERT
annotations:
description: '{{$service.name}} 5xx rate in {{$deployment.environment}}
description: '{{$service.name}} 5xx rate in {{$deployment.environment.name}}
is {{$value}}%.'
summary: API service error rate elevated
condition:
@@ -22656,7 +22784,7 @@ paths:
- expression: count()
disabled: true
filter:
expression: service.name CONTAINS 'api' AND http.status_code
expression: service.name CONTAINS 'api' AND http.response.status_code
>= 500
groupBy:
- fieldContext: resource
@@ -22664,7 +22792,7 @@ paths:
name: service.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: traces
stepInterval: 60
@@ -22681,14 +22809,14 @@ paths:
name: service.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: B
signal: traces
stepInterval: 60
type: builder_query
- spec:
expression: (A / B) * 100
legend: '{{service.name}} ({{deployment.environment}})'
legend: '{{service.name}} ({{deployment.environment.name}})'
name: F1
type: builder_formula
queryType: builder
@@ -22716,7 +22844,7 @@ paths:
notificationSettings:
groupBy:
- service.name
- deployment.environment
- deployment.environment.name
newGroupEvalDelay: 2m
renotify:
alertStates:
@@ -23066,8 +23194,8 @@ paths:
alert: Payments-api error log rate above 1%
alertType: LOGS_BASED_ALERT
annotations:
description: Error log rate in {{$deployment.environment}} is
{{$value}}%
description: Error log rate in {{$deployment.environment.name}}
is {{$value}}%
summary: Payments-api error rate above {{$threshold}}%
condition:
compositeQuery:
@@ -23083,7 +23211,7 @@ paths:
groupBy:
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: logs
stepInterval: 60
@@ -23097,14 +23225,14 @@ paths:
groupBy:
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: B
signal: logs
stepInterval: 60
type: builder_query
- spec:
expression: (A / B) * 100
legend: '{{deployment.environment}}'
legend: '{{deployment.environment.name}}'
name: F1
type: builder_formula
queryType: builder
@@ -23130,7 +23258,7 @@ paths:
team: payments
notificationSettings:
groupBy:
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -23149,7 +23277,7 @@ paths:
alertType: LOGS_BASED_ALERT
annotations:
description: '{{$k8s.pod.name}} emitted {{$value}} panic log(s)
in {{$deployment.environment}}.'
in {{$deployment.environment.name}}.'
summary: Payments service panic
condition:
compositeQuery:
@@ -23167,8 +23295,8 @@ paths:
name: k8s.pod.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
name: deployment.environment.name
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
name: A
signal: logs
stepInterval: 60
@@ -23197,7 +23325,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -23267,8 +23395,9 @@ paths:
version: v5
metric_promql:
description: PromQL expression instead of the builder. Dotted OTEL
resource attributes are quoted ("deployment.environment"). Useful
for queries that combine series with group_right or other Prom operators.
resource attributes are quoted ("deployment.environment.name").
Useful for queries that combine series with group_right or other
Prom operators.
summary: Metric threshold PromQL rule
value:
alert: Kafka consumer group lag above 1000
@@ -23284,9 +23413,9 @@ paths:
- spec:
legend: '{{topic}}/{{partition}} ({{group}})'
name: A
query: (max by(topic, partition, "deployment.environment")(kafka_log_end_offset)
- on(topic, partition, "deployment.environment") group_right
max by(group, topic, partition, "deployment.environment")(kafka_consumer_committed_offset))
query: (max by(topic, partition, "deployment.environment.name")(kafka_log_end_offset)
- on(topic, partition, "deployment.environment.name")
group_right max by(group, topic, partition, "deployment.environment.name")(kafka_consumer_committed_offset))
> 0
type: promql
queryType: promql
@@ -23422,7 +23551,7 @@ paths:
alertType: METRIC_BASED_ALERT
annotations:
description: Pod {{$k8s.pod.name}} CPU is at {{$value}} of request
in {{$deployment.environment}}.
in {{$deployment.environment.name}}.
summary: Pod CPU above {{$threshold}} of request
condition:
compositeQuery:
@@ -23441,8 +23570,8 @@ paths:
name: k8s.pod.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
name: deployment.environment.name
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
name: A
signal: metrics
stepInterval: 60
@@ -23473,7 +23602,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -23497,7 +23626,7 @@ paths:
alert: API 5xx error rate above 1%
alertType: TRACES_BASED_ALERT
annotations:
description: '{{$service.name}} 5xx rate in {{$deployment.environment}}
description: '{{$service.name}} 5xx rate in {{$deployment.environment.name}}
is {{$value}}%.'
summary: API service error rate elevated
condition:
@@ -23509,7 +23638,7 @@ paths:
- expression: count()
disabled: true
filter:
expression: service.name CONTAINS 'api' AND http.status_code
expression: service.name CONTAINS 'api' AND http.response.status_code
>= 500
groupBy:
- fieldContext: resource
@@ -23517,7 +23646,7 @@ paths:
name: service.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: traces
stepInterval: 60
@@ -23534,14 +23663,14 @@ paths:
name: service.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: B
signal: traces
stepInterval: 60
type: builder_query
- spec:
expression: (A / B) * 100
legend: '{{service.name}} ({{deployment.environment}})'
legend: '{{service.name}} ({{deployment.environment.name}})'
name: F1
type: builder_formula
queryType: builder
@@ -23569,7 +23698,7 @@ paths:
notificationSettings:
groupBy:
- service.name
- deployment.environment
- deployment.environment.name
newGroupEvalDelay: 2m
renotify:
alertStates:
@@ -24428,8 +24557,8 @@ paths:
alert: Payments-api error log rate above 1%
alertType: LOGS_BASED_ALERT
annotations:
description: Error log rate in {{$deployment.environment}} is
{{$value}}%
description: Error log rate in {{$deployment.environment.name}}
is {{$value}}%
summary: Payments-api error rate above {{$threshold}}%
condition:
compositeQuery:
@@ -24445,7 +24574,7 @@ paths:
groupBy:
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: logs
stepInterval: 60
@@ -24459,14 +24588,14 @@ paths:
groupBy:
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: B
signal: logs
stepInterval: 60
type: builder_query
- spec:
expression: (A / B) * 100
legend: '{{deployment.environment}}'
legend: '{{deployment.environment.name}}'
name: F1
type: builder_formula
queryType: builder
@@ -24492,7 +24621,7 @@ paths:
team: payments
notificationSettings:
groupBy:
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -24511,7 +24640,7 @@ paths:
alertType: LOGS_BASED_ALERT
annotations:
description: '{{$k8s.pod.name}} emitted {{$value}} panic log(s)
in {{$deployment.environment}}.'
in {{$deployment.environment.name}}.'
summary: Payments service panic
condition:
compositeQuery:
@@ -24529,8 +24658,8 @@ paths:
name: k8s.pod.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
name: deployment.environment.name
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
name: A
signal: logs
stepInterval: 60
@@ -24559,7 +24688,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -24629,8 +24758,9 @@ paths:
version: v5
metric_promql:
description: PromQL expression instead of the builder. Dotted OTEL
resource attributes are quoted ("deployment.environment"). Useful
for queries that combine series with group_right or other Prom operators.
resource attributes are quoted ("deployment.environment.name").
Useful for queries that combine series with group_right or other
Prom operators.
summary: Metric threshold PromQL rule
value:
alert: Kafka consumer group lag above 1000
@@ -24646,9 +24776,9 @@ paths:
- spec:
legend: '{{topic}}/{{partition}} ({{group}})'
name: A
query: (max by(topic, partition, "deployment.environment")(kafka_log_end_offset)
- on(topic, partition, "deployment.environment") group_right
max by(group, topic, partition, "deployment.environment")(kafka_consumer_committed_offset))
query: (max by(topic, partition, "deployment.environment.name")(kafka_log_end_offset)
- on(topic, partition, "deployment.environment.name")
group_right max by(group, topic, partition, "deployment.environment.name")(kafka_consumer_committed_offset))
> 0
type: promql
queryType: promql
@@ -24784,7 +24914,7 @@ paths:
alertType: METRIC_BASED_ALERT
annotations:
description: Pod {{$k8s.pod.name}} CPU is at {{$value}} of request
in {{$deployment.environment}}.
in {{$deployment.environment.name}}.
summary: Pod CPU above {{$threshold}} of request
condition:
compositeQuery:
@@ -24803,8 +24933,8 @@ paths:
name: k8s.pod.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
name: deployment.environment.name
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
name: A
signal: metrics
stepInterval: 60
@@ -24835,7 +24965,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -24859,7 +24989,7 @@ paths:
alert: API 5xx error rate above 1%
alertType: TRACES_BASED_ALERT
annotations:
description: '{{$service.name}} 5xx rate in {{$deployment.environment}}
description: '{{$service.name}} 5xx rate in {{$deployment.environment.name}}
is {{$value}}%.'
summary: API service error rate elevated
condition:
@@ -24871,7 +25001,7 @@ paths:
- expression: count()
disabled: true
filter:
expression: service.name CONTAINS 'api' AND http.status_code
expression: service.name CONTAINS 'api' AND http.response.status_code
>= 500
groupBy:
- fieldContext: resource
@@ -24879,7 +25009,7 @@ paths:
name: service.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: traces
stepInterval: 60
@@ -24896,14 +25026,14 @@ paths:
name: service.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: B
signal: traces
stepInterval: 60
type: builder_query
- spec:
expression: (A / B) * 100
legend: '{{service.name}} ({{deployment.environment}})'
legend: '{{service.name}} ({{deployment.environment.name}})'
name: F1
type: builder_formula
queryType: builder
@@ -24931,7 +25061,7 @@ paths:
notificationSettings:
groupBy:
- service.name
- deployment.environment
- deployment.environment.name
newGroupEvalDelay: 2m
renotify:
alertStates:

View File

@@ -131,7 +131,7 @@ A `LogicalField` is one meaning: one name, context, and data type, backed by one
The resolution order is the same for every storage, in the filter and in every select field, group by, order by, and aggregation:
1. **Own context.** A key under one of the storage's own contexts (`span.x`, `log.x`) matches its own context first, then a column the storage knows under that context. Only when both miss does it look up as if it had no context. `span.http.method` then corrects to the attribute. Strict contexts (`resource.`, `attribute.`, `scope.`, `body.`) are kept as written.
2. **Matches.** The metadata keys under the key's spellings, grouped into families when the flag is on. Each combination of context and data type is one interpretation.
2. **Matches.** The metadata keys under the key's spellings, grouped into families when the flag is on. Each combination of context and data type is one interpretation. A key written as `exact(key)` in an expression, or a structured key with `fieldResolution: exact`, reads its own spelling alone.
3. **Ambiguity.** A filter settles several interpretations by resource over attribute, with a warning. A select field, group by, order by, or aggregation keeps every interpretation in metadata order and folds them, so it shows the value wherever it is.
4. **Intrinsic column first**, bare keys only. A column every row has leads the list. Metadata can report the column, or the storage's `Fallback` can. When only `Fallback` knows the column, its key carries the data type the column reads as (`querybuilder.ColumnDataType`). A same-named metadata key of a contradicting type then drops, and a time column merges none. When metadata reports the column too, every match reads. A metadata gap degrades to the correct column, never to a corrupt metadata key.
5. **Fallback.** With no match, the storage's fallback keys for the key. When the storage ignores unknown keys (a side query whose main query owns the error), the key is `Skipped`. Otherwise a key nothing can serve is an error with suggestions. The not-found warning fires only when every fallback key is a guess, that is, none of them is always present.

View File

@@ -0,0 +1,9 @@
import axios from 'api';
import { SemconvMigrationReport } from 'types/api/semconvMigration';
async function getSemconvMigrationReport(): Promise<SemconvMigrationReport> {
const response = await axios.get('/fields/semconv-migration');
return response.data.data;
}
export default getSemconvMigrationReport;

View File

@@ -64,12 +64,12 @@ export const COMMON_FILTERS = {
SERVER_SPANS: "kind_string = 'Server'",
CLIENT_SPANS: "kind_string = 'Client'",
INTERNAL_SPANS: "kind_string = 'Internal'",
ERROR_SPANS: 'http.status_code >= 400',
SUCCESS_SPANS: 'http.status_code < 400',
ERROR_SPANS: 'http.response.status_code >= 400',
SUCCESS_SPANS: 'http.response.status_code < 400',
// Common service filters
EXCLUDE_HEALTH_CHECKS: "http.route != '/health' AND http.route != '/ping'",
HTTP_REQUESTS: "http.method != ''",
HTTP_REQUESTS: "http.request.method != ''",
// Log filters
ERROR_LOGS: "severity_text = 'ERROR'",
@@ -87,7 +87,7 @@ export const COMMON_GROUP_BY_FIELDS = {
fieldContext: 'resource' as const,
},
HTTP_METHOD: {
name: 'http.method',
name: 'http.request.method',
fieldDataType: 'string' as const,
fieldContext: 'attribute' as const,
},
@@ -97,7 +97,7 @@ export const COMMON_GROUP_BY_FIELDS = {
fieldContext: 'attribute' as const,
},
HTTP_STATUS_CODE: {
name: 'http.status_code',
name: 'http.response.status_code',
fieldDataType: 'int64' as const,
fieldContext: 'attribute' as const,
},

View File

@@ -145,7 +145,7 @@ function CeleryOverviewConfigOptions(): JSX.Element {
{
placeholder: 'Destination',
queryParam: QueryParams.destination,
filterType: ['messaging.destination.name', 'messaging.destination'],
filterType: ['messaging.destination.name'],
},
{
placeholder: 'Kind',

View File

@@ -3,6 +3,7 @@ import { Badge, BadgeColor } from '@signozhq/ui/badge';
import { LogType } from 'components/Logs/LogStateIndicator/LogStateIndicator';
import { getLogIndicatorType } from 'components/Logs/LogStateIndicator/utils';
import { ILog } from 'types/api/logs/log';
import { getSemconvMembers } from 'utils/semconv';
import styles from './LogHighlights.module.scss';
import TraceIdField from './TraceIdField';
@@ -79,7 +80,9 @@ export const LOG_HIGHLIGHTS: LogHighlightConfig[] = [
key: 'environment',
label: 'ENVIRONMENT',
render: (log): ReactNode | null => {
const value = getAttr(log, 'deployment.environment');
const value = getSemconvMembers('deployment.environment.name')
.map((key) => getAttr(log, key))
.find(Boolean);
return value ? valueBadge(value) : null;
},
},

View File

@@ -1758,7 +1758,7 @@ QuerySearch.defaultProps = {
signalSource: '',
hardcodedAttributeKeys: undefined,
placeholder:
"Enter your filter query (e.g., http.status_code >= 500 AND service.name = 'frontend')",
"Enter your filter query (e.g., http.response.status_code >= 500 AND service.name = 'frontend')",
showFilterSuggestionsWithoutMetric: false,
initialExpression: undefined,
};

View File

@@ -995,6 +995,16 @@ describe('removeKeysFromExpression', () => {
expect(result).toBe("status = 'success'");
});
it('should remove a comparison that uses the exact field wrapper', () => {
const expression =
"exact(resource.deployment.environment) EXISTS AND service.name = 'api-gateway'";
const result = removeKeysFromExpression(expression, [
'resource.deployment.environment',
]);
expect(result).toBe("service.name = 'api-gateway'");
});
it('should remove multiple keys from expression', () => {
const expression =
"service.name = 'api-gateway' AND status = 'success' AND region = 'us-east-1'";

View File

@@ -687,7 +687,16 @@ export const removeKeysFromExpression = (
}
function visitComparison(ctx: ComparisonContext): string | null {
const keyText = ctx.key().getText().trim().toLowerCase();
const field = ctx.field();
// The runtime returns null for the inactive field alternative even though
// the generated TypeScript signature is non-nullable.
const exactCall = field.exactCall() as unknown as ReturnType<
typeof field.exactCall
> | null;
const keyText = (exactCall ? exactCall.key() : field.key())
.getText()
.trim()
.toLowerCase();
if (!keysSet.has(keyText)) {
return src(ctx);

View File

@@ -5,7 +5,7 @@ import { ArrowUpRight } from '@signozhq/icons';
const QUICK_FILTER_DOC_PATHS: Record<string, string> = {
severity_text: 'severity-text',
'deployment.environment': 'environment',
'deployment.environment.name': 'environment',
'service.name': 'service-name',
'host.name': 'hostname',
'k8s.cluster.name': 'k8s-cluster-name',

View File

@@ -0,0 +1,32 @@
import { Alert } from 'antd';
import { findOldSemconvNames } from 'utils/semconv';
interface SemconvEditorWarningProps {
value: unknown;
editor: string;
}
function SemconvEditorWarning({
value,
editor,
}: SemconvEditorWarningProps): JSX.Element | null {
const text = typeof value === 'string' ? value : JSON.stringify(value ?? '');
const renames = findOldSemconvNames(text);
if (renames.length === 0) {
return null;
}
return (
<Alert
type="warning"
showIcon
data-testid="semconv-editor-warning"
message={`${editor} contains renamed OpenTelemetry fields`}
description={renames
.map(({ old, current }) => `${old} → ${current}`)
.join(', ')}
/>
);
}
export default SemconvEditorWarning;

View File

@@ -0,0 +1,23 @@
import { Badge } from '@signozhq/ui/badge';
import { getSemconvRename } from 'utils/semconv';
interface SemconvOldNameBadgeProps {
name: string;
}
function SemconvOldNameBadge({
name,
}: SemconvOldNameBadgeProps): JSX.Element | null {
const rename = getSemconvRename(name);
if (!rename || rename.family.kind !== 'attribute') {
return null;
}
return (
<Badge color="amber" variant="outline" data-testid="semconv-old-name-badge">
old name, renamed to {rename.current}
</Badge>
);
}
export default SemconvOldNameBadge;

View File

@@ -0,0 +1,39 @@
import { render, screen } from '@testing-library/react';
import SemconvEditorWarning from '../SemconvEditorWarning';
import SemconvOldNameBadge from '../SemconvOldNameBadge';
describe('semantic convention product hints', () => {
it('badges an old raw attribute with its current name', () => {
render(<SemconvOldNameBadge name="deployment.environment" />);
expect(screen.getByTestId('semconv-old-name-badge')).toHaveTextContent(
'old name, renamed to deployment.environment.name',
);
});
it('does not badge a current raw attribute', () => {
render(<SemconvOldNameBadge name="deployment.environment.name" />);
expect(
screen.queryByTestId('semconv-old-name-badge'),
).not.toBeInTheDocument();
});
it('shows an informational editor warning without disabling the editor', () => {
render(
<>
<input aria-label="query" defaultValue="deployment.environment = 'prod'" />
<SemconvEditorWarning
value="deployment.environment = 'prod'"
editor="ClickHouse SQL"
/>
</>,
);
expect(screen.getByLabelText('query')).not.toBeDisabled();
expect(screen.getByTestId('semconv-editor-warning')).toHaveTextContent(
'deployment.environment → deployment.environment.name',
);
});
});

View File

@@ -0,0 +1,2 @@
export { default as SemconvEditorWarning } from './SemconvEditorWarning';
export { default as SemconvOldNameBadge } from './SemconvOldNameBadge';

View File

@@ -19,6 +19,51 @@ export type SemconvFamily = {
};
export const SEMCONV_FAMILIES: readonly SemconvFamily[] = [
{
current: 'code.file.path',
kind: 'attribute',
members: [
{
name: 'code.filepath',
contexts: [],
signals: [],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'code.function.name',
kind: 'attribute',
members: [
{
name: 'code.function',
contexts: [],
signals: [],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'code.line.number',
kind: 'attribute',
members: [
{
name: 'code.lineno',
contexts: [],
signals: [],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'container.cpu.usage',
kind: 'metric',
@@ -34,6 +79,84 @@ export const SEMCONV_FAMILIES: readonly SemconvFamily[] = [
signals: [],
valueMap: {},
},
{
current: 'container.runtime.name',
kind: 'attribute',
members: [
{
name: 'container.runtime',
contexts: [],
signals: [],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'db.namespace',
kind: 'attribute',
members: [
{
name: 'db.elasticsearch.cluster.name',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
},
{
name: 'db.name',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
},
{
name: 'db.cassandra.keyspace',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
},
{
name: 'db.hbase.namespace',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'db.operation.name',
kind: 'attribute',
members: [
{
name: 'db.operation',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'db.query.text',
kind: 'attribute',
members: [
{
name: 'db.statement',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'deployment.environment.name',
kind: 'attribute',
@@ -49,6 +172,36 @@ export const SEMCONV_FAMILIES: readonly SemconvFamily[] = [
signals: ['logs', 'metrics', 'traces'],
valueMap: {},
},
{
current: 'http.request.method',
kind: 'attribute',
members: [
{
name: 'http.method',
contexts: ['attribute'],
signals: ['logs', 'traces'],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'http.response.status_code',
kind: 'attribute',
members: [
{
name: 'http.status_code',
contexts: ['attribute'],
signals: ['logs', 'traces'],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'k8s.node.cpu.usage',
kind: 'metric',
@@ -79,4 +232,175 @@ export const SEMCONV_FAMILIES: readonly SemconvFamily[] = [
signals: [],
valueMap: {},
},
{
current: 'messaging.client.id',
kind: 'attribute',
members: [
{
name: 'messaging.client_id',
contexts: ['attribute'],
signals: ['metrics', 'traces'],
applyToMetrics: [],
},
{
name: 'messaging.kafka.client_id',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
},
{
name: 'messaging.rocketmq.client_id',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'messaging.consumer.group.name',
kind: 'attribute',
members: [
{
name: 'messaging.eventhubs.consumer.group',
contexts: [],
signals: [],
applyToMetrics: [],
},
{
name: 'messaging.kafka.consumer.group',
contexts: [],
signals: [],
applyToMetrics: [],
},
{
name: 'messaging.rocketmq.client_group',
contexts: [],
signals: [],
applyToMetrics: [],
},
{
name: 'messaging.kafka.consumer_group',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'messaging.destination.name',
kind: 'attribute',
members: [
{
name: 'messaging.destination',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'messaging.operation.type',
kind: 'attribute',
members: [
{
name: 'messaging.operation',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'rpc.system.name',
kind: 'attribute',
members: [
{
name: 'rpc.system',
contexts: [],
signals: [],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'service.peer.name',
kind: 'attribute',
members: [
{
name: 'peer.service',
contexts: [],
signals: [],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'url.full',
kind: 'attribute',
members: [
{
name: 'http.url',
contexts: ['attribute'],
signals: ['logs', 'traces'],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'url.scheme',
kind: 'attribute',
members: [
{
name: 'http.scheme',
contexts: ['attribute'],
signals: ['logs', 'traces'],
applyToMetrics: [],
},
],
contexts: [],
signals: [],
valueMap: {},
},
{
current: 'user_agent.original',
kind: 'attribute',
members: [
{
name: 'browser.user_agent',
contexts: ['resource'],
signals: [],
applyToMetrics: [],
},
{
name: 'http.user_agent',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
},
],
contexts: ['attribute'],
signals: ['logs', 'traces'],
valueMap: {},
},
] as const;

View File

@@ -10,7 +10,7 @@ export const AllTraceFilterKeyValue: Record<string, string> = {
durationNano: 'Duration',
duration_nano: 'Duration',
durationNanoMax: 'Duration',
'deployment.environment': 'Environment',
'deployment.environment.name': 'Environment',
hasError: 'Status',
has_error: 'Status',
serviceName: 'Service Name',
@@ -55,11 +55,11 @@ export const traceFilterKeys: Record<AllTraceFilterKeys, BaseAutocompleteData> =
id: 'serviceName--string--tag--true',
},
'deployment.environment': {
key: 'deployment.environment',
'deployment.environment.name': {
key: 'deployment.environment.name',
dataType: DataTypes.String,
type: 'resource',
id: 'deployment.environment--string--resource--false',
id: 'deployment.environment.name--string--resource--false',
},
name: {
key: 'name',

View File

@@ -155,7 +155,7 @@ function DomainList(): JSX.Element {
dataSource={DataSource.TRACES}
queryData={query}
onChange={handleSearchChange}
placeholder="Enter your filter query (e.g., deployment.environment = 'otel-demo' AND service.name = 'frontend')"
placeholder="Enter your filter query (e.g., deployment.environment.name = 'otel-demo' AND service.name = 'frontend')"
hardcodedAttributeKeys={ApiMonitoringHardcodedAttributeKeys}
/>
</div>
@@ -180,9 +180,8 @@ function DomainList(): JSX.Element {
</div>
<div className="no-domain-subtitle">
Ensure all HTTP client spans are being sent with kind as{' '}
<span className="attribute">Client</span> and url set in{' '}
<span className="attribute">url.full</span> or{' '}
<span className="attribute">http.url</span> attribute.
<span className="attribute">Client</span> and the URL set in the{' '}
<span className="attribute">url.full</span> attribute.
</div>
<a
href={DOCLINKS.EXTERNAL_API_MONITORING}

View File

@@ -6,9 +6,9 @@ import { SPAN_ATTRIBUTES } from './Explorer/Domains/DomainDetails/constants';
export const ApiMonitoringHardcodedAttributeKeys: QueryKeyDataSuggestionsProps[] =
[
{
label: 'deployment.environment',
label: 'deployment.environment.name',
type: 'resource',
name: 'deployment.environment',
name: 'deployment.environment.name',
signal: 'traces',
fieldDataType: QUERY_BUILDER_KEY_TYPES.STRING,
},

View File

@@ -87,7 +87,7 @@ export const ApiMonitoringQuickFiltersConfig: IQuickFiltersConfig[] = [
title: 'Environment',
attributeKey: {
key: 'deployment.environment',
key: 'deployment.environment.name',
dataType: DataTypes.String,
type: 'resource',
},

View File

@@ -11,6 +11,7 @@ import {
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { FormatTimezoneAdjustedTimestamp } from 'hooks/useTimezoneFormatter/useTimezoneFormatter';
import { getSemconvMembers } from 'utils/semconv';
import styles from './traceListColumns.module.scss';
const keyToLabelMap: Record<string, string> = {
@@ -27,7 +28,12 @@ const keyToLabelMap: Record<string, string> = {
const keyAliases: Record<string, string[]> = {
serviceName: ['serviceName', 'service.name', 'service_name'],
durationNano: ['durationNano', 'duration.nano', 'duration_nano'],
httpMethod: ['httpMethod', 'http.method', 'http_method'],
httpMethod: [
'httpMethod',
...getSemconvMembers('http.request.method'),
'http_request_method',
'http_method',
],
responseStatusCode: [
'response_status_code',
'response.status.code',

View File

@@ -128,7 +128,7 @@ export const INFRA_MONITORING_ATTR_KEYS = {
K8S_OBJECT_NAME: 'k8s.object.name',
// Environment
DEPLOYMENT_ENVIRONMENT: 'deployment.environment',
DEPLOYMENT_ENVIRONMENT: 'deployment.environment.name',
// Host System
OS_TYPE: 'os.type',
@@ -907,7 +907,7 @@ export const ENTITY_FILTER_PLACEHOLDERS: Record<InfraMonitoringEntity, string> =
[InfraMonitoringEntity.NAMESPACES]:
"Enter your filter query (e.g., k8s.namespace.name = 'production' AND k8s.cluster.name = 'prod-cluster')",
[InfraMonitoringEntity.CLUSTERS]:
"Enter your filter query (e.g., k8s.cluster.name = 'prod-cluster' AND deployment.environment = 'production')",
"Enter your filter query (e.g., k8s.cluster.name = 'prod-cluster' AND deployment.environment.name = 'production')",
[InfraMonitoringEntity.DEPLOYMENTS]:
"Enter your filter query (e.g., k8s.deployment.name = 'api-server' AND k8s.namespace.name = 'production')",
[InfraMonitoringEntity.STATEFULSETS]:

View File

@@ -2,6 +2,17 @@
color: white;
}
.semconv-migration-report {
margin-top: 32px;
display: flex;
flex-direction: column;
gap: 12px;
.ant-table-wrapper {
margin-top: 4px;
}
}
.ingestion-key-container {
margin-top: 24px;
display: flex;

View File

@@ -5,6 +5,8 @@ import getIngestionData from 'api/settings/getIngestionData';
import { useAppContext } from 'providers/App/App';
import { IngestionDataType } from 'types/api/settings/ingestion';
import SemconvMigrationReport from './SemconvMigrationReport';
import './IngestionSettings.styles.scss';
export default function IngestionSettings(): JSX.Element {
@@ -84,6 +86,7 @@ export default function IngestionSettings(): JSX.Element {
dataSource={data}
bordered
/>
<SemconvMigrationReport />
</div>
);
}

View File

@@ -83,6 +83,8 @@ import { MeterAggregateOperator } from 'types/common/queryBuilder';
import { USER_ROLES } from 'types/roles';
import { getDaysUntilExpiry } from 'utils/timeUtils';
import SemconvMigrationReport from './SemconvMigrationReport';
import './IngestionSettings.styles.scss';
const { Option } = Select;
@@ -1705,6 +1707,7 @@ function MultiIngestionSettings(): JSX.Element {
}}
className="ingestion-keys-table"
/>
<SemconvMigrationReport />
</div>
{/* Delete Key Modal */}

View File

@@ -0,0 +1,72 @@
import { useQuery } from 'react-query';
import { Alert, Table, TableColumnsType } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import getSemconvMigrationReport from 'api/semconv/getMigrationReport';
import dayjs from 'dayjs';
import { SemconvMigrationReportEntry } from 'types/api/semconvMigration';
function SemconvMigrationReport(): JSX.Element {
const { data, isLoading, isError } = useQuery({
queryKey: ['semconv-migration-report'],
queryFn: getSemconvMigrationReport,
});
const columns: TableColumnsType<SemconvMigrationReportEntry> = [
{
title: 'Old name',
dataIndex: 'old',
key: 'old',
},
{
title: 'Current name',
dataIndex: 'current',
key: 'current',
},
{
title: 'Signal',
dataIndex: 'signal',
key: 'signal',
},
{
title: 'Services still sending only the old name',
dataIndex: 'services',
key: 'services',
render: (services: string[]): string => services.join(', '),
},
{
title: 'Last seen',
dataIndex: 'lastSeenUnixMilli',
key: 'lastSeenUnixMilli',
render: (value: number): string =>
dayjs(value).format('YYYY-MM-DD HH:mm:ss'),
},
];
return (
<section className="semconv-migration-report">
<Typography.Title level={4}>Semantic convention migration</Typography.Title>
<Typography.Text>
Services in this report sent an old OpenTelemetry field during the last 24
hours without sending its current replacement. Update their SDK or
instrumentation when practical; SigNoz queries remain backward compatible.
</Typography.Text>
{isError && (
<Alert
type="error"
showIcon
message="Could not load the semantic convention migration report"
/>
)}
<Table
loading={isLoading}
columns={columns}
dataSource={data?.entries ?? []}
rowKey={(entry): string => `${entry.current}-${entry.old}-${entry.signal}`}
pagination={false}
locale={{ emptyText: 'No old-only services found in the last 24 hours' }}
/>
</section>
);
}
export default SemconvMigrationReport;

View File

@@ -18,7 +18,7 @@ export const SAMPLE_SPAN_JSON = `{
},
"resource": {
"service.name": "llm-gateway",
"deployment.environment": "production"
"deployment.environment.name": "production"
}
}`;

View File

@@ -1,3 +1,5 @@
import { getSemconvMembers } from 'utils/semconv';
// Field-name allowlists that drive signal-specific cell rendering. Both legacy
// camelCase and snake_case variants are listed because the API has shipped both.
@@ -12,12 +14,10 @@ export const TIMESTAMP_FIELD_NAMES = new Set([
export const STATUS_FIELD_NAMES = new Set([
'httpMethod',
'http_method',
'http.method',
'http.request.method',
...getSemconvMembers('http.request.method'),
'responseStatusCode',
'response_status_code',
'http.status_code',
'http.response.status_code',
...getSemconvMembers('http.response.status_code'),
]);
// trace_/max_llm_duration_nano are trace-level durations the per-trace query computes.

View File

@@ -74,7 +74,8 @@ export function tracesRunQueryAction(
properties: {
key: {
type: 'string',
description: 'Attribute key, e.g. service.name, http.status_code',
description:
'Attribute key, e.g. service.name, http.response.status_code',
},
op: {
type: 'string',
@@ -143,7 +144,7 @@ export function tracesAddFilterAction(
properties: {
key: {
type: 'string',
description: 'Attribute key, e.g. service.name, http.status_code',
description: 'Attribute key, e.g. service.name, http.response.status_code',
},
op: {
type: 'string',

View File

@@ -1,6 +1,7 @@
import { Divider } from '@signozhq/ui/divider';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Typography } from '@signozhq/ui/typography';
import { SemconvOldNameBadge } from 'components/Semconv';
import { TagContainer, TagLabel, TagValue } from './FieldRenderer.styles';
import { FieldRendererProps } from './LogDetailedView.types';
@@ -28,6 +29,7 @@ function FieldRenderer({ field }: FieldRendererProps): JSX.Element {
<Typography.Text truncate={1} className="label">
{newField}{' '}
</Typography.Text>
<SemconvOldNameBadge name={newField} />
</TooltipSimple>
<div className="tags">
@@ -47,7 +49,10 @@ function FieldRenderer({ field }: FieldRendererProps): JSX.Element {
</div>
</>
) : (
<span className="label">{field}</span>
<>
<span className="label">{field}</span>
<SemconvOldNameBadge name={field} />
</>
)}
</span>
);

View File

@@ -164,7 +164,9 @@ describe('useInitialQuery - Priority-Based Resource Filtering', () => {
value: 'frontend-service',
}),
expect.objectContaining({
key: expect.objectContaining({ key: 'deployment.environment' }),
key: expect.objectContaining({
key: 'deployment.environment.name',
}),
value: 'production',
}),
expect.objectContaining({
@@ -286,7 +288,9 @@ describe('useInitialQuery - Priority-Based Resource Filtering', () => {
value: 'legacy-app',
}),
expect.objectContaining({
key: expect.objectContaining({ key: 'deployment.environment' }),
key: expect.objectContaining({
key: 'deployment.environment.name',
}),
value: 'production',
}),
expect.objectContaining({

View File

@@ -6,13 +6,14 @@ import {
TagFilterItem,
} from 'types/api/queryBuilder/queryBuilderData';
import { v4 as uuid } from 'uuid';
import { getSemconvRename } from 'utils/semconv';
const FALLBACK_STARTS_WITH_REGEX = /^(k8s|cloud|host|deployment)/; // regex to filter out resources that start with the specified keywords
const FALLBACK_CONTAINS_REGEX = /(env|service|file|container|tenant)/; // regex to filter out resources that contains the specified keywords
// Priority categories for filter selection
// Strategy:
// - Always include: service.name, deployment.environment, env, environment
// - Always include: service.name, deployment.environment.name, env, environment
// - Select ONE category only: stops at the first category with a matching attribute
// - Within category: picks the first available attribute by order
// - Order (highest to lowest priority): Kubernetes > Cloud > Host > Container
@@ -26,27 +27,36 @@ const PRIORITY_CATEGORIES = [
const SERVICE_AND_ENVIRONMENT_KEYS = [
'service.name',
'deployment.environment',
'deployment.environment.name',
'env',
'environment',
];
export const getFiltersFromResources = (
resources: ILog['resources_string'],
): TagFilterItem[] =>
Object.keys(resources).map((key: string) => {
): TagFilterItem[] => {
const items = new Map<string, TagFilterItem>();
Object.keys(resources).forEach((key: string) => {
const currentKey = getSemconvRename(key)?.current ?? key;
const resourceValue = resources[key] as string;
return {
const item = {
id: uuid(),
key: {
key,
key: currentKey,
dataType: DataTypes.String,
type: 'resource',
},
op: OPERATORS['='],
value: resourceValue,
};
// If raw data contains both names, retain the current value just like the
// backend's current-first resolver.
if (!items.has(currentKey) || key === currentKey) {
items.set(currentKey, item);
}
});
return Array.from(items.values());
};
export const isServiceOrEnvironmentAttribute = (key: string): boolean =>
SERVICE_AND_ENVIRONMENT_KEYS.includes(key);

View File

@@ -28,7 +28,7 @@ import { v4 as uuid } from 'uuid';
export const dbSystemTags: Tags[] = [
{
Key: 'db.system.(string)',
Key: 'db.system.name.(string)',
StringValues: [''],
NumberValues: [],
BoolValues: [],

View File

@@ -1,6 +1,7 @@
import { useTranslation } from 'react-i18next';
import { Form } from 'antd';
import { initialQueryBuilderFormValuesMap } from 'constants/queryBuilder';
import { SemconvEditorWarning } from 'components/Semconv';
import QueryBuilderSearchV2 from 'container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2';
import isEqual from 'lodash-es/isEqual';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
@@ -55,6 +56,7 @@ function TagFilterInputWithLogsResultPreview({
value={value}
onChange={onChange}
/>
<SemconvEditorWarning value={value} editor="Pipeline filter" />
<div className="pipeline-filter-input-preview-container">
<LogsFilterPreview filter={value} />
</div>

View File

@@ -2,6 +2,7 @@ import { ChangeEvent, useCallback } from 'react';
import MEditor, { Monaco } from '@monaco-editor/react';
import { Color } from '@signozhq/design-tokens';
import { Input } from 'antd';
import { SemconvEditorWarning } from 'components/Semconv';
import { LEGEND } from 'constants/global';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useIsDarkMode } from 'hooks/useDarkMode';
@@ -118,6 +119,7 @@ function ClickHouseQueryBuilder({
theme={isDarkMode ? 'my-theme' : 'light'}
beforeMount={setEditorTheme}
/>
<SemconvEditorWarning value={queryData?.query} editor="ClickHouse SQL" />
<Input
onChange={handleUpdateInput}
name="legend"

View File

@@ -1,6 +1,7 @@
import { ChangeEvent, useCallback } from 'react';
import { Input } from 'antd';
import { LEGEND } from 'constants/global';
import { SemconvEditorWarning } from 'components/Semconv';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { IPromQLQuery } from 'types/api/queryBuilder/queryBuilderData';
import { EQueryType } from 'types/common/dashboard';
@@ -66,6 +67,7 @@ function PromQLQueryBuilder({
style={{ marginBottom: '0.5rem' }}
data-testid="promql-query-input"
/>
<SemconvEditorWarning value={queryData?.query} editor="PromQL" />
<Input
onChange={handleUpdateQuery}

View File

@@ -1,3 +1,5 @@
import { getSemconvMembers } from 'utils/semconv';
// Field-name allowlists that drive signal-specific cell rendering. Both legacy
// camelCase and snake_case variants are listed because the API has shipped both.
export const TIMESTAMP_FIELD_NAMES = new Set(['timestamp']);
@@ -5,12 +7,10 @@ export const TIMESTAMP_FIELD_NAMES = new Set(['timestamp']);
export const STATUS_FIELD_NAMES = new Set([
'httpMethod',
'http_method',
'http.method',
'http.request.method',
...getSemconvMembers('http.request.method'),
'responseStatusCode',
'response_status_code',
'http.status_code',
'http.response.status_code',
...getSemconvMembers('http.response.status_code'),
]);
export const DURATION_FIELD_NAMES = new Set(['durationNano', 'duration_nano']);

View File

@@ -35,13 +35,7 @@ export default function CeleryOverviewDetails({
? undefined
: getFiltersFromKeyValue('messaging.system', value, 'tag');
case 'destination':
return getFiltersFromKeyValue(
details.messaging_system === 'celery'
? 'messaging.destination'
: 'messaging.destination.name',
value,
'tag',
);
return getFiltersFromKeyValue('messaging.destination.name', value, 'tag');
case 'kind_string':
return getFiltersFromKeyValue('kind_string', value, '');
default:

View File

@@ -233,7 +233,7 @@ describe('Logs Explorer Tests', () => {
);
const queries = queryAllByText(
"Enter your filter query (e.g., http.status_code >= 500 AND service.name = 'frontend')",
"Enter your filter query (e.g., http.response.status_code >= 500 AND service.name = 'frontend')",
);
expect(queries).toHaveLength(1);
});

View File

@@ -40,7 +40,7 @@ export const LogsQuickFiltersConfig: IQuickFiltersConfig[] = [
type: FiltersType.CHECKBOX,
title: 'Environment',
attributeKey: {
key: 'deployment.environment',
key: 'deployment.environment.name',
dataType: DataTypes.String,
type: 'resource',
},

View File

@@ -11,6 +11,7 @@ import { Skeleton } from 'antd';
import { DetailsHeader, DetailsPanelDrawer } from 'components/DetailsPanel';
import { HeaderAction } from 'components/DetailsPanel/DetailsHeader/DetailsHeader';
import { DetailsPanelState } from 'components/DetailsPanel/types';
import { SemconvOldNameBadge } from 'components/Semconv';
import { QueryParams } from 'constants/query';
import {
initialQueryBuilderFormValuesMap,
@@ -108,6 +109,12 @@ function SpanDetailsContent({
() => getSpanDisplayData(selectedSpan),
[selectedSpan],
);
const semconvLabelSuffix = useCallback(
(fieldKey: string): React.ReactNode => (
<SemconvOldNameBadge name={fieldKey} />
),
[],
);
// Map span attribute actions to PrettyView actions format.
// Use the last key in fieldKeyPath (the actual attribute key), not the full display path.
@@ -329,6 +336,7 @@ function SpanDetailsContent({
visibleActions: VISIBLE_ACTIONS,
pinnedFieldsValue,
onPinnedFieldsChange,
labelSuffixRenderer: semconvLabelSuffix,
}}
/>
</TabsContent>

View File

@@ -29,7 +29,7 @@ export const KEY_ATTRIBUTE_KEYS: Record<string, string[]> = {
traces: [
'service.name',
'service.namespace',
'deployment.environment',
'deployment.environment.name',
'timestamp',
'duration_nano',
'kind_string',

View File

@@ -359,7 +359,7 @@ function Filters({
onChange={handleExpressionChange}
onRun={handleRunQuery}
dataSource={DataSource.TRACES}
placeholder="Enter your filter query (e.g., http.status_code >= 500 AND service.name = 'frontend')"
placeholder="Enter your filter query (e.g., http.response.status_code >= 500 AND service.name = 'frontend')"
/>
</div>
</div>

View File

@@ -22,8 +22,8 @@ export const SPAN_CATEGORIES: readonly SpanCategory[] = [
// Map each category to the attribute key it filters on
const CATEGORY_KEYS: Record<Exclude<SpanCategory, 'All'>, string> = {
Database: 'db.system',
HTTP: 'http.method',
Database: 'db.system.name',
HTTP: 'http.request.method',
Functions: 'kind_string',
Jobs: 'messaging.system',
LLM: 'gen_ai.request.model',
@@ -34,8 +34,8 @@ const ALL_CATEGORY_KEYS = Object.values(CATEGORY_KEYS);
// The expression clause to add for each category
const CATEGORY_EXPRESSIONS: Record<Exclude<SpanCategory, 'All'>, string> = {
Database: 'db.system exists',
HTTP: 'http.method exists',
Database: 'db.system.name exists',
HTTP: 'http.request.method exists',
Functions: "kind_string = 'Internal'",
Jobs: 'messaging.system exists',
LLM: 'gen_ai.request.model exists',

View File

@@ -74,7 +74,8 @@ export function tracesRunQueryAction(
properties: {
key: {
type: 'string',
description: 'Attribute key, e.g. service.name, http.status_code',
description:
'Attribute key, e.g. service.name, http.response.status_code',
},
op: {
type: 'string',
@@ -143,7 +144,7 @@ export function tracesAddFilterAction(
properties: {
key: {
type: 'string',
description: 'Attribute key, e.g. service.name, http.status_code',
description: 'Attribute key, e.g. service.name, http.response.status_code',
},
op: {
type: 'string',

File diff suppressed because one or more lines are too long

View File

@@ -25,12 +25,13 @@ HAS=24
HASANY=25
HASALL=26
SEARCH=27
BOOL=28
NUMBER=29
QUOTED_TEXT=30
KEY=31
WS=32
FREETEXT=33
EXACT=28
BOOL=29
NUMBER=30
QUOTED_TEXT=31
KEY=32
WS=33
FREETEXT=34
'('=1
')'=2
'['=3

File diff suppressed because one or more lines are too long

View File

@@ -25,12 +25,13 @@ HAS=24
HASANY=25
HASALL=26
SEARCH=27
BOOL=28
NUMBER=29
QUOTED_TEXT=30
KEY=31
WS=32
FREETEXT=33
EXACT=28
BOOL=29
NUMBER=30
QUOTED_TEXT=31
KEY=32
WS=33
FREETEXT=34
'('=1
')'=2
'['=3

View File

@@ -39,12 +39,13 @@ export default class FilterQueryLexer extends Lexer {
public static readonly HASANY = 25;
public static readonly HASALL = 26;
public static readonly SEARCH = 27;
public static readonly BOOL = 28;
public static readonly NUMBER = 29;
public static readonly QUOTED_TEXT = 30;
public static readonly KEY = 31;
public static readonly WS = 32;
public static readonly FREETEXT = 33;
public static readonly EXACT = 28;
public static readonly BOOL = 29;
public static readonly NUMBER = 30;
public static readonly QUOTED_TEXT = 31;
public static readonly KEY = 32;
public static readonly WS = 33;
public static readonly FREETEXT = 34;
public static readonly EOF = Token.EOF;
public static readonly channelNames: string[] = [ "DEFAULT_TOKEN_CHANNEL", "HIDDEN" ];
@@ -70,8 +71,8 @@ export default class FilterQueryLexer extends Lexer {
"HASTOKEN",
"HAS", "HASANY",
"HASALL", "SEARCH",
"BOOL", "NUMBER",
"QUOTED_TEXT",
"EXACT", "BOOL",
"NUMBER", "QUOTED_TEXT",
"KEY", "WS",
"FREETEXT" ];
public static readonly modeNames: string[] = [ "DEFAULT_MODE", ];
@@ -80,8 +81,8 @@ export default class FilterQueryLexer extends Lexer {
"LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS",
"NEQ", "LT", "LE", "GT", "GE", "LIKE", "ILIKE", "BETWEEN", "EXISTS", "REGEXP",
"CONTAINS", "IN", "NOT", "AND", "OR", "HASTOKEN", "HAS", "HASANY", "HASALL",
"SEARCH", "BOOL", "SIGN", "NUMBER", "QUOTED_TEXT", "SEGMENT", "EMPTY_BRACKS",
"OLD_JSON_BRACKS", "KEY", "WS", "DIGIT", "FREETEXT",
"SEARCH", "EXACT", "BOOL", "SIGN", "NUMBER", "QUOTED_TEXT", "SEGMENT",
"EMPTY_BRACKS", "OLD_JSON_BRACKS", "KEY", "WS", "DIGIT", "FREETEXT",
];
@@ -102,122 +103,124 @@ export default class FilterQueryLexer extends Lexer {
public get modeNames(): string[] { return FilterQueryLexer.modeNames; }
public static readonly _serializedATN: number[] = [4,0,33,329,6,-1,2,0,
public static readonly _serializedATN: number[] = [4,0,34,337,6,-1,2,0,
7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,
7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,
16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,
2,24,7,24,2,25,7,25,2,26,7,26,2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2,
31,7,31,2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,1,0,
1,0,1,1,1,1,1,2,1,2,1,3,1,3,1,4,1,4,1,5,1,5,1,5,3,5,91,8,5,1,6,1,6,1,6,
1,7,1,7,1,7,1,8,1,8,1,9,1,9,1,9,1,10,1,10,1,11,1,11,1,11,1,12,1,12,1,12,
1,12,1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14,1,14,1,
14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,3,15,134,8,15,1,16,1,16,1,16,1,16,
1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,3,17,151,8,17,1,
18,1,18,1,18,1,19,1,19,1,19,1,19,1,20,1,20,1,20,1,20,1,21,1,21,1,21,1,22,
1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,23,1,23,1,23,1,23,1,24,1,24,1,
24,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,26,1,26,
1,26,1,26,1,26,1,26,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,3,27,210,
8,27,1,28,1,28,1,29,3,29,215,8,29,1,29,4,29,218,8,29,11,29,12,29,219,1,
29,1,29,5,29,224,8,29,10,29,12,29,227,9,29,3,29,229,8,29,1,29,1,29,3,29,
233,8,29,1,29,4,29,236,8,29,11,29,12,29,237,3,29,240,8,29,1,29,3,29,243,
8,29,1,29,1,29,4,29,247,8,29,11,29,12,29,248,1,29,1,29,3,29,253,8,29,1,
29,4,29,256,8,29,11,29,12,29,257,3,29,260,8,29,3,29,262,8,29,1,30,1,30,
1,30,1,30,5,30,268,8,30,10,30,12,30,271,9,30,1,30,1,30,1,30,1,30,1,30,5,
30,278,8,30,10,30,12,30,281,9,30,1,30,3,30,284,8,30,1,31,1,31,5,31,288,
8,31,10,31,12,31,291,9,31,1,32,1,32,1,32,1,33,1,33,1,33,1,33,1,34,1,34,
1,34,1,34,1,34,1,34,1,34,4,34,307,8,34,11,34,12,34,308,5,34,311,8,34,10,
34,12,34,314,9,34,1,35,4,35,317,8,35,11,35,12,35,318,1,35,1,35,1,36,1,36,
1,37,4,37,326,8,37,11,37,12,37,327,0,0,38,1,1,3,2,5,3,7,4,9,5,11,6,13,7,
15,8,17,9,19,10,21,11,23,12,25,13,27,14,29,15,31,16,33,17,35,18,37,19,39,
20,41,21,43,22,45,23,47,24,49,25,51,26,53,27,55,28,57,0,59,29,61,30,63,
0,65,0,67,0,69,31,71,32,73,0,75,33,1,0,29,2,0,76,76,108,108,2,0,73,73,105,
105,2,0,75,75,107,107,2,0,69,69,101,101,2,0,66,66,98,98,2,0,84,84,116,116,
2,0,87,87,119,119,2,0,78,78,110,110,2,0,88,88,120,120,2,0,83,83,115,115,
2,0,82,82,114,114,2,0,71,71,103,103,2,0,80,80,112,112,2,0,67,67,99,99,2,
0,79,79,111,111,2,0,65,65,97,97,2,0,68,68,100,100,2,0,72,72,104,104,2,0,
89,89,121,121,2,0,85,85,117,117,2,0,70,70,102,102,2,0,43,43,45,45,2,0,34,
34,92,92,2,0,39,39,92,92,4,0,35,36,64,90,95,95,97,123,7,0,35,36,45,45,47,
58,64,90,95,95,97,123,125,125,3,0,9,10,13,13,32,32,1,0,48,57,8,0,9,10,13,
13,32,34,39,41,44,44,60,62,91,91,93,93,353,0,1,1,0,0,0,0,3,1,0,0,0,0,5,
1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,
0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,1,
0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,0,
0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0,0,49,1,
0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,59,1,0,0,0,0,61,1,0,0,0,
0,69,1,0,0,0,0,71,1,0,0,0,0,75,1,0,0,0,1,77,1,0,0,0,3,79,1,0,0,0,5,81,1,
0,0,0,7,83,1,0,0,0,9,85,1,0,0,0,11,90,1,0,0,0,13,92,1,0,0,0,15,95,1,0,0,
0,17,98,1,0,0,0,19,100,1,0,0,0,21,103,1,0,0,0,23,105,1,0,0,0,25,108,1,0,
0,0,27,113,1,0,0,0,29,119,1,0,0,0,31,127,1,0,0,0,33,135,1,0,0,0,35,142,
1,0,0,0,37,152,1,0,0,0,39,155,1,0,0,0,41,159,1,0,0,0,43,163,1,0,0,0,45,
166,1,0,0,0,47,175,1,0,0,0,49,179,1,0,0,0,51,186,1,0,0,0,53,193,1,0,0,0,
55,209,1,0,0,0,57,211,1,0,0,0,59,261,1,0,0,0,61,283,1,0,0,0,63,285,1,0,
0,0,65,292,1,0,0,0,67,295,1,0,0,0,69,299,1,0,0,0,71,316,1,0,0,0,73,322,
1,0,0,0,75,325,1,0,0,0,77,78,5,40,0,0,78,2,1,0,0,0,79,80,5,41,0,0,80,4,
1,0,0,0,81,82,5,91,0,0,82,6,1,0,0,0,83,84,5,93,0,0,84,8,1,0,0,0,85,86,5,
44,0,0,86,10,1,0,0,0,87,91,5,61,0,0,88,89,5,61,0,0,89,91,5,61,0,0,90,87,
1,0,0,0,90,88,1,0,0,0,91,12,1,0,0,0,92,93,5,33,0,0,93,94,5,61,0,0,94,14,
1,0,0,0,95,96,5,60,0,0,96,97,5,62,0,0,97,16,1,0,0,0,98,99,5,60,0,0,99,18,
1,0,0,0,100,101,5,60,0,0,101,102,5,61,0,0,102,20,1,0,0,0,103,104,5,62,0,
0,104,22,1,0,0,0,105,106,5,62,0,0,106,107,5,61,0,0,107,24,1,0,0,0,108,109,
7,0,0,0,109,110,7,1,0,0,110,111,7,2,0,0,111,112,7,3,0,0,112,26,1,0,0,0,
113,114,7,1,0,0,114,115,7,0,0,0,115,116,7,1,0,0,116,117,7,2,0,0,117,118,
7,3,0,0,118,28,1,0,0,0,119,120,7,4,0,0,120,121,7,3,0,0,121,122,7,5,0,0,
122,123,7,6,0,0,123,124,7,3,0,0,124,125,7,3,0,0,125,126,7,7,0,0,126,30,
1,0,0,0,127,128,7,3,0,0,128,129,7,8,0,0,129,130,7,1,0,0,130,131,7,9,0,0,
131,133,7,5,0,0,132,134,7,9,0,0,133,132,1,0,0,0,133,134,1,0,0,0,134,32,
1,0,0,0,135,136,7,10,0,0,136,137,7,3,0,0,137,138,7,11,0,0,138,139,7,3,0,
0,139,140,7,8,0,0,140,141,7,12,0,0,141,34,1,0,0,0,142,143,7,13,0,0,143,
144,7,14,0,0,144,145,7,7,0,0,145,146,7,5,0,0,146,147,7,15,0,0,147,148,7,
1,0,0,148,150,7,7,0,0,149,151,7,9,0,0,150,149,1,0,0,0,150,151,1,0,0,0,151,
36,1,0,0,0,152,153,7,1,0,0,153,154,7,7,0,0,154,38,1,0,0,0,155,156,7,7,0,
0,156,157,7,14,0,0,157,158,7,5,0,0,158,40,1,0,0,0,159,160,7,15,0,0,160,
161,7,7,0,0,161,162,7,16,0,0,162,42,1,0,0,0,163,164,7,14,0,0,164,165,7,
10,0,0,165,44,1,0,0,0,166,167,7,17,0,0,167,168,7,15,0,0,168,169,7,9,0,0,
169,170,7,5,0,0,170,171,7,14,0,0,171,172,7,2,0,0,172,173,7,3,0,0,173,174,
7,7,0,0,174,46,1,0,0,0,175,176,7,17,0,0,176,177,7,15,0,0,177,178,7,9,0,
0,178,48,1,0,0,0,179,180,7,17,0,0,180,181,7,15,0,0,181,182,7,9,0,0,182,
183,7,15,0,0,183,184,7,7,0,0,184,185,7,18,0,0,185,50,1,0,0,0,186,187,7,
17,0,0,187,188,7,15,0,0,188,189,7,9,0,0,189,190,7,15,0,0,190,191,7,0,0,
0,191,192,7,0,0,0,192,52,1,0,0,0,193,194,7,9,0,0,194,195,7,3,0,0,195,196,
7,15,0,0,196,197,7,10,0,0,197,198,7,13,0,0,198,199,7,17,0,0,199,54,1,0,
0,0,200,201,7,5,0,0,201,202,7,10,0,0,202,203,7,19,0,0,203,210,7,3,0,0,204,
205,7,20,0,0,205,206,7,15,0,0,206,207,7,0,0,0,207,208,7,9,0,0,208,210,7,
3,0,0,209,200,1,0,0,0,209,204,1,0,0,0,210,56,1,0,0,0,211,212,7,21,0,0,212,
58,1,0,0,0,213,215,3,57,28,0,214,213,1,0,0,0,214,215,1,0,0,0,215,217,1,
0,0,0,216,218,3,73,36,0,217,216,1,0,0,0,218,219,1,0,0,0,219,217,1,0,0,0,
219,220,1,0,0,0,220,228,1,0,0,0,221,225,5,46,0,0,222,224,3,73,36,0,223,
222,1,0,0,0,224,227,1,0,0,0,225,223,1,0,0,0,225,226,1,0,0,0,226,229,1,0,
0,0,227,225,1,0,0,0,228,221,1,0,0,0,228,229,1,0,0,0,229,239,1,0,0,0,230,
232,7,3,0,0,231,233,3,57,28,0,232,231,1,0,0,0,232,233,1,0,0,0,233,235,1,
0,0,0,234,236,3,73,36,0,235,234,1,0,0,0,236,237,1,0,0,0,237,235,1,0,0,0,
237,238,1,0,0,0,238,240,1,0,0,0,239,230,1,0,0,0,239,240,1,0,0,0,240,262,
1,0,0,0,241,243,3,57,28,0,242,241,1,0,0,0,242,243,1,0,0,0,243,244,1,0,0,
0,244,246,5,46,0,0,245,247,3,73,36,0,246,245,1,0,0,0,247,248,1,0,0,0,248,
246,1,0,0,0,248,249,1,0,0,0,249,259,1,0,0,0,250,252,7,3,0,0,251,253,3,57,
28,0,252,251,1,0,0,0,252,253,1,0,0,0,253,255,1,0,0,0,254,256,3,73,36,0,
255,254,1,0,0,0,256,257,1,0,0,0,257,255,1,0,0,0,257,258,1,0,0,0,258,260,
1,0,0,0,259,250,1,0,0,0,259,260,1,0,0,0,260,262,1,0,0,0,261,214,1,0,0,0,
261,242,1,0,0,0,262,60,1,0,0,0,263,269,5,34,0,0,264,268,8,22,0,0,265,266,
5,92,0,0,266,268,9,0,0,0,267,264,1,0,0,0,267,265,1,0,0,0,268,271,1,0,0,
0,269,267,1,0,0,0,269,270,1,0,0,0,270,272,1,0,0,0,271,269,1,0,0,0,272,284,
5,34,0,0,273,279,5,39,0,0,274,278,8,23,0,0,275,276,5,92,0,0,276,278,9,0,
0,0,277,274,1,0,0,0,277,275,1,0,0,0,278,281,1,0,0,0,279,277,1,0,0,0,279,
280,1,0,0,0,280,282,1,0,0,0,281,279,1,0,0,0,282,284,5,39,0,0,283,263,1,
0,0,0,283,273,1,0,0,0,284,62,1,0,0,0,285,289,7,24,0,0,286,288,7,25,0,0,
287,286,1,0,0,0,288,291,1,0,0,0,289,287,1,0,0,0,289,290,1,0,0,0,290,64,
1,0,0,0,291,289,1,0,0,0,292,293,5,91,0,0,293,294,5,93,0,0,294,66,1,0,0,
0,295,296,5,91,0,0,296,297,5,42,0,0,297,298,5,93,0,0,298,68,1,0,0,0,299,
312,3,63,31,0,300,301,5,46,0,0,301,311,3,63,31,0,302,311,3,65,32,0,303,
311,3,67,33,0,304,306,5,46,0,0,305,307,3,73,36,0,306,305,1,0,0,0,307,308,
1,0,0,0,308,306,1,0,0,0,308,309,1,0,0,0,309,311,1,0,0,0,310,300,1,0,0,0,
310,302,1,0,0,0,310,303,1,0,0,0,310,304,1,0,0,0,311,314,1,0,0,0,312,310,
1,0,0,0,312,313,1,0,0,0,313,70,1,0,0,0,314,312,1,0,0,0,315,317,7,26,0,0,
316,315,1,0,0,0,317,318,1,0,0,0,318,316,1,0,0,0,318,319,1,0,0,0,319,320,
1,0,0,0,320,321,6,35,0,0,321,72,1,0,0,0,322,323,7,27,0,0,323,74,1,0,0,0,
324,326,8,28,0,0,325,324,1,0,0,0,326,327,1,0,0,0,327,325,1,0,0,0,327,328,
1,0,0,0,328,76,1,0,0,0,29,0,90,133,150,209,214,219,225,228,232,237,239,
242,248,252,257,259,261,267,269,277,279,283,289,308,310,312,318,327,1,6,
0,0];
31,7,31,2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,
7,38,1,0,1,0,1,1,1,1,1,2,1,2,1,3,1,3,1,4,1,4,1,5,1,5,1,5,3,5,93,8,5,1,6,
1,6,1,6,1,7,1,7,1,7,1,8,1,8,1,9,1,9,1,9,1,10,1,10,1,11,1,11,1,11,1,12,1,
12,1,12,1,12,1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14,
1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,3,15,136,8,15,1,16,1,16,1,
16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,3,17,153,
8,17,1,18,1,18,1,18,1,19,1,19,1,19,1,19,1,20,1,20,1,20,1,20,1,21,1,21,1,
21,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,23,1,23,1,23,1,23,1,24,
1,24,1,24,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,
26,1,26,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1,27,1,27,1,27,1,28,1,28,1,28,
1,28,1,28,1,28,1,28,1,28,1,28,3,28,218,8,28,1,29,1,29,1,30,3,30,223,8,30,
1,30,4,30,226,8,30,11,30,12,30,227,1,30,1,30,5,30,232,8,30,10,30,12,30,
235,9,30,3,30,237,8,30,1,30,1,30,3,30,241,8,30,1,30,4,30,244,8,30,11,30,
12,30,245,3,30,248,8,30,1,30,3,30,251,8,30,1,30,1,30,4,30,255,8,30,11,30,
12,30,256,1,30,1,30,3,30,261,8,30,1,30,4,30,264,8,30,11,30,12,30,265,3,
30,268,8,30,3,30,270,8,30,1,31,1,31,1,31,1,31,5,31,276,8,31,10,31,12,31,
279,9,31,1,31,1,31,1,31,1,31,1,31,5,31,286,8,31,10,31,12,31,289,9,31,1,
31,3,31,292,8,31,1,32,1,32,5,32,296,8,32,10,32,12,32,299,9,32,1,33,1,33,
1,33,1,34,1,34,1,34,1,34,1,35,1,35,1,35,1,35,1,35,1,35,1,35,4,35,315,8,
35,11,35,12,35,316,5,35,319,8,35,10,35,12,35,322,9,35,1,36,4,36,325,8,36,
11,36,12,36,326,1,36,1,36,1,37,1,37,1,38,4,38,334,8,38,11,38,12,38,335,
0,0,39,1,1,3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,
27,14,29,15,31,16,33,17,35,18,37,19,39,20,41,21,43,22,45,23,47,24,49,25,
51,26,53,27,55,28,57,29,59,0,61,30,63,31,65,0,67,0,69,0,71,32,73,33,75,
0,77,34,1,0,29,2,0,76,76,108,108,2,0,73,73,105,105,2,0,75,75,107,107,2,
0,69,69,101,101,2,0,66,66,98,98,2,0,84,84,116,116,2,0,87,87,119,119,2,0,
78,78,110,110,2,0,88,88,120,120,2,0,83,83,115,115,2,0,82,82,114,114,2,0,
71,71,103,103,2,0,80,80,112,112,2,0,67,67,99,99,2,0,79,79,111,111,2,0,65,
65,97,97,2,0,68,68,100,100,2,0,72,72,104,104,2,0,89,89,121,121,2,0,85,85,
117,117,2,0,70,70,102,102,2,0,43,43,45,45,2,0,34,34,92,92,2,0,39,39,92,
92,4,0,35,36,64,90,95,95,97,123,7,0,35,36,45,45,47,58,64,90,95,95,97,123,
125,125,3,0,9,10,13,13,32,32,1,0,48,57,8,0,9,10,13,13,32,34,39,41,44,44,
60,62,91,91,93,93,361,0,1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,
9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,
0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,
31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,
0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,
53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,71,1,0,
0,0,0,73,1,0,0,0,0,77,1,0,0,0,1,79,1,0,0,0,3,81,1,0,0,0,5,83,1,0,0,0,7,
85,1,0,0,0,9,87,1,0,0,0,11,92,1,0,0,0,13,94,1,0,0,0,15,97,1,0,0,0,17,100,
1,0,0,0,19,102,1,0,0,0,21,105,1,0,0,0,23,107,1,0,0,0,25,110,1,0,0,0,27,
115,1,0,0,0,29,121,1,0,0,0,31,129,1,0,0,0,33,137,1,0,0,0,35,144,1,0,0,0,
37,154,1,0,0,0,39,157,1,0,0,0,41,161,1,0,0,0,43,165,1,0,0,0,45,168,1,0,
0,0,47,177,1,0,0,0,49,181,1,0,0,0,51,188,1,0,0,0,53,195,1,0,0,0,55,202,
1,0,0,0,57,217,1,0,0,0,59,219,1,0,0,0,61,269,1,0,0,0,63,291,1,0,0,0,65,
293,1,0,0,0,67,300,1,0,0,0,69,303,1,0,0,0,71,307,1,0,0,0,73,324,1,0,0,0,
75,330,1,0,0,0,77,333,1,0,0,0,79,80,5,40,0,0,80,2,1,0,0,0,81,82,5,41,0,
0,82,4,1,0,0,0,83,84,5,91,0,0,84,6,1,0,0,0,85,86,5,93,0,0,86,8,1,0,0,0,
87,88,5,44,0,0,88,10,1,0,0,0,89,93,5,61,0,0,90,91,5,61,0,0,91,93,5,61,0,
0,92,89,1,0,0,0,92,90,1,0,0,0,93,12,1,0,0,0,94,95,5,33,0,0,95,96,5,61,0,
0,96,14,1,0,0,0,97,98,5,60,0,0,98,99,5,62,0,0,99,16,1,0,0,0,100,101,5,60,
0,0,101,18,1,0,0,0,102,103,5,60,0,0,103,104,5,61,0,0,104,20,1,0,0,0,105,
106,5,62,0,0,106,22,1,0,0,0,107,108,5,62,0,0,108,109,5,61,0,0,109,24,1,
0,0,0,110,111,7,0,0,0,111,112,7,1,0,0,112,113,7,2,0,0,113,114,7,3,0,0,114,
26,1,0,0,0,115,116,7,1,0,0,116,117,7,0,0,0,117,118,7,1,0,0,118,119,7,2,
0,0,119,120,7,3,0,0,120,28,1,0,0,0,121,122,7,4,0,0,122,123,7,3,0,0,123,
124,7,5,0,0,124,125,7,6,0,0,125,126,7,3,0,0,126,127,7,3,0,0,127,128,7,7,
0,0,128,30,1,0,0,0,129,130,7,3,0,0,130,131,7,8,0,0,131,132,7,1,0,0,132,
133,7,9,0,0,133,135,7,5,0,0,134,136,7,9,0,0,135,134,1,0,0,0,135,136,1,0,
0,0,136,32,1,0,0,0,137,138,7,10,0,0,138,139,7,3,0,0,139,140,7,11,0,0,140,
141,7,3,0,0,141,142,7,8,0,0,142,143,7,12,0,0,143,34,1,0,0,0,144,145,7,13,
0,0,145,146,7,14,0,0,146,147,7,7,0,0,147,148,7,5,0,0,148,149,7,15,0,0,149,
150,7,1,0,0,150,152,7,7,0,0,151,153,7,9,0,0,152,151,1,0,0,0,152,153,1,0,
0,0,153,36,1,0,0,0,154,155,7,1,0,0,155,156,7,7,0,0,156,38,1,0,0,0,157,158,
7,7,0,0,158,159,7,14,0,0,159,160,7,5,0,0,160,40,1,0,0,0,161,162,7,15,0,
0,162,163,7,7,0,0,163,164,7,16,0,0,164,42,1,0,0,0,165,166,7,14,0,0,166,
167,7,10,0,0,167,44,1,0,0,0,168,169,7,17,0,0,169,170,7,15,0,0,170,171,7,
9,0,0,171,172,7,5,0,0,172,173,7,14,0,0,173,174,7,2,0,0,174,175,7,3,0,0,
175,176,7,7,0,0,176,46,1,0,0,0,177,178,7,17,0,0,178,179,7,15,0,0,179,180,
7,9,0,0,180,48,1,0,0,0,181,182,7,17,0,0,182,183,7,15,0,0,183,184,7,9,0,
0,184,185,7,15,0,0,185,186,7,7,0,0,186,187,7,18,0,0,187,50,1,0,0,0,188,
189,7,17,0,0,189,190,7,15,0,0,190,191,7,9,0,0,191,192,7,15,0,0,192,193,
7,0,0,0,193,194,7,0,0,0,194,52,1,0,0,0,195,196,7,9,0,0,196,197,7,3,0,0,
197,198,7,15,0,0,198,199,7,10,0,0,199,200,7,13,0,0,200,201,7,17,0,0,201,
54,1,0,0,0,202,203,7,3,0,0,203,204,7,8,0,0,204,205,7,15,0,0,205,206,7,13,
0,0,206,207,7,5,0,0,207,56,1,0,0,0,208,209,7,5,0,0,209,210,7,10,0,0,210,
211,7,19,0,0,211,218,7,3,0,0,212,213,7,20,0,0,213,214,7,15,0,0,214,215,
7,0,0,0,215,216,7,9,0,0,216,218,7,3,0,0,217,208,1,0,0,0,217,212,1,0,0,0,
218,58,1,0,0,0,219,220,7,21,0,0,220,60,1,0,0,0,221,223,3,59,29,0,222,221,
1,0,0,0,222,223,1,0,0,0,223,225,1,0,0,0,224,226,3,75,37,0,225,224,1,0,0,
0,226,227,1,0,0,0,227,225,1,0,0,0,227,228,1,0,0,0,228,236,1,0,0,0,229,233,
5,46,0,0,230,232,3,75,37,0,231,230,1,0,0,0,232,235,1,0,0,0,233,231,1,0,
0,0,233,234,1,0,0,0,234,237,1,0,0,0,235,233,1,0,0,0,236,229,1,0,0,0,236,
237,1,0,0,0,237,247,1,0,0,0,238,240,7,3,0,0,239,241,3,59,29,0,240,239,1,
0,0,0,240,241,1,0,0,0,241,243,1,0,0,0,242,244,3,75,37,0,243,242,1,0,0,0,
244,245,1,0,0,0,245,243,1,0,0,0,245,246,1,0,0,0,246,248,1,0,0,0,247,238,
1,0,0,0,247,248,1,0,0,0,248,270,1,0,0,0,249,251,3,59,29,0,250,249,1,0,0,
0,250,251,1,0,0,0,251,252,1,0,0,0,252,254,5,46,0,0,253,255,3,75,37,0,254,
253,1,0,0,0,255,256,1,0,0,0,256,254,1,0,0,0,256,257,1,0,0,0,257,267,1,0,
0,0,258,260,7,3,0,0,259,261,3,59,29,0,260,259,1,0,0,0,260,261,1,0,0,0,261,
263,1,0,0,0,262,264,3,75,37,0,263,262,1,0,0,0,264,265,1,0,0,0,265,263,1,
0,0,0,265,266,1,0,0,0,266,268,1,0,0,0,267,258,1,0,0,0,267,268,1,0,0,0,268,
270,1,0,0,0,269,222,1,0,0,0,269,250,1,0,0,0,270,62,1,0,0,0,271,277,5,34,
0,0,272,276,8,22,0,0,273,274,5,92,0,0,274,276,9,0,0,0,275,272,1,0,0,0,275,
273,1,0,0,0,276,279,1,0,0,0,277,275,1,0,0,0,277,278,1,0,0,0,278,280,1,0,
0,0,279,277,1,0,0,0,280,292,5,34,0,0,281,287,5,39,0,0,282,286,8,23,0,0,
283,284,5,92,0,0,284,286,9,0,0,0,285,282,1,0,0,0,285,283,1,0,0,0,286,289,
1,0,0,0,287,285,1,0,0,0,287,288,1,0,0,0,288,290,1,0,0,0,289,287,1,0,0,0,
290,292,5,39,0,0,291,271,1,0,0,0,291,281,1,0,0,0,292,64,1,0,0,0,293,297,
7,24,0,0,294,296,7,25,0,0,295,294,1,0,0,0,296,299,1,0,0,0,297,295,1,0,0,
0,297,298,1,0,0,0,298,66,1,0,0,0,299,297,1,0,0,0,300,301,5,91,0,0,301,302,
5,93,0,0,302,68,1,0,0,0,303,304,5,91,0,0,304,305,5,42,0,0,305,306,5,93,
0,0,306,70,1,0,0,0,307,320,3,65,32,0,308,309,5,46,0,0,309,319,3,65,32,0,
310,319,3,67,33,0,311,319,3,69,34,0,312,314,5,46,0,0,313,315,3,75,37,0,
314,313,1,0,0,0,315,316,1,0,0,0,316,314,1,0,0,0,316,317,1,0,0,0,317,319,
1,0,0,0,318,308,1,0,0,0,318,310,1,0,0,0,318,311,1,0,0,0,318,312,1,0,0,0,
319,322,1,0,0,0,320,318,1,0,0,0,320,321,1,0,0,0,321,72,1,0,0,0,322,320,
1,0,0,0,323,325,7,26,0,0,324,323,1,0,0,0,325,326,1,0,0,0,326,324,1,0,0,
0,326,327,1,0,0,0,327,328,1,0,0,0,328,329,6,36,0,0,329,74,1,0,0,0,330,331,
7,27,0,0,331,76,1,0,0,0,332,334,8,28,0,0,333,332,1,0,0,0,334,335,1,0,0,
0,335,333,1,0,0,0,335,336,1,0,0,0,336,78,1,0,0,0,29,0,92,135,152,217,222,
227,233,236,240,245,247,250,256,260,265,267,269,275,277,285,287,291,297,
316,318,320,326,335,1,6,0,0];
private static __ATN: ATN;
public static get _ATN(): ATN {

View File

@@ -21,6 +21,8 @@ import { FunctionParamContext } from "./FilterQueryParser.js";
import { ArrayContext } from "./FilterQueryParser.js";
import { ValueContext } from "./FilterQueryParser.js";
import { KeyContext } from "./FilterQueryParser.js";
import { FieldContext } from "./FilterQueryParser.js";
import { ExactCallContext } from "./FilterQueryParser.js";
/**
@@ -208,5 +210,25 @@ export default class FilterQueryListener extends ParseTreeListener {
* @param ctx the parse tree
*/
exitKey?: (ctx: KeyContext) => void;
/**
* Enter a parse tree produced by `FilterQueryParser.field`.
* @param ctx the parse tree
*/
enterField?: (ctx: FieldContext) => void;
/**
* Exit a parse tree produced by `FilterQueryParser.field`.
* @param ctx the parse tree
*/
exitField?: (ctx: FieldContext) => void;
/**
* Enter a parse tree produced by `FilterQueryParser.exactCall`.
* @param ctx the parse tree
*/
enterExactCall?: (ctx: ExactCallContext) => void;
/**
* Exit a parse tree produced by `FilterQueryParser.exactCall`.
* @param ctx the parse tree
*/
exitExactCall?: (ctx: ExactCallContext) => void;
}

File diff suppressed because it is too large Load Diff

View File

@@ -21,6 +21,8 @@ import { FunctionParamContext } from "./FilterQueryParser.js";
import { ArrayContext } from "./FilterQueryParser.js";
import { ValueContext } from "./FilterQueryParser.js";
import { KeyContext } from "./FilterQueryParser.js";
import { FieldContext } from "./FilterQueryParser.js";
import { ExactCallContext } from "./FilterQueryParser.js";
/**
@@ -139,5 +141,17 @@ export default class FilterQueryVisitor<Result> extends ParseTreeVisitor<Result>
* @return the visitor result
*/
visitKey?: (ctx: KeyContext) => Result;
/**
* Visit a parse tree produced by `FilterQueryParser.field`.
* @param ctx the parse tree
* @return the visitor result
*/
visitField?: (ctx: FieldContext) => Result;
/**
* Visit a parse tree produced by `FilterQueryParser.exactCall`.
* @param ctx the parse tree
* @return the visitor result
*/
visitExactCall?: (ctx: ExactCallContext) => Result;
}

View File

@@ -246,6 +246,12 @@
padding-left: 6px !important;
}
&__label {
display: inline-flex;
align-items: baseline;
gap: 6px;
}
&__pinned-icon {
flex-shrink: 0;
color: var(--text-robin-400);

View File

@@ -76,6 +76,7 @@ export interface PrettyViewProps {
value: unknown,
keyPath: readonly (string | number)[],
) => React.ReactNode | undefined;
labelSuffixRenderer?: (fieldKey: string) => React.ReactNode;
}
function PrettyView({
@@ -88,6 +89,7 @@ function PrettyView({
pinnedFieldsValue,
onPinnedFieldsChange,
renderLeafValue,
labelSuffixRenderer,
}: PrettyViewProps): JSX.Element {
const isDarkMode = useIsDarkMode();
const [, setCopy] = useCopyToClipboard();
@@ -318,10 +320,24 @@ function PrettyView({
}}
/>
<span>{displayKey}</span>
{labelSuffixRenderer?.(displayKey)}
</span>
);
},
[togglePin, pinnedEntries],
[togglePin, pinnedEntries, labelSuffixRenderer],
);
const labelRenderer = useCallback(
(keyPath: KeyPath): React.ReactNode => {
const displayKey = String(keyPath[0]);
return (
<span className="pretty-view__label">
<span>{displayKey}</span>
{labelSuffixRenderer?.(displayKey)}
</span>
);
},
[labelSuffixRenderer],
);
return (
@@ -364,6 +380,7 @@ function PrettyView({
shouldExpandNodeInitially={shouldExpandNodeInitially}
valueRenderer={valueRenderer}
getItemString={getItemString}
labelRenderer={labelRenderer}
/>
</div>
);

View File

@@ -0,0 +1,14 @@
export interface SemconvMigrationReportEntry {
current: string;
old: string;
signal: string;
services: string[];
resourceSets: number;
lastSeenUnixMilli: number;
}
export interface SemconvMigrationReport {
startUnixMilli: number;
endUnixMilli: number;
entries: SemconvMigrationReportEntry[];
}

View File

@@ -123,6 +123,8 @@ export type SpaceAggregation =
export type ColumnType = 'group' | 'aggregation';
export type FieldResolution = 'exact';
// ===================== Variable Types =====================
export type VariableType = 'query' | 'dynamic' | 'custom' | 'text';
@@ -142,6 +144,7 @@ export interface TelemetryFieldKey {
signal?: SignalType;
fieldContext?: FieldContext;
fieldDataType?: FieldDataType;
fieldResolution?: FieldResolution;
materialized?: boolean;
isIndexed?: boolean;
}

View File

@@ -0,0 +1,40 @@
import {
findOldSemconvNames,
getSemconvMembers,
getSemconvRename,
} from 'utils/semconv';
describe('semantic convention helpers', () => {
it('returns the current name for an old attribute', () => {
expect(getSemconvRename('deployment.environment')).toMatchObject({
old: 'deployment.environment',
current: 'deployment.environment.name',
});
});
it('finds old names in editor text without matching larger custom names', () => {
expect(
findOldSemconvNames(
"deployment.environment = 'prod' AND custom.db.system.value = 'x'",
),
).toStrictEqual([
expect.objectContaining({
old: 'deployment.environment',
current: 'deployment.environment.name',
}),
]);
});
it('does not warn for current names', () => {
expect(
findOldSemconvNames('deployment.environment.name = prod'),
).toStrictEqual([]);
});
it('returns current-first members for compatibility readers', () => {
expect(getSemconvMembers('http.request.method')).toStrictEqual([
'http.request.method',
'http.method',
]);
});
});

View File

@@ -0,0 +1,69 @@
import {
SEMCONV_FAMILIES,
SemconvFamily,
} from 'constants/generated/semconvFamilies.gen';
export type SemconvRename = {
old: string;
current: string;
family: SemconvFamily;
};
const OLD_NAMES = SEMCONV_FAMILIES.flatMap((family) =>
family.members.map((member) => ({
old: member.name,
current: family.current,
family,
})),
);
const OLD_NAME_INDEX = new Map(OLD_NAMES.map((rename) => [rename.old, rename]));
const FAMILY_BY_NAME = new Map(
SEMCONV_FAMILIES.flatMap((family) =>
[family.current, ...family.members.map((member) => member.name)].map(
(name) => [name, family] as const,
),
),
);
export function getSemconvRename(name: string): SemconvRename | undefined {
return OLD_NAME_INDEX.get(name);
}
/** Returns the current name first, followed by every historical spelling. */
export function getSemconvMembers(name: string): readonly string[] {
const family = FAMILY_BY_NAME.get(name);
return family
? [family.current, ...family.members.map((member) => member.name)]
: [name];
}
export function findOldSemconvNames(text: string): SemconvRename[] {
if (!text) {
return [];
}
return OLD_NAMES.filter(({ old }) => containsSemconvName(text, old));
}
function containsSemconvName(text: string, name: string): boolean {
let offset = 0;
while (offset < text.length) {
const index = text.indexOf(name, offset);
if (index === -1) {
return false;
}
const before = index === 0 ? '' : text[index - 1];
const afterIndex = index + name.length;
const after = afterIndex === text.length ? '' : text[afterIndex];
if (!isSemconvNameCharacter(before) && !isSemconvNameCharacter(after)) {
return true;
}
offset = index + 1;
}
return false;
}
function isSemconvNameCharacter(value: string): boolean {
return /[A-Za-z0-9_.-]/.test(value);
}

View File

@@ -50,30 +50,30 @@ primary
* [NOT] BETWEEN, [NOT] IN, [NOT] EXISTS, [NOT] REGEXP, [NOT] CONTAINS, etc.
*/
comparison
: key EQUALS value
| key (NOT_EQUALS | NEQ) value
| key LT value
| key LE value
| key GT value
| key GE value
: field EQUALS value
| field (NOT_EQUALS | NEQ) value
| field LT value
| field LE value
| field GT value
| field GE value
| key (LIKE | ILIKE) value
| key NOT (LIKE | ILIKE) value
| field (LIKE | ILIKE) value
| field NOT (LIKE | ILIKE) value
| key BETWEEN value AND value
| key NOT BETWEEN value AND value
| field BETWEEN value AND value
| field NOT BETWEEN value AND value
| key inClause
| key notInClause
| field inClause
| field notInClause
| key EXISTS
| key NOT EXISTS
| field EXISTS
| field NOT EXISTS
| key REGEXP value
| key NOT REGEXP value
| field REGEXP value
| field NOT REGEXP value
| key CONTAINS value
| key NOT CONTAINS value
| field CONTAINS value
| field NOT CONTAINS value
;
// in(...) or in[...]
@@ -126,7 +126,7 @@ functionParamList
;
functionParam
: key
: field
| value
| array
;
@@ -155,6 +155,17 @@ key
: KEY
;
// exact(key) disables semantic-convention family resolution for this field.
// It is deliberately a field wrapper rather than a general function.
field
: key
| exactCall
;
exactCall
: EXACT LPAREN key RPAREN
;
/*
* Lexer Rules
@@ -195,6 +206,7 @@ HAS : [Hh][Aa][Ss] ;
HASANY : [Hh][Aa][Ss][Aa][Nn][Yy] ;
HASALL : [Hh][Aa][Ss][Aa][Ll][Ll] ;
SEARCH : [Ss][Ee][Aa][Rr][Cc][Hh] ;
EXACT : [Ee][Xx][Aa][Cc][Tt] ;
// Potential boolean constants
BOOL

View File

@@ -46,5 +46,23 @@ func (provider *provider) addFieldsRoutes(router *mux.Router) error {
return err
}
if err := router.Handle("/api/v1/fields/semconv-migration", handler.New(provider.authzMiddleware.ViewAccess(provider.fieldsHandler.GetSemconvMigrationReport), handler.OpenAPIDef{
ID: "GetSemconvMigrationReport",
Tags: []string{"fields"},
Summary: "Get semantic-convention migration report",
Description: "Returns services that still emit old semantic-convention names without the current family name",
Request: nil,
RequestQuery: new(telemetrytypes.PostableSemconvMigrationReportParams),
RequestContentType: "",
Response: new(telemetrytypes.GettableSemconvMigrationReport),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
})).Methods(http.MethodGet).GetError(); err != nil {
return err
}
return nil
}

View File

@@ -51,9 +51,9 @@ func postableRuleExamples() []handler.OpenAPIExample {
"filter": map[string]any{"expression": "k8s.deployment.name = 'api-service'"},
"groupBy": []any{
map[string]any{"name": "k8s.pod.name", "fieldContext": "resource", "fieldDataType": "string"},
map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"},
map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"},
},
"legend": "{{k8s.pod.name}} ({{deployment.environment}})",
"legend": "{{k8s.pod.name}} ({{deployment.environment.name}})",
},
},
},
@@ -74,12 +74,12 @@ func postableRuleExamples() []handler.OpenAPIExample {
},
"evaluation": rolling("15m", "1m"),
"notificationSettings": map[string]any{
"groupBy": []any{"k8s.pod.name", "deployment.environment"},
"groupBy": []any{"k8s.pod.name", "deployment.environment.name"},
"renotify": renotify("4h", "firing"),
},
"labels": map[string]any{"severity": "critical", "team": "platform"},
"annotations": map[string]any{
"description": "Pod {{$k8s.pod.name}} CPU is at {{$value}} of request in {{$deployment.environment}}.",
"description": "Pod {{$k8s.pod.name}} CPU is at {{$value}} of request in {{$deployment.environment.name}}.",
"summary": "Pod CPU above {{$threshold}} of request",
},
},
@@ -170,7 +170,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
{
Name: "metric_promql",
Summary: "Metric threshold PromQL rule",
Description: "PromQL expression instead of the builder. Dotted OTEL resource attributes are quoted (\"deployment.environment\"). Useful for queries that combine series with group_right or other Prom operators.",
Description: "PromQL expression instead of the builder. Dotted OTEL resource attributes are quoted (\"deployment.environment.name\"). Useful for queries that combine series with group_right or other Prom operators.",
Value: map[string]any{
"alert": "Kafka consumer group lag above 1000",
"alertType": "METRIC_BASED_ALERT",
@@ -187,7 +187,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
"type": "promql",
"spec": map[string]any{
"name": "A",
"query": "(max by(topic, partition, \"deployment.environment\")(kafka_log_end_offset) - on(topic, partition, \"deployment.environment\") group_right max by(group, topic, partition, \"deployment.environment\")(kafka_consumer_committed_offset)) > 0",
"query": "(max by(topic, partition, \"deployment.environment.name\")(kafka_log_end_offset) - on(topic, partition, \"deployment.environment.name\") group_right max by(group, topic, partition, \"deployment.environment.name\")(kafka_consumer_committed_offset)) > 0",
"legend": "{{topic}}/{{partition}} ({{group}})",
},
},
@@ -299,9 +299,9 @@ func postableRuleExamples() []handler.OpenAPIExample {
"filter": map[string]any{"expression": "service.name = 'payments-api' AND severity_text = 'ERROR' AND body CONTAINS 'panic'"},
"groupBy": []any{
map[string]any{"name": "k8s.pod.name", "fieldContext": "resource", "fieldDataType": "string"},
map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"},
map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"},
},
"legend": "{{k8s.pod.name}} ({{deployment.environment}})",
"legend": "{{k8s.pod.name}} ({{deployment.environment.name}})",
},
},
},
@@ -322,12 +322,12 @@ func postableRuleExamples() []handler.OpenAPIExample {
},
"evaluation": rolling("5m", "1m"),
"notificationSettings": map[string]any{
"groupBy": []any{"k8s.pod.name", "deployment.environment"},
"groupBy": []any{"k8s.pod.name", "deployment.environment.name"},
"renotify": renotify("15m", "firing"),
},
"labels": map[string]any{"severity": "critical", "team": "payments"},
"annotations": map[string]any{
"description": "{{$k8s.pod.name}} emitted {{$value}} panic log(s) in {{$deployment.environment}}.",
"description": "{{$k8s.pod.name}} emitted {{$value}} panic log(s) in {{$deployment.environment.name}}.",
"summary": "Payments service panic",
},
},
@@ -358,7 +358,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
"disabled": true,
"aggregations": []any{map[string]any{"expression": "count()"}},
"filter": map[string]any{"expression": "service.name = 'payments-api' AND severity_text IN ['ERROR', 'FATAL']"},
"groupBy": []any{map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"}},
"groupBy": []any{map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"}},
},
},
map[string]any{
@@ -370,7 +370,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
"disabled": true,
"aggregations": []any{map[string]any{"expression": "count()"}},
"filter": map[string]any{"expression": "service.name = 'payments-api'"},
"groupBy": []any{map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"}},
"groupBy": []any{map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"}},
},
},
map[string]any{
@@ -378,7 +378,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
"spec": map[string]any{
"name": "F1",
"expression": "(A / B) * 100",
"legend": "{{deployment.environment}}",
"legend": "{{deployment.environment.name}}",
},
},
},
@@ -399,12 +399,12 @@ func postableRuleExamples() []handler.OpenAPIExample {
},
"evaluation": rolling("5m", "1m"),
"notificationSettings": map[string]any{
"groupBy": []any{"deployment.environment"},
"groupBy": []any{"deployment.environment.name"},
"renotify": renotify("30m", "firing"),
},
"labels": map[string]any{"severity": "critical", "team": "payments"},
"annotations": map[string]any{
"description": "Error log rate in {{$deployment.environment}} is {{$value}}%",
"description": "Error log rate in {{$deployment.environment.name}} is {{$value}}%",
"summary": "Payments-api error rate above {{$threshold}}%",
},
},
@@ -669,10 +669,10 @@ func postableRuleExamples() []handler.OpenAPIExample {
"stepInterval": 60,
"disabled": true,
"aggregations": []any{map[string]any{"expression": "count()"}},
"filter": map[string]any{"expression": "service.name CONTAINS 'api' AND http.status_code >= 500"},
"filter": map[string]any{"expression": "service.name CONTAINS 'api' AND http.response.status_code >= 500"},
"groupBy": []any{
map[string]any{"name": "service.name", "fieldContext": "resource", "fieldDataType": "string"},
map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"},
map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"},
},
},
},
@@ -687,7 +687,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
"filter": map[string]any{"expression": "service.name CONTAINS 'api'"},
"groupBy": []any{
map[string]any{"name": "service.name", "fieldContext": "resource", "fieldDataType": "string"},
map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"},
map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"},
},
},
},
@@ -696,7 +696,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
"spec": map[string]any{
"name": "F1",
"expression": "(A / B) * 100",
"legend": "{{service.name}} ({{deployment.environment}})",
"legend": "{{service.name}} ({{deployment.environment.name}})",
},
},
},
@@ -717,14 +717,14 @@ func postableRuleExamples() []handler.OpenAPIExample {
},
"evaluation": rolling("5m", "1m"),
"notificationSettings": map[string]any{
"groupBy": []any{"service.name", "deployment.environment"},
"groupBy": []any{"service.name", "deployment.environment.name"},
"newGroupEvalDelay": "2m",
"usePolicy": false,
"renotify": renotify("30m", "firing", "nodata"),
},
"labels": map[string]any{"team": "platform"},
"annotations": map[string]any{
"description": "{{$service.name}} 5xx rate in {{$deployment.environment}} is {{$value}}%.",
"description": "{{$service.name}} 5xx rate in {{$deployment.environment.name}} is {{$value}}%.",
"summary": "API service error rate elevated",
},
},

View File

@@ -188,11 +188,15 @@ func (r *WhereClauseRewriter) VisitPrimary(ctx *parser.PrimaryContext) any {
// VisitComparison visits comparison expressions.
func (r *WhereClauseRewriter) VisitComparison(ctx *parser.ComparisonContext) any {
if ctx.Key() == nil {
if ctx.Field() == nil {
return nil
}
key := ctx.Key().GetText()
field := ctx.Field().GetText()
key := field
if exactCall := ctx.Field().ExactCall(); exactCall != nil {
key = exactCall.Key().GetText()
}
r.keysSeen[key] = struct{}{}
// Check if this key is in the labels and was part of group by
@@ -200,13 +204,13 @@ func (r *WhereClauseRewriter) VisitComparison(ctx *parser.ComparisonContext) any
if _, partOfGroup := r.groupBySet[key]; partOfGroup {
// Case 1: Replace with actual value
escapedValue := escapeValueIfNeeded(value)
fmt.Fprintf(&r.rewritten, "%s=%s", key, escapedValue)
fmt.Fprintf(&r.rewritten, "%s=%s", field, escapedValue)
return nil
}
}
// Otherwise, keep the original comparison
r.rewritten.WriteString(key)
r.rewritten.WriteString(field)
if ctx.EQUALS() != nil {
r.rewritten.WriteString("=")
@@ -409,8 +413,8 @@ func (r *WhereClauseRewriter) VisitFunctionParamList(ctx *parser.FunctionParamLi
// VisitFunctionParam visits function parameters.
func (r *WhereClauseRewriter) VisitFunctionParam(ctx *parser.FunctionParamContext) any {
if ctx.Key() != nil {
ctx.Key().Accept(r)
if ctx.Field() != nil {
r.rewritten.WriteString(ctx.Field().GetText())
} else if ctx.Value() != nil {
ctx.Value().Accept(r)
} else if ctx.Array() != nil {

View File

@@ -234,6 +234,18 @@ func TestPrepareFiltersV5(t *testing.T) {
expected: "(error_details EXISTS) AND service.name='serviceA'",
description: "Should preserve EXISTS operator",
},
{
name: "exact_field_label_replacement",
labels: map[string]string{
"deployment.environment": "production",
},
whereClause: "exact(deployment.environment) = 'staging'",
groupByItems: []qbtypes.GroupByKey{
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "deployment.environment"}},
},
expected: "exact(deployment.environment)='production'",
description: "Should keep the exact wrapper when replacing a grouped label",
},
{
name: "empty_where_clause_with_labels",

View File

@@ -23,13 +23,13 @@ func TestSource(t *testing.T) {
err := json.Unmarshal(buf.Bytes(), &m)
require.NoError(t, err)
assert.Contains(t, m, "code.filepath")
assert.Contains(t, m, "code.function")
assert.Contains(t, m, "code.lineno")
assert.Contains(t, m, "code.file.path")
assert.Contains(t, m, "code.function.name")
assert.Contains(t, m, "code.line.number")
assert.Contains(t, m["code.filepath"], "source_test.go")
assert.Contains(t, m["code.function"], "TestSource")
assert.NotZero(t, m["code.lineno"])
assert.Contains(t, m["code.file.path"], "source_test.go")
assert.Contains(t, m["code.function.name"], "TestSource")
assert.NotZero(t, m["code.line.number"])
// Ensure the nested "source" key is not present.
assert.NotContains(t, m, "source")

View File

@@ -8,4 +8,7 @@ type Handler interface {
// Gets the fields values for the given field value selector
GetFieldsValues(http.ResponseWriter, *http.Request)
// Gets services that still emit only historical semantic-convention names.
GetSemconvMigrationReport(http.ResponseWriter, *http.Request)
}

View File

@@ -2,6 +2,7 @@ package implfields
import (
"net/http"
"time"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/http/binding"
@@ -16,6 +17,43 @@ type handler struct {
telemetryMetadataStore telemetrytypes.MetadataStore
}
func (handler *handler) GetSemconvMigrationReport(rw http.ResponseWriter, req *http.Request) {
ctx := req.Context()
var params telemetrytypes.PostableSemconvMigrationReportParams
if err := binding.Query.BindQuery(req.URL.Query(), &params); err != nil {
render.Error(rw, err)
return
}
now := time.Now()
if params.EndUnixMilli == 0 {
params.EndUnixMilli = now.UnixMilli()
}
if params.StartUnixMilli == 0 {
params.StartUnixMilli = now.Add(-24 * time.Hour).UnixMilli()
}
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
report, err := handler.telemetryMetadataStore.GetSemconvMigrationReport(
ctx,
valuer.MustNewUUID(claims.OrgID),
params.StartUnixMilli,
params.EndUnixMilli,
)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, report)
}
func NewHandler(settings factory.ProviderSettings, telemetryMetadataStore telemetrytypes.MetadataStore) fields.Handler {
return &handler{
telemetryMetadataStore: telemetryMetadataStore,

File diff suppressed because one or more lines are too long

View File

@@ -25,12 +25,13 @@ HAS=24
HASANY=25
HASALL=26
SEARCH=27
BOOL=28
NUMBER=29
QUOTED_TEXT=30
KEY=31
WS=32
FREETEXT=33
EXACT=28
BOOL=29
NUMBER=30
QUOTED_TEXT=31
KEY=32
WS=33
FREETEXT=34
'('=1
')'=2
'['=3

File diff suppressed because one or more lines are too long

View File

@@ -25,12 +25,13 @@ HAS=24
HASANY=25
HASALL=26
SEARCH=27
BOOL=28
NUMBER=29
QUOTED_TEXT=30
KEY=31
WS=32
FREETEXT=33
EXACT=28
BOOL=29
NUMBER=30
QUOTED_TEXT=31
KEY=32
WS=33
FREETEXT=34
'('=1
')'=2
'['=3

View File

@@ -1,7 +1,6 @@
// Code generated from FilterQuery.g4 by ANTLR 4.13.2. DO NOT EDIT.
package parser // FilterQuery
import "github.com/antlr4-go/antlr/v4"
// BaseFilterQueryListener is a complete listener for a parse tree produced by FilterQueryParser.
@@ -128,3 +127,15 @@ func (s *BaseFilterQueryListener) EnterKey(ctx *KeyContext) {}
// ExitKey is called when production key is exited.
func (s *BaseFilterQueryListener) ExitKey(ctx *KeyContext) {}
// EnterField is called when production field is entered.
func (s *BaseFilterQueryListener) EnterField(ctx *FieldContext) {}
// ExitField is called when production field is exited.
func (s *BaseFilterQueryListener) ExitField(ctx *FieldContext) {}
// EnterExactCall is called when production exactCall is entered.
func (s *BaseFilterQueryListener) EnterExactCall(ctx *ExactCallContext) {}
// ExitExactCall is called when production exactCall is exited.
func (s *BaseFilterQueryListener) ExitExactCall(ctx *ExactCallContext) {}

View File

@@ -1,7 +1,6 @@
// Code generated from FilterQuery.g4 by ANTLR 4.13.2. DO NOT EDIT.
package parser // FilterQuery
import "github.com/antlr4-go/antlr/v4"
type BaseFilterQueryVisitor struct {
@@ -79,3 +78,11 @@ func (v *BaseFilterQueryVisitor) VisitValue(ctx *ValueContext) interface{} {
func (v *BaseFilterQueryVisitor) VisitKey(ctx *KeyContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseFilterQueryVisitor) VisitField(ctx *FieldContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseFilterQueryVisitor) VisitExactCall(ctx *ExactCallContext) interface{} {
return v.VisitChildren(ctx)
}

View File

@@ -50,66 +50,69 @@ func filterquerylexerLexerInit() {
"", "LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS",
"NEQ", "LT", "LE", "GT", "GE", "LIKE", "ILIKE", "BETWEEN", "EXISTS",
"REGEXP", "CONTAINS", "IN", "NOT", "AND", "OR", "HASTOKEN", "HAS", "HASANY",
"HASALL", "SEARCH", "BOOL", "NUMBER", "QUOTED_TEXT", "KEY", "WS", "FREETEXT",
"HASALL", "SEARCH", "EXACT", "BOOL", "NUMBER", "QUOTED_TEXT", "KEY",
"WS", "FREETEXT",
}
staticData.RuleNames = []string{
"LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS",
"NEQ", "LT", "LE", "GT", "GE", "LIKE", "ILIKE", "BETWEEN", "EXISTS",
"REGEXP", "CONTAINS", "IN", "NOT", "AND", "OR", "HASTOKEN", "HAS", "HASANY",
"HASALL", "SEARCH", "BOOL", "SIGN", "NUMBER", "QUOTED_TEXT", "SEGMENT",
"EMPTY_BRACKS", "OLD_JSON_BRACKS", "KEY", "WS", "DIGIT", "FREETEXT",
"HASALL", "SEARCH", "EXACT", "BOOL", "SIGN", "NUMBER", "QUOTED_TEXT",
"SEGMENT", "EMPTY_BRACKS", "OLD_JSON_BRACKS", "KEY", "WS", "DIGIT",
"FREETEXT",
}
staticData.PredictionContextCache = antlr.NewPredictionContextCache()
staticData.serializedATN = []int32{
4, 0, 33, 329, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
4, 0, 34, 337, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2,
10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15,
7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7,
20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25,
2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2,
31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36,
7, 36, 2, 37, 7, 37, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1,
4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 91, 8, 5, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7,
1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11,
1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1,
13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15,
1, 15, 1, 15, 1, 15, 1, 15, 3, 15, 134, 8, 15, 1, 16, 1, 16, 1, 16, 1,
16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17,
1, 17, 3, 17, 151, 8, 17, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1,
19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22,
1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1,
24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25,
1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1,
27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 210,
8, 27, 1, 28, 1, 28, 1, 29, 3, 29, 215, 8, 29, 1, 29, 4, 29, 218, 8, 29,
11, 29, 12, 29, 219, 1, 29, 1, 29, 5, 29, 224, 8, 29, 10, 29, 12, 29, 227,
9, 29, 3, 29, 229, 8, 29, 1, 29, 1, 29, 3, 29, 233, 8, 29, 1, 29, 4, 29,
236, 8, 29, 11, 29, 12, 29, 237, 3, 29, 240, 8, 29, 1, 29, 3, 29, 243,
8, 29, 1, 29, 1, 29, 4, 29, 247, 8, 29, 11, 29, 12, 29, 248, 1, 29, 1,
29, 3, 29, 253, 8, 29, 1, 29, 4, 29, 256, 8, 29, 11, 29, 12, 29, 257, 3,
29, 260, 8, 29, 3, 29, 262, 8, 29, 1, 30, 1, 30, 1, 30, 1, 30, 5, 30, 268,
8, 30, 10, 30, 12, 30, 271, 9, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 5,
30, 278, 8, 30, 10, 30, 12, 30, 281, 9, 30, 1, 30, 3, 30, 284, 8, 30, 1,
31, 1, 31, 5, 31, 288, 8, 31, 10, 31, 12, 31, 291, 9, 31, 1, 32, 1, 32,
1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1,
34, 1, 34, 4, 34, 307, 8, 34, 11, 34, 12, 34, 308, 5, 34, 311, 8, 34, 10,
34, 12, 34, 314, 9, 34, 1, 35, 4, 35, 317, 8, 35, 11, 35, 12, 35, 318,
1, 35, 1, 35, 1, 36, 1, 36, 1, 37, 4, 37, 326, 8, 37, 11, 37, 12, 37, 327,
0, 0, 38, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19,
10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37,
19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55,
28, 57, 0, 59, 29, 61, 30, 63, 0, 65, 0, 67, 0, 69, 31, 71, 32, 73, 0,
75, 33, 1, 0, 29, 2, 0, 76, 76, 108, 108, 2, 0, 73, 73, 105, 105, 2, 0,
75, 75, 107, 107, 2, 0, 69, 69, 101, 101, 2, 0, 66, 66, 98, 98, 2, 0, 84,
84, 116, 116, 2, 0, 87, 87, 119, 119, 2, 0, 78, 78, 110, 110, 2, 0, 88,
88, 120, 120, 2, 0, 83, 83, 115, 115, 2, 0, 82, 82, 114, 114, 2, 0, 71,
71, 103, 103, 2, 0, 80, 80, 112, 112, 2, 0, 67, 67, 99, 99, 2, 0, 79, 79,
111, 111, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 72, 72, 104,
104, 2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117, 117, 2, 0, 70, 70, 102,
102, 2, 0, 43, 43, 45, 45, 2, 0, 34, 34, 92, 92, 2, 0, 39, 39, 92, 92,
4, 0, 35, 36, 64, 90, 95, 95, 97, 123, 7, 0, 35, 36, 45, 45, 47, 58, 64,
90, 95, 95, 97, 123, 125, 125, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57,
8, 0, 9, 10, 13, 13, 32, 34, 39, 41, 44, 44, 60, 62, 91, 91, 93, 93, 353,
7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2,
1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 93, 8, 5, 1, 6, 1, 6, 1,
6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11,
1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1,
13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14,
1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 3, 15, 136, 8, 15, 1, 16, 1,
16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17,
1, 17, 1, 17, 1, 17, 3, 17, 153, 8, 17, 1, 18, 1, 18, 1, 18, 1, 19, 1,
19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 22,
1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1,
23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25,
1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1,
26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28,
1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 3, 28, 218, 8, 28, 1, 29, 1,
29, 1, 30, 3, 30, 223, 8, 30, 1, 30, 4, 30, 226, 8, 30, 11, 30, 12, 30,
227, 1, 30, 1, 30, 5, 30, 232, 8, 30, 10, 30, 12, 30, 235, 9, 30, 3, 30,
237, 8, 30, 1, 30, 1, 30, 3, 30, 241, 8, 30, 1, 30, 4, 30, 244, 8, 30,
11, 30, 12, 30, 245, 3, 30, 248, 8, 30, 1, 30, 3, 30, 251, 8, 30, 1, 30,
1, 30, 4, 30, 255, 8, 30, 11, 30, 12, 30, 256, 1, 30, 1, 30, 3, 30, 261,
8, 30, 1, 30, 4, 30, 264, 8, 30, 11, 30, 12, 30, 265, 3, 30, 268, 8, 30,
3, 30, 270, 8, 30, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 276, 8, 31, 10, 31,
12, 31, 279, 9, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 286, 8, 31,
10, 31, 12, 31, 289, 9, 31, 1, 31, 3, 31, 292, 8, 31, 1, 32, 1, 32, 5,
32, 296, 8, 32, 10, 32, 12, 32, 299, 9, 32, 1, 33, 1, 33, 1, 33, 1, 34,
1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 4,
35, 315, 8, 35, 11, 35, 12, 35, 316, 5, 35, 319, 8, 35, 10, 35, 12, 35,
322, 9, 35, 1, 36, 4, 36, 325, 8, 36, 11, 36, 12, 36, 326, 1, 36, 1, 36,
1, 37, 1, 37, 1, 38, 4, 38, 334, 8, 38, 11, 38, 12, 38, 335, 0, 0, 39,
1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11,
23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20,
41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29,
59, 0, 61, 30, 63, 31, 65, 0, 67, 0, 69, 0, 71, 32, 73, 33, 75, 0, 77,
34, 1, 0, 29, 2, 0, 76, 76, 108, 108, 2, 0, 73, 73, 105, 105, 2, 0, 75,
75, 107, 107, 2, 0, 69, 69, 101, 101, 2, 0, 66, 66, 98, 98, 2, 0, 84, 84,
116, 116, 2, 0, 87, 87, 119, 119, 2, 0, 78, 78, 110, 110, 2, 0, 88, 88,
120, 120, 2, 0, 83, 83, 115, 115, 2, 0, 82, 82, 114, 114, 2, 0, 71, 71,
103, 103, 2, 0, 80, 80, 112, 112, 2, 0, 67, 67, 99, 99, 2, 0, 79, 79, 111,
111, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 72, 72, 104, 104,
2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117, 117, 2, 0, 70, 70, 102, 102,
2, 0, 43, 43, 45, 45, 2, 0, 34, 34, 92, 92, 2, 0, 39, 39, 92, 92, 4, 0,
35, 36, 64, 90, 95, 95, 97, 123, 7, 0, 35, 36, 45, 45, 47, 58, 64, 90,
95, 95, 97, 123, 125, 125, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 8,
0, 9, 10, 13, 13, 32, 34, 39, 41, 44, 44, 60, 62, 91, 91, 93, 93, 361,
0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0,
0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0,
0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0,
@@ -117,107 +120,110 @@ func filterquerylexerLexerInit() {
0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39,
1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0,
47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0,
0, 55, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 69, 1, 0, 0,
0, 0, 71, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 1, 77, 1, 0, 0, 0, 3, 79, 1, 0,
0, 0, 5, 81, 1, 0, 0, 0, 7, 83, 1, 0, 0, 0, 9, 85, 1, 0, 0, 0, 11, 90,
1, 0, 0, 0, 13, 92, 1, 0, 0, 0, 15, 95, 1, 0, 0, 0, 17, 98, 1, 0, 0, 0,
19, 100, 1, 0, 0, 0, 21, 103, 1, 0, 0, 0, 23, 105, 1, 0, 0, 0, 25, 108,
1, 0, 0, 0, 27, 113, 1, 0, 0, 0, 29, 119, 1, 0, 0, 0, 31, 127, 1, 0, 0,
0, 33, 135, 1, 0, 0, 0, 35, 142, 1, 0, 0, 0, 37, 152, 1, 0, 0, 0, 39, 155,
1, 0, 0, 0, 41, 159, 1, 0, 0, 0, 43, 163, 1, 0, 0, 0, 45, 166, 1, 0, 0,
0, 47, 175, 1, 0, 0, 0, 49, 179, 1, 0, 0, 0, 51, 186, 1, 0, 0, 0, 53, 193,
1, 0, 0, 0, 55, 209, 1, 0, 0, 0, 57, 211, 1, 0, 0, 0, 59, 261, 1, 0, 0,
0, 61, 283, 1, 0, 0, 0, 63, 285, 1, 0, 0, 0, 65, 292, 1, 0, 0, 0, 67, 295,
1, 0, 0, 0, 69, 299, 1, 0, 0, 0, 71, 316, 1, 0, 0, 0, 73, 322, 1, 0, 0,
0, 75, 325, 1, 0, 0, 0, 77, 78, 5, 40, 0, 0, 78, 2, 1, 0, 0, 0, 79, 80,
5, 41, 0, 0, 80, 4, 1, 0, 0, 0, 81, 82, 5, 91, 0, 0, 82, 6, 1, 0, 0, 0,
83, 84, 5, 93, 0, 0, 84, 8, 1, 0, 0, 0, 85, 86, 5, 44, 0, 0, 86, 10, 1,
0, 0, 0, 87, 91, 5, 61, 0, 0, 88, 89, 5, 61, 0, 0, 89, 91, 5, 61, 0, 0,
90, 87, 1, 0, 0, 0, 90, 88, 1, 0, 0, 0, 91, 12, 1, 0, 0, 0, 92, 93, 5,
33, 0, 0, 93, 94, 5, 61, 0, 0, 94, 14, 1, 0, 0, 0, 95, 96, 5, 60, 0, 0,
96, 97, 5, 62, 0, 0, 97, 16, 1, 0, 0, 0, 98, 99, 5, 60, 0, 0, 99, 18, 1,
0, 0, 0, 100, 101, 5, 60, 0, 0, 101, 102, 5, 61, 0, 0, 102, 20, 1, 0, 0,
0, 103, 104, 5, 62, 0, 0, 104, 22, 1, 0, 0, 0, 105, 106, 5, 62, 0, 0, 106,
107, 5, 61, 0, 0, 107, 24, 1, 0, 0, 0, 108, 109, 7, 0, 0, 0, 109, 110,
7, 1, 0, 0, 110, 111, 7, 2, 0, 0, 111, 112, 7, 3, 0, 0, 112, 26, 1, 0,
0, 0, 113, 114, 7, 1, 0, 0, 114, 115, 7, 0, 0, 0, 115, 116, 7, 1, 0, 0,
116, 117, 7, 2, 0, 0, 117, 118, 7, 3, 0, 0, 118, 28, 1, 0, 0, 0, 119, 120,
7, 4, 0, 0, 120, 121, 7, 3, 0, 0, 121, 122, 7, 5, 0, 0, 122, 123, 7, 6,
0, 0, 123, 124, 7, 3, 0, 0, 124, 125, 7, 3, 0, 0, 125, 126, 7, 7, 0, 0,
126, 30, 1, 0, 0, 0, 127, 128, 7, 3, 0, 0, 128, 129, 7, 8, 0, 0, 129, 130,
7, 1, 0, 0, 130, 131, 7, 9, 0, 0, 131, 133, 7, 5, 0, 0, 132, 134, 7, 9,
0, 0, 133, 132, 1, 0, 0, 0, 133, 134, 1, 0, 0, 0, 134, 32, 1, 0, 0, 0,
135, 136, 7, 10, 0, 0, 136, 137, 7, 3, 0, 0, 137, 138, 7, 11, 0, 0, 138,
139, 7, 3, 0, 0, 139, 140, 7, 8, 0, 0, 140, 141, 7, 12, 0, 0, 141, 34,
1, 0, 0, 0, 142, 143, 7, 13, 0, 0, 143, 144, 7, 14, 0, 0, 144, 145, 7,
7, 0, 0, 145, 146, 7, 5, 0, 0, 146, 147, 7, 15, 0, 0, 147, 148, 7, 1, 0,
0, 148, 150, 7, 7, 0, 0, 149, 151, 7, 9, 0, 0, 150, 149, 1, 0, 0, 0, 150,
151, 1, 0, 0, 0, 151, 36, 1, 0, 0, 0, 152, 153, 7, 1, 0, 0, 153, 154, 7,
7, 0, 0, 154, 38, 1, 0, 0, 0, 155, 156, 7, 7, 0, 0, 156, 157, 7, 14, 0,
0, 157, 158, 7, 5, 0, 0, 158, 40, 1, 0, 0, 0, 159, 160, 7, 15, 0, 0, 160,
161, 7, 7, 0, 0, 161, 162, 7, 16, 0, 0, 162, 42, 1, 0, 0, 0, 163, 164,
7, 14, 0, 0, 164, 165, 7, 10, 0, 0, 165, 44, 1, 0, 0, 0, 166, 167, 7, 17,
0, 0, 167, 168, 7, 15, 0, 0, 168, 169, 7, 9, 0, 0, 169, 170, 7, 5, 0, 0,
170, 171, 7, 14, 0, 0, 171, 172, 7, 2, 0, 0, 172, 173, 7, 3, 0, 0, 173,
174, 7, 7, 0, 0, 174, 46, 1, 0, 0, 0, 175, 176, 7, 17, 0, 0, 176, 177,
7, 15, 0, 0, 177, 178, 7, 9, 0, 0, 178, 48, 1, 0, 0, 0, 179, 180, 7, 17,
0, 0, 180, 181, 7, 15, 0, 0, 181, 182, 7, 9, 0, 0, 182, 183, 7, 15, 0,
0, 183, 184, 7, 7, 0, 0, 184, 185, 7, 18, 0, 0, 185, 50, 1, 0, 0, 0, 186,
187, 7, 17, 0, 0, 187, 188, 7, 15, 0, 0, 188, 189, 7, 9, 0, 0, 189, 190,
7, 15, 0, 0, 190, 191, 7, 0, 0, 0, 191, 192, 7, 0, 0, 0, 192, 52, 1, 0,
0, 0, 193, 194, 7, 9, 0, 0, 194, 195, 7, 3, 0, 0, 195, 196, 7, 15, 0, 0,
196, 197, 7, 10, 0, 0, 197, 198, 7, 13, 0, 0, 198, 199, 7, 17, 0, 0, 199,
54, 1, 0, 0, 0, 200, 201, 7, 5, 0, 0, 201, 202, 7, 10, 0, 0, 202, 203,
7, 19, 0, 0, 203, 210, 7, 3, 0, 0, 204, 205, 7, 20, 0, 0, 205, 206, 7,
15, 0, 0, 206, 207, 7, 0, 0, 0, 207, 208, 7, 9, 0, 0, 208, 210, 7, 3, 0,
0, 209, 200, 1, 0, 0, 0, 209, 204, 1, 0, 0, 0, 210, 56, 1, 0, 0, 0, 211,
212, 7, 21, 0, 0, 212, 58, 1, 0, 0, 0, 213, 215, 3, 57, 28, 0, 214, 213,
1, 0, 0, 0, 214, 215, 1, 0, 0, 0, 215, 217, 1, 0, 0, 0, 216, 218, 3, 73,
36, 0, 217, 216, 1, 0, 0, 0, 218, 219, 1, 0, 0, 0, 219, 217, 1, 0, 0, 0,
219, 220, 1, 0, 0, 0, 220, 228, 1, 0, 0, 0, 221, 225, 5, 46, 0, 0, 222,
224, 3, 73, 36, 0, 223, 222, 1, 0, 0, 0, 224, 227, 1, 0, 0, 0, 225, 223,
1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 229, 1, 0, 0, 0, 227, 225, 1, 0,
0, 0, 228, 221, 1, 0, 0, 0, 228, 229, 1, 0, 0, 0, 229, 239, 1, 0, 0, 0,
230, 232, 7, 3, 0, 0, 231, 233, 3, 57, 28, 0, 232, 231, 1, 0, 0, 0, 232,
233, 1, 0, 0, 0, 233, 235, 1, 0, 0, 0, 234, 236, 3, 73, 36, 0, 235, 234,
1, 0, 0, 0, 236, 237, 1, 0, 0, 0, 237, 235, 1, 0, 0, 0, 237, 238, 1, 0,
0, 0, 238, 240, 1, 0, 0, 0, 239, 230, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0,
240, 262, 1, 0, 0, 0, 241, 243, 3, 57, 28, 0, 242, 241, 1, 0, 0, 0, 242,
243, 1, 0, 0, 0, 243, 244, 1, 0, 0, 0, 244, 246, 5, 46, 0, 0, 245, 247,
3, 73, 36, 0, 246, 245, 1, 0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 246, 1,
0, 0, 0, 248, 249, 1, 0, 0, 0, 249, 259, 1, 0, 0, 0, 250, 252, 7, 3, 0,
0, 251, 253, 3, 57, 28, 0, 252, 251, 1, 0, 0, 0, 252, 253, 1, 0, 0, 0,
253, 255, 1, 0, 0, 0, 254, 256, 3, 73, 36, 0, 255, 254, 1, 0, 0, 0, 256,
257, 1, 0, 0, 0, 257, 255, 1, 0, 0, 0, 257, 258, 1, 0, 0, 0, 258, 260,
1, 0, 0, 0, 259, 250, 1, 0, 0, 0, 259, 260, 1, 0, 0, 0, 260, 262, 1, 0,
0, 0, 261, 214, 1, 0, 0, 0, 261, 242, 1, 0, 0, 0, 262, 60, 1, 0, 0, 0,
263, 269, 5, 34, 0, 0, 264, 268, 8, 22, 0, 0, 265, 266, 5, 92, 0, 0, 266,
268, 9, 0, 0, 0, 267, 264, 1, 0, 0, 0, 267, 265, 1, 0, 0, 0, 268, 271,
1, 0, 0, 0, 269, 267, 1, 0, 0, 0, 269, 270, 1, 0, 0, 0, 270, 272, 1, 0,
0, 0, 271, 269, 1, 0, 0, 0, 272, 284, 5, 34, 0, 0, 273, 279, 5, 39, 0,
0, 274, 278, 8, 23, 0, 0, 275, 276, 5, 92, 0, 0, 276, 278, 9, 0, 0, 0,
277, 274, 1, 0, 0, 0, 277, 275, 1, 0, 0, 0, 278, 281, 1, 0, 0, 0, 279,
277, 1, 0, 0, 0, 279, 280, 1, 0, 0, 0, 280, 282, 1, 0, 0, 0, 281, 279,
1, 0, 0, 0, 282, 284, 5, 39, 0, 0, 283, 263, 1, 0, 0, 0, 283, 273, 1, 0,
0, 0, 284, 62, 1, 0, 0, 0, 285, 289, 7, 24, 0, 0, 286, 288, 7, 25, 0, 0,
287, 286, 1, 0, 0, 0, 288, 291, 1, 0, 0, 0, 289, 287, 1, 0, 0, 0, 289,
290, 1, 0, 0, 0, 290, 64, 1, 0, 0, 0, 291, 289, 1, 0, 0, 0, 292, 293, 5,
91, 0, 0, 293, 294, 5, 93, 0, 0, 294, 66, 1, 0, 0, 0, 295, 296, 5, 91,
0, 0, 296, 297, 5, 42, 0, 0, 297, 298, 5, 93, 0, 0, 298, 68, 1, 0, 0, 0,
299, 312, 3, 63, 31, 0, 300, 301, 5, 46, 0, 0, 301, 311, 3, 63, 31, 0,
302, 311, 3, 65, 32, 0, 303, 311, 3, 67, 33, 0, 304, 306, 5, 46, 0, 0,
305, 307, 3, 73, 36, 0, 306, 305, 1, 0, 0, 0, 307, 308, 1, 0, 0, 0, 308,
306, 1, 0, 0, 0, 308, 309, 1, 0, 0, 0, 309, 311, 1, 0, 0, 0, 310, 300,
1, 0, 0, 0, 310, 302, 1, 0, 0, 0, 310, 303, 1, 0, 0, 0, 310, 304, 1, 0,
0, 0, 311, 314, 1, 0, 0, 0, 312, 310, 1, 0, 0, 0, 312, 313, 1, 0, 0, 0,
313, 70, 1, 0, 0, 0, 314, 312, 1, 0, 0, 0, 315, 317, 7, 26, 0, 0, 316,
315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 316, 1, 0, 0, 0, 318, 319,
1, 0, 0, 0, 319, 320, 1, 0, 0, 0, 320, 321, 6, 35, 0, 0, 321, 72, 1, 0,
0, 0, 322, 323, 7, 27, 0, 0, 323, 74, 1, 0, 0, 0, 324, 326, 8, 28, 0, 0,
325, 324, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0, 327, 325, 1, 0, 0, 0, 327,
328, 1, 0, 0, 0, 328, 76, 1, 0, 0, 0, 29, 0, 90, 133, 150, 209, 214, 219,
225, 228, 232, 237, 239, 242, 248, 252, 257, 259, 261, 267, 269, 277, 279,
283, 289, 308, 310, 312, 318, 327, 1, 6, 0, 0,
0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0,
0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 1, 79, 1, 0,
0, 0, 3, 81, 1, 0, 0, 0, 5, 83, 1, 0, 0, 0, 7, 85, 1, 0, 0, 0, 9, 87, 1,
0, 0, 0, 11, 92, 1, 0, 0, 0, 13, 94, 1, 0, 0, 0, 15, 97, 1, 0, 0, 0, 17,
100, 1, 0, 0, 0, 19, 102, 1, 0, 0, 0, 21, 105, 1, 0, 0, 0, 23, 107, 1,
0, 0, 0, 25, 110, 1, 0, 0, 0, 27, 115, 1, 0, 0, 0, 29, 121, 1, 0, 0, 0,
31, 129, 1, 0, 0, 0, 33, 137, 1, 0, 0, 0, 35, 144, 1, 0, 0, 0, 37, 154,
1, 0, 0, 0, 39, 157, 1, 0, 0, 0, 41, 161, 1, 0, 0, 0, 43, 165, 1, 0, 0,
0, 45, 168, 1, 0, 0, 0, 47, 177, 1, 0, 0, 0, 49, 181, 1, 0, 0, 0, 51, 188,
1, 0, 0, 0, 53, 195, 1, 0, 0, 0, 55, 202, 1, 0, 0, 0, 57, 217, 1, 0, 0,
0, 59, 219, 1, 0, 0, 0, 61, 269, 1, 0, 0, 0, 63, 291, 1, 0, 0, 0, 65, 293,
1, 0, 0, 0, 67, 300, 1, 0, 0, 0, 69, 303, 1, 0, 0, 0, 71, 307, 1, 0, 0,
0, 73, 324, 1, 0, 0, 0, 75, 330, 1, 0, 0, 0, 77, 333, 1, 0, 0, 0, 79, 80,
5, 40, 0, 0, 80, 2, 1, 0, 0, 0, 81, 82, 5, 41, 0, 0, 82, 4, 1, 0, 0, 0,
83, 84, 5, 91, 0, 0, 84, 6, 1, 0, 0, 0, 85, 86, 5, 93, 0, 0, 86, 8, 1,
0, 0, 0, 87, 88, 5, 44, 0, 0, 88, 10, 1, 0, 0, 0, 89, 93, 5, 61, 0, 0,
90, 91, 5, 61, 0, 0, 91, 93, 5, 61, 0, 0, 92, 89, 1, 0, 0, 0, 92, 90, 1,
0, 0, 0, 93, 12, 1, 0, 0, 0, 94, 95, 5, 33, 0, 0, 95, 96, 5, 61, 0, 0,
96, 14, 1, 0, 0, 0, 97, 98, 5, 60, 0, 0, 98, 99, 5, 62, 0, 0, 99, 16, 1,
0, 0, 0, 100, 101, 5, 60, 0, 0, 101, 18, 1, 0, 0, 0, 102, 103, 5, 60, 0,
0, 103, 104, 5, 61, 0, 0, 104, 20, 1, 0, 0, 0, 105, 106, 5, 62, 0, 0, 106,
22, 1, 0, 0, 0, 107, 108, 5, 62, 0, 0, 108, 109, 5, 61, 0, 0, 109, 24,
1, 0, 0, 0, 110, 111, 7, 0, 0, 0, 111, 112, 7, 1, 0, 0, 112, 113, 7, 2,
0, 0, 113, 114, 7, 3, 0, 0, 114, 26, 1, 0, 0, 0, 115, 116, 7, 1, 0, 0,
116, 117, 7, 0, 0, 0, 117, 118, 7, 1, 0, 0, 118, 119, 7, 2, 0, 0, 119,
120, 7, 3, 0, 0, 120, 28, 1, 0, 0, 0, 121, 122, 7, 4, 0, 0, 122, 123, 7,
3, 0, 0, 123, 124, 7, 5, 0, 0, 124, 125, 7, 6, 0, 0, 125, 126, 7, 3, 0,
0, 126, 127, 7, 3, 0, 0, 127, 128, 7, 7, 0, 0, 128, 30, 1, 0, 0, 0, 129,
130, 7, 3, 0, 0, 130, 131, 7, 8, 0, 0, 131, 132, 7, 1, 0, 0, 132, 133,
7, 9, 0, 0, 133, 135, 7, 5, 0, 0, 134, 136, 7, 9, 0, 0, 135, 134, 1, 0,
0, 0, 135, 136, 1, 0, 0, 0, 136, 32, 1, 0, 0, 0, 137, 138, 7, 10, 0, 0,
138, 139, 7, 3, 0, 0, 139, 140, 7, 11, 0, 0, 140, 141, 7, 3, 0, 0, 141,
142, 7, 8, 0, 0, 142, 143, 7, 12, 0, 0, 143, 34, 1, 0, 0, 0, 144, 145,
7, 13, 0, 0, 145, 146, 7, 14, 0, 0, 146, 147, 7, 7, 0, 0, 147, 148, 7,
5, 0, 0, 148, 149, 7, 15, 0, 0, 149, 150, 7, 1, 0, 0, 150, 152, 7, 7, 0,
0, 151, 153, 7, 9, 0, 0, 152, 151, 1, 0, 0, 0, 152, 153, 1, 0, 0, 0, 153,
36, 1, 0, 0, 0, 154, 155, 7, 1, 0, 0, 155, 156, 7, 7, 0, 0, 156, 38, 1,
0, 0, 0, 157, 158, 7, 7, 0, 0, 158, 159, 7, 14, 0, 0, 159, 160, 7, 5, 0,
0, 160, 40, 1, 0, 0, 0, 161, 162, 7, 15, 0, 0, 162, 163, 7, 7, 0, 0, 163,
164, 7, 16, 0, 0, 164, 42, 1, 0, 0, 0, 165, 166, 7, 14, 0, 0, 166, 167,
7, 10, 0, 0, 167, 44, 1, 0, 0, 0, 168, 169, 7, 17, 0, 0, 169, 170, 7, 15,
0, 0, 170, 171, 7, 9, 0, 0, 171, 172, 7, 5, 0, 0, 172, 173, 7, 14, 0, 0,
173, 174, 7, 2, 0, 0, 174, 175, 7, 3, 0, 0, 175, 176, 7, 7, 0, 0, 176,
46, 1, 0, 0, 0, 177, 178, 7, 17, 0, 0, 178, 179, 7, 15, 0, 0, 179, 180,
7, 9, 0, 0, 180, 48, 1, 0, 0, 0, 181, 182, 7, 17, 0, 0, 182, 183, 7, 15,
0, 0, 183, 184, 7, 9, 0, 0, 184, 185, 7, 15, 0, 0, 185, 186, 7, 7, 0, 0,
186, 187, 7, 18, 0, 0, 187, 50, 1, 0, 0, 0, 188, 189, 7, 17, 0, 0, 189,
190, 7, 15, 0, 0, 190, 191, 7, 9, 0, 0, 191, 192, 7, 15, 0, 0, 192, 193,
7, 0, 0, 0, 193, 194, 7, 0, 0, 0, 194, 52, 1, 0, 0, 0, 195, 196, 7, 9,
0, 0, 196, 197, 7, 3, 0, 0, 197, 198, 7, 15, 0, 0, 198, 199, 7, 10, 0,
0, 199, 200, 7, 13, 0, 0, 200, 201, 7, 17, 0, 0, 201, 54, 1, 0, 0, 0, 202,
203, 7, 3, 0, 0, 203, 204, 7, 8, 0, 0, 204, 205, 7, 15, 0, 0, 205, 206,
7, 13, 0, 0, 206, 207, 7, 5, 0, 0, 207, 56, 1, 0, 0, 0, 208, 209, 7, 5,
0, 0, 209, 210, 7, 10, 0, 0, 210, 211, 7, 19, 0, 0, 211, 218, 7, 3, 0,
0, 212, 213, 7, 20, 0, 0, 213, 214, 7, 15, 0, 0, 214, 215, 7, 0, 0, 0,
215, 216, 7, 9, 0, 0, 216, 218, 7, 3, 0, 0, 217, 208, 1, 0, 0, 0, 217,
212, 1, 0, 0, 0, 218, 58, 1, 0, 0, 0, 219, 220, 7, 21, 0, 0, 220, 60, 1,
0, 0, 0, 221, 223, 3, 59, 29, 0, 222, 221, 1, 0, 0, 0, 222, 223, 1, 0,
0, 0, 223, 225, 1, 0, 0, 0, 224, 226, 3, 75, 37, 0, 225, 224, 1, 0, 0,
0, 226, 227, 1, 0, 0, 0, 227, 225, 1, 0, 0, 0, 227, 228, 1, 0, 0, 0, 228,
236, 1, 0, 0, 0, 229, 233, 5, 46, 0, 0, 230, 232, 3, 75, 37, 0, 231, 230,
1, 0, 0, 0, 232, 235, 1, 0, 0, 0, 233, 231, 1, 0, 0, 0, 233, 234, 1, 0,
0, 0, 234, 237, 1, 0, 0, 0, 235, 233, 1, 0, 0, 0, 236, 229, 1, 0, 0, 0,
236, 237, 1, 0, 0, 0, 237, 247, 1, 0, 0, 0, 238, 240, 7, 3, 0, 0, 239,
241, 3, 59, 29, 0, 240, 239, 1, 0, 0, 0, 240, 241, 1, 0, 0, 0, 241, 243,
1, 0, 0, 0, 242, 244, 3, 75, 37, 0, 243, 242, 1, 0, 0, 0, 244, 245, 1,
0, 0, 0, 245, 243, 1, 0, 0, 0, 245, 246, 1, 0, 0, 0, 246, 248, 1, 0, 0,
0, 247, 238, 1, 0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 270, 1, 0, 0, 0, 249,
251, 3, 59, 29, 0, 250, 249, 1, 0, 0, 0, 250, 251, 1, 0, 0, 0, 251, 252,
1, 0, 0, 0, 252, 254, 5, 46, 0, 0, 253, 255, 3, 75, 37, 0, 254, 253, 1,
0, 0, 0, 255, 256, 1, 0, 0, 0, 256, 254, 1, 0, 0, 0, 256, 257, 1, 0, 0,
0, 257, 267, 1, 0, 0, 0, 258, 260, 7, 3, 0, 0, 259, 261, 3, 59, 29, 0,
260, 259, 1, 0, 0, 0, 260, 261, 1, 0, 0, 0, 261, 263, 1, 0, 0, 0, 262,
264, 3, 75, 37, 0, 263, 262, 1, 0, 0, 0, 264, 265, 1, 0, 0, 0, 265, 263,
1, 0, 0, 0, 265, 266, 1, 0, 0, 0, 266, 268, 1, 0, 0, 0, 267, 258, 1, 0,
0, 0, 267, 268, 1, 0, 0, 0, 268, 270, 1, 0, 0, 0, 269, 222, 1, 0, 0, 0,
269, 250, 1, 0, 0, 0, 270, 62, 1, 0, 0, 0, 271, 277, 5, 34, 0, 0, 272,
276, 8, 22, 0, 0, 273, 274, 5, 92, 0, 0, 274, 276, 9, 0, 0, 0, 275, 272,
1, 0, 0, 0, 275, 273, 1, 0, 0, 0, 276, 279, 1, 0, 0, 0, 277, 275, 1, 0,
0, 0, 277, 278, 1, 0, 0, 0, 278, 280, 1, 0, 0, 0, 279, 277, 1, 0, 0, 0,
280, 292, 5, 34, 0, 0, 281, 287, 5, 39, 0, 0, 282, 286, 8, 23, 0, 0, 283,
284, 5, 92, 0, 0, 284, 286, 9, 0, 0, 0, 285, 282, 1, 0, 0, 0, 285, 283,
1, 0, 0, 0, 286, 289, 1, 0, 0, 0, 287, 285, 1, 0, 0, 0, 287, 288, 1, 0,
0, 0, 288, 290, 1, 0, 0, 0, 289, 287, 1, 0, 0, 0, 290, 292, 5, 39, 0, 0,
291, 271, 1, 0, 0, 0, 291, 281, 1, 0, 0, 0, 292, 64, 1, 0, 0, 0, 293, 297,
7, 24, 0, 0, 294, 296, 7, 25, 0, 0, 295, 294, 1, 0, 0, 0, 296, 299, 1,
0, 0, 0, 297, 295, 1, 0, 0, 0, 297, 298, 1, 0, 0, 0, 298, 66, 1, 0, 0,
0, 299, 297, 1, 0, 0, 0, 300, 301, 5, 91, 0, 0, 301, 302, 5, 93, 0, 0,
302, 68, 1, 0, 0, 0, 303, 304, 5, 91, 0, 0, 304, 305, 5, 42, 0, 0, 305,
306, 5, 93, 0, 0, 306, 70, 1, 0, 0, 0, 307, 320, 3, 65, 32, 0, 308, 309,
5, 46, 0, 0, 309, 319, 3, 65, 32, 0, 310, 319, 3, 67, 33, 0, 311, 319,
3, 69, 34, 0, 312, 314, 5, 46, 0, 0, 313, 315, 3, 75, 37, 0, 314, 313,
1, 0, 0, 0, 315, 316, 1, 0, 0, 0, 316, 314, 1, 0, 0, 0, 316, 317, 1, 0,
0, 0, 317, 319, 1, 0, 0, 0, 318, 308, 1, 0, 0, 0, 318, 310, 1, 0, 0, 0,
318, 311, 1, 0, 0, 0, 318, 312, 1, 0, 0, 0, 319, 322, 1, 0, 0, 0, 320,
318, 1, 0, 0, 0, 320, 321, 1, 0, 0, 0, 321, 72, 1, 0, 0, 0, 322, 320, 1,
0, 0, 0, 323, 325, 7, 26, 0, 0, 324, 323, 1, 0, 0, 0, 325, 326, 1, 0, 0,
0, 326, 324, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0, 327, 328, 1, 0, 0, 0, 328,
329, 6, 36, 0, 0, 329, 74, 1, 0, 0, 0, 330, 331, 7, 27, 0, 0, 331, 76,
1, 0, 0, 0, 332, 334, 8, 28, 0, 0, 333, 332, 1, 0, 0, 0, 334, 335, 1, 0,
0, 0, 335, 333, 1, 0, 0, 0, 335, 336, 1, 0, 0, 0, 336, 78, 1, 0, 0, 0,
29, 0, 92, 135, 152, 217, 222, 227, 233, 236, 240, 245, 247, 250, 256,
260, 265, 267, 269, 275, 277, 285, 287, 291, 297, 316, 318, 320, 326, 335,
1, 6, 0, 0,
}
deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
@@ -285,10 +291,11 @@ const (
FilterQueryLexerHASANY = 25
FilterQueryLexerHASALL = 26
FilterQueryLexerSEARCH = 27
FilterQueryLexerBOOL = 28
FilterQueryLexerNUMBER = 29
FilterQueryLexerQUOTED_TEXT = 30
FilterQueryLexerKEY = 31
FilterQueryLexerWS = 32
FilterQueryLexerFREETEXT = 33
FilterQueryLexerEXACT = 28
FilterQueryLexerBOOL = 29
FilterQueryLexerNUMBER = 30
FilterQueryLexerQUOTED_TEXT = 31
FilterQueryLexerKEY = 32
FilterQueryLexerWS = 33
FilterQueryLexerFREETEXT = 34
)

View File

@@ -1,7 +1,6 @@
// Code generated from FilterQuery.g4 by ANTLR 4.13.2. DO NOT EDIT.
package parser // FilterQuery
import "github.com/antlr4-go/antlr/v4"
// FilterQueryListener is a complete listener for a parse tree produced by FilterQueryParser.
@@ -62,6 +61,12 @@ type FilterQueryListener interface {
// EnterKey is called when entering the key production.
EnterKey(c *KeyContext)
// EnterField is called when entering the field production.
EnterField(c *FieldContext)
// EnterExactCall is called when entering the exactCall production.
EnterExactCall(c *ExactCallContext)
// ExitQuery is called when exiting the query production.
ExitQuery(c *QueryContext)
@@ -115,4 +120,10 @@ type FilterQueryListener interface {
// ExitKey is called when exiting the key production.
ExitKey(c *KeyContext)
// ExitField is called when exiting the field production.
ExitField(c *FieldContext)
// ExitExactCall is called when exiting the exactCall production.
ExitExactCall(c *ExactCallContext)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
// Code generated from FilterQuery.g4 by ANTLR 4.13.2. DO NOT EDIT.
package parser // FilterQuery
import "github.com/antlr4-go/antlr/v4"
// A complete Visitor for a parse tree produced by FilterQueryParser.
@@ -61,4 +60,10 @@ type FilterQueryVisitor interface {
// Visit a parse tree produced by FilterQueryParser#key.
VisitKey(ctx *KeyContext) interface{}
// Visit a parse tree produced by FilterQueryParser#field.
VisitField(ctx *FieldContext) interface{}
// Visit a parse tree produced by FilterQueryParser#exactCall.
VisitExactCall(ctx *ExactCallContext) interface{}
}

View File

@@ -146,7 +146,7 @@ func (v *Visitor) VisitPrimary(ctx *grammar.PrimaryContext) any {
}
func (v *Visitor) VisitComparison(ctx *grammar.ComparisonContext) any {
key := strings.TrimSpace(ctx.Key().GetText())
key := strings.TrimSpace(fieldKeyText(ctx.Field()))
operation, ok := v.extractOperation(ctx)
if !ok {
return ""
@@ -512,3 +512,11 @@ func trimQuotes(s string) string {
s = strings.ReplaceAll(s, `\'`, `'`)
return s
}
// fieldKeyText reads the key of a field; exact() carries no meaning here.
func fieldKeyText(field grammar.IFieldContext) string {
if exactCall := field.ExactCall(); exactCall != nil {
return exactCall.Key().GetText()
}
return field.Key().GetText()
}

View File

@@ -116,6 +116,9 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, req *qbtype
// safely assume ms regardless of the resolution the caller sent.
req.Start = querybuilder.ToMilliSecs(req.Start)
req.End = querybuilder.ToMilliSecs(req.End)
// Key resolution rewrites parts of the builder queries, so the caller's
// spelling is read before that.
semconvResolutions := semconvResolutionsForRequest(req)
event := &qbtypes.QBEvent{
Version: "v5",
@@ -164,6 +167,7 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, req *qbtype
qbResp, qbErr := q.run(ctx, orgID, queries, req, steps, event, preseededResults)
if qbResp != nil {
qbResp.QBEvent = event
qbResp.Meta.SemconvResolutions = semconvResolutions
if len(intervalWarnings) != 0 && req.RequestType == qbtypes.RequestTypeTimeSeries {
if qbResp.Warning == nil {
qbResp.Warning = &qbtypes.QueryWarnData{

View File

@@ -0,0 +1,197 @@
package querier
import (
"encoding/json"
"strings"
"unicode"
grammar "github.com/SigNoz/signoz/pkg/parser/filterquery/grammar"
"github.com/SigNoz/signoz/pkg/querybuilder"
"github.com/SigNoz/signoz/pkg/semconv"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/antlr4-go/antlr/v4"
)
// semconvResolutionsForRequest reports the family spellings a query builder
// request names. Raw ClickHouse SQL and PromQL are not rewritten, so they
// report nothing.
func semconvResolutionsForRequest(req *qbtypes.QueryRangeRequest) []qbtypes.SemconvResolution {
if req == nil {
return nil
}
resolutions := make([]qbtypes.SemconvResolution, 0)
seen := make(map[string]struct{})
for _, envelope := range req.CompositeQuery.Queries {
signal, metric, applies := semconvResolutionSignal(envelope.Spec)
if !applies {
continue
}
text, err := semconvResolutionText(envelope.Spec)
if err != nil {
continue
}
for family := range semconv.All() {
if family.Kind() == semconv.KindMetric && signal != telemetrytypes.SignalMetrics {
continue
}
selector := telemetrytypes.FieldKeySelector{Name: family.Current(), Signal: signal, MetricContext: metric}
spellings := semconv.Members(family.Kind(), selector)
if family.Kind() == semconv.KindAttribute && signal == telemetrytypes.SignalMetrics {
spellings = querybuilder.MetricLabelSpellings(selector)
}
if len(spellings) <= 1 {
continue
}
for _, requested := range spellings {
if !containsSemconvName(text, requested) {
continue
}
identity := family.Kind().StringValue() + "\x00" + requested + "\x00" + family.Current()
if _, ok := seen[identity]; ok {
continue
}
seen[identity] = struct{}{}
resolutions = append(resolutions, qbtypes.SemconvResolution{
Requested: requested,
Current: family.Current(),
Members: append([]string{family.Current()}, family.Old()...),
Kind: family.Kind().StringValue(),
})
}
}
}
if len(resolutions) == 0 {
return nil
}
return resolutions
}
// semconvResolutionText renders the spec without the fields that opt out of
// family resolution: exact(key) in expressions and keys with an exact
// fieldResolution.
func semconvResolutionText(spec any) (string, error) {
payload, err := json.Marshal(spec)
if err != nil {
return "", err
}
var document any
if err := json.Unmarshal(payload, &document); err != nil {
return "", err
}
scrubExactFields(document)
payload, err = json.Marshal(document)
if err != nil {
return "", err
}
return string(payload), nil
}
func scrubExactFields(value any) {
switch typed := value.(type) {
case map[string]any:
if resolution, ok := typed["fieldResolution"].(string); ok && resolution == telemetrytypes.FieldResolutionExact.StringValue() {
delete(typed, "name")
}
for key, child := range typed {
if text, ok := child.(string); ok {
typed[key] = scrubExactCalls(text)
continue
}
scrubExactFields(child)
}
case []any:
for _, child := range typed {
scrubExactFields(child)
}
}
}
// scrubExactCalls blanks every exact(key) in an expression.
func scrubExactCalls(input string) string {
lexer := grammar.NewFilterQueryLexer(antlr.NewInputStream(input))
tokens := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)
tokens.Fill()
all := tokens.GetAllTokens()
result := []rune(input)
for index := 0; index+3 < len(all); index++ {
if all[index].GetTokenType() != grammar.FilterQueryLexerEXACT ||
all[index+1].GetTokenType() != grammar.FilterQueryLexerLPAREN ||
all[index+2].GetTokenType() != grammar.FilterQueryLexerKEY ||
all[index+3].GetTokenType() != grammar.FilterQueryLexerRPAREN {
continue
}
start, stop := all[index].GetStart(), all[index+3].GetStop()
if start < 0 || stop >= len(result) {
continue
}
for position := start; position <= stop; position++ {
result[position] = ' '
}
}
return string(result)
}
func semconvResolutionSignal(spec any) (telemetrytypes.Signal, *telemetrytypes.MetricContext, bool) {
switch query := spec.(type) {
case qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation], qbtypes.QueryBuilderTraceOperator:
return telemetrytypes.SignalTraces, nil, true
case qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]:
return telemetrytypes.SignalLogs, nil, true
case qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]:
var metric *telemetrytypes.MetricContext
if len(query.Aggregations) > 0 {
metric = &telemetrytypes.MetricContext{MetricName: query.Aggregations[0].MetricName}
}
return telemetrytypes.SignalMetrics, metric, true
case qbtypes.QueryBuilderJoin:
// A join can hold several signals. The unspecified signal keeps the
// family signal gates in force and admits every applicable family.
return telemetrytypes.SignalUnspecified, nil, true
default:
return telemetrytypes.SignalUnspecified, nil, false
}
}
// containsSemconvName reports whether name occurs in text as a whole key. A
// field context prefix counts as a boundary, so `resource.x` names `x`.
func containsSemconvName(text, name string) bool {
for offset := 0; offset < len(text); {
index := strings.Index(text[offset:], name)
if index < 0 {
return false
}
start := offset + index
end := start + len(name)
if hasSemconvNameStartBoundary(text, start) &&
(end == len(text) || !isSemconvNameRune(rune(text[end]))) {
return true
}
offset = start + 1
}
return false
}
func hasSemconvNameStartBoundary(text string, start int) bool {
if start == 0 || !isSemconvNameRune(rune(text[start-1])) {
return true
}
for _, qualifier := range []string{"resource.", "attribute.", "tag.", "point."} {
qualifierStart := start - len(qualifier)
if qualifierStart >= 0 && text[qualifierStart:start] == qualifier &&
(qualifierStart == 0 || !isSemconvNameRune(rune(text[qualifierStart-1]))) {
return true
}
}
return false
}
func isSemconvNameRune(r rune) bool {
return unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' || r == '.' || r == '-'
}

View File

@@ -0,0 +1,134 @@
package querier
import (
"encoding/json"
"testing"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSemconvResolutionsReportsOldTraceAttribute(t *testing.T) {
req := &qbtypes.QueryRangeRequest{
CompositeQuery: qbtypes.CompositeQuery{Queries: []qbtypes.QueryEnvelope{{
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
Signal: telemetrytypes.SignalTraces,
Filter: &qbtypes.Filter{Expression: "deployment.environment = 'prod'"},
},
}}},
}
assert.Equal(t, []qbtypes.SemconvResolution{{
Requested: "deployment.environment",
Current: "deployment.environment.name",
Members: []string{"deployment.environment.name", "deployment.environment"},
Kind: "attribute",
}}, semconvResolutionsForRequest(req), "old trace attribute should be reported as a family resolution")
}
func TestSemconvResolutionsReportsContextQualifiedOldAttribute(t *testing.T) {
var req qbtypes.QueryRangeRequest
err := json.Unmarshal([]byte(`{
"start": 1,
"end": 2,
"requestType": "raw",
"compositeQuery": {
"queries": [{
"type": "builder_query",
"spec": {
"signal": "traces",
"name": "A",
"filter": {"expression": "resource.deployment.environment EXISTS"}
}
}]
}
}`), &req)
require.NoError(t, err)
assert.Equal(t, []qbtypes.SemconvResolution{{
Requested: "deployment.environment",
Current: "deployment.environment.name",
Members: []string{"deployment.environment.name", "deployment.environment"},
Kind: "attribute",
}}, semconvResolutionsForRequest(&req), "qualified old trace attribute should be reported after request decoding")
}
func TestSemconvResolutionsReportsCurrentLogAttribute(t *testing.T) {
req := &qbtypes.QueryRangeRequest{
CompositeQuery: qbtypes.CompositeQuery{Queries: []qbtypes.QueryEnvelope{{
Spec: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
Signal: telemetrytypes.SignalLogs,
SelectFields: []telemetrytypes.TelemetryFieldKey{{
Name: "deployment.environment.name",
}},
},
}}},
}
assert.Equal(t, []qbtypes.SemconvResolution{{
Requested: "deployment.environment.name",
Current: "deployment.environment.name",
Members: []string{"deployment.environment.name", "deployment.environment"},
Kind: "attribute",
}}, semconvResolutionsForRequest(req), "current log attribute should identify its complete family")
}
func TestSemconvResolutionsReportsSpanMetricResourceLabel(t *testing.T) {
req := &qbtypes.QueryRangeRequest{
CompositeQuery: qbtypes.CompositeQuery{Queries: []qbtypes.QueryEnvelope{{
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
Signal: telemetrytypes.SignalMetrics,
Aggregations: []qbtypes.MetricAggregation{{MetricName: "signoz_calls_total"}},
Filter: &qbtypes.Filter{Expression: "resource_deployment.environment = 'prod'"},
},
}}},
}
assert.Equal(t, []qbtypes.SemconvResolution{{
Requested: "resource_deployment.environment",
Current: "deployment.environment.name",
Members: []string{"deployment.environment.name", "deployment.environment"},
Kind: "attribute",
}}, semconvResolutionsForRequest(req), "a span-metrics resource label is reported under its stored spelling")
}
func TestSemconvResolutionsIgnoresRawSQL(t *testing.T) {
req := &qbtypes.QueryRangeRequest{
CompositeQuery: qbtypes.CompositeQuery{Queries: []qbtypes.QueryEnvelope{{
Spec: qbtypes.ClickHouseQuery{Query: "SELECT attributes_string['deployment.environment']"},
}}},
}
assert.Empty(t, semconvResolutionsForRequest(req), "raw SQL is not rewritten and should not report a resolution")
}
func TestSemconvResolutionsRequiresNameBoundary(t *testing.T) {
req := &qbtypes.QueryRangeRequest{
CompositeQuery: qbtypes.CompositeQuery{Queries: []qbtypes.QueryEnvelope{{
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
Signal: telemetrytypes.SignalTraces,
Filter: &qbtypes.Filter{Expression: "custom.deployment.environment = 'prod'"},
},
}}},
}
assert.Empty(t, semconvResolutionsForRequest(req), "a family name embedded in a larger custom key must not match")
}
func TestSemconvResolutionsForDecodedRequest(t *testing.T) {
var req qbtypes.QueryRangeRequest
require.NoError(t, json.Unmarshal([]byte(`{
"requestType":"raw",
"compositeQuery":{"queries":[{"type":"builder_query","spec":{
"signal":"traces","name":"A","filter":{"expression":"resource.deployment.environment EXISTS"}
}}]}
}`), &req))
assert.Equal(t, []qbtypes.SemconvResolution{{
Requested: "deployment.environment",
Current: "deployment.environment.name",
Members: []string{"deployment.environment.name", "deployment.environment"},
Kind: "attribute",
}}, semconvResolutionsForRequest(&req))
}

View File

@@ -12,6 +12,8 @@ import (
errorsV2 "github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/query-service/model"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/semconv"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
)
func (r *ClickHouseReader) GetQBFilterSuggestionsForLogs(
@@ -248,17 +250,24 @@ func newRankingStrategy() attribRankingStrategy {
// Synonyms of interesting attributes should come next
resourceHierarchy := fingerprint.ResourceHierarchy()
for _, attr := range []string{
for _, current := range []string{
"service.name",
"deployment.environment",
"deployment.environment.name",
"k8s.namespace.name",
"k8s.pod.name",
"k8s.container.name",
"k8s.node.name",
} {
interestingResourceAttrsInDescRank = append(
interestingResourceAttrsInDescRank, resourceHierarchy.Synonyms(attr)...,
)
members := semconv.Members(semconv.KindAttribute, telemetrytypes.FieldKeySelector{
Name: current,
FieldContext: telemetrytypes.FieldContextResource,
})
for _, member := range members {
interestingResourceAttrsInDescRank = append(interestingResourceAttrsInDescRank, member)
interestingResourceAttrsInDescRank = append(
interestingResourceAttrsInDescRank, resourceHierarchy.Synonyms(member)...,
)
}
}
interestingResourceAttrsInAscRank := interestingResourceAttrsInDescRank[:]

View File

@@ -1808,10 +1808,10 @@ func (aH *APIHandler) onboardConsumers(
status = "1"
}
} else if key == "cgroup" {
attribute = "messaging.kafka.consumer.group"
attribute = "messaging.consumer.group.name"
if intValue != 0 {
status = "0"
message = "messaging.kafka.consumer.group attribute is not present in your spans"
message = "messaging.consumer.group.name attribute is not present in your spans"
} else {
status = "1"
}
@@ -1824,10 +1824,10 @@ func (aH *APIHandler) onboardConsumers(
status = "1"
}
} else if key == "clientid" {
attribute = "messaging.client_id"
attribute = "messaging.client.id"
if intValue != 0 {
status = "0"
message = "messaging.client_id attribute is not present in your spans"
message = "messaging.client.id attribute is not present in your spans"
} else {
status = "1"
}

View File

@@ -335,7 +335,7 @@ Response in query range `table` format
"status": "1"
},
{
"attribute": "messaging.client_id",
"attribute": "messaging.client.id",
"error_message": "",
"status": "1"
},
@@ -350,7 +350,7 @@ Response in query range `table` format
"status": "1"
},
{
"attribute": "messaging.kafka.consumer.group",
"attribute": "messaging.consumer.group.name",
"error_message": "",
"status": "1"
},
@@ -392,7 +392,7 @@ Response in query range `table` format
"status": "0"
},
{
"attribute": "messaging.client_id",
"attribute": "messaging.client.id",
"error_message": "",
"status": "1"
},
@@ -407,8 +407,8 @@ Response in query range `table` format
"status": "1"
},
{
"attribute": "messaging.kafka.consumer.group",
"error_message": "messaging.kafka.consumer.group attribute is not present in your spans",
"attribute": "messaging.consumer.group.name",
"error_message": "messaging.consumer.group.name attribute is not present in your spans",
"status": "0"
},
{

View File

@@ -2,8 +2,20 @@ package kafka
import (
"fmt"
"github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/semconvsql"
)
var kafkaSemconvAttributes = []string{
"messaging.destination.name",
"messaging.consumer.group.name",
"messaging.client.id",
}
func resolveKafkaSemconvSQL(query string) string {
return semconvsql.ResolveTraceStringAttributes(query, kafkaSemconvAttributes...)
}
func generateConsumerSQL(start, end int64, topic, partition, consumerGroup, queueType string) string {
timeRange := (end - start) / 1000000000
tsBucketStart := (start / 1000000000) - 1800
@@ -26,7 +38,7 @@ WITH consumer_query AS (
AND attribute_string_messaging$$system = '%s'
AND attributes_string['messaging.destination.name'] = '%s'
AND attributes_string['messaging.destination.partition.id'] = '%s'
AND attributes_string['messaging.kafka.consumer.group'] = '%s'
AND attributes_string['messaging.consumer.group.name'] = '%s'
GROUP BY resource_string_service$$name
)
@@ -41,7 +53,7 @@ FROM
ORDER BY
resource_string_service$$name;
`, start, end, tsBucketStart, tsBucketEnd, queueType, topic, partition, consumerGroup, timeRange)
return query
return resolveKafkaSemconvSQL(query)
}
// S1 landing
@@ -77,7 +89,7 @@ FROM
ORDER BY
topic;
`, start, end, tsBucketStart, tsBucketEnd, queueType, timeRange)
return query
return resolveKafkaSemconvSQL(query)
}
// S1 consumer
@@ -88,7 +100,7 @@ func generateConsumerPartitionLatencySQL(start, end int64, topic, partition, que
query := fmt.Sprintf(`
WITH consumer_pl AS (
SELECT
attributes_string['messaging.kafka.consumer.group'] AS consumer_group,
attributes_string['messaging.consumer.group.name'] AS consumer_group,
resource_string_service$$name,
quantile(0.99)(durationNano) / 1000000 AS p99,
COUNT(*) AS total_requests,
@@ -117,7 +129,7 @@ FROM
ORDER BY
consumer_group;
`, start, end, tsBucketStart, tsBucketEnd, queueType, topic, partition, timeRange)
return query
return resolveKafkaSemconvSQL(query)
}
// S3, producer overview
@@ -153,7 +165,7 @@ SELECT
FROM
producer_latency
`, start, end, tsBucketStart, tsBucketEnd, queueType, timeRange)
return query
return resolveKafkaSemconvSQL(query)
}
// S3, producer topic/service overview
@@ -189,7 +201,7 @@ SELECT
FROM
consumer_latency
`, start, end, tsBucketStart, tsBucketEnd, service, queueType, topic, timeRange)
return query
return resolveKafkaSemconvSQL(query)
}
// S3 consumer overview
@@ -229,7 +241,7 @@ FROM
ORDER BY
topic;
`, start, end, tsBucketStart, tsBucketEnd, queueType, timeRange, timeRange)
return query
return resolveKafkaSemconvSQL(query)
}
// S3 consumer topic/service
@@ -265,7 +277,7 @@ SELECT
FROM
consumer_latency
`, start, end, tsBucketStart, tsBucketEnd, service, queueType, topic, timeRange)
return query
return resolveKafkaSemconvSQL(query)
}
// s4
@@ -315,7 +327,7 @@ GROUP BY
producer_service,
consumer_service
`, start, end, start, end, queueType, queueType, evalTime, evalTime)
return query
return resolveKafkaSemconvSQL(query)
}
func generateProducerSQL(start, end int64, topic, partition, queueType string) string {
@@ -352,7 +364,7 @@ FROM
ORDER BY
resource_string_service$$name;
`, start, end, tsBucketStart, tsBucketEnd, queueType, topic, partition, timeRange)
return query
return resolveKafkaSemconvSQL(query)
}
func generateNetworkLatencyThroughputSQL(start, end int64, consumerGroup, partitionID, queueType string) string {
@@ -361,7 +373,7 @@ func generateNetworkLatencyThroughputSQL(start, end int64, consumerGroup, partit
tsBucketEnd := end / 1000000000
query := fmt.Sprintf(`
SELECT
attributes_string['messaging.client_id'] AS client_id,
attributes_string['messaging.client.id'] AS client_id,
resources_string['service.instance.id'] AS service_instance_id,
resource_string_service$$name AS service_name,
count(*) / %d AS throughput
@@ -373,12 +385,12 @@ WHERE
AND ts_bucket_start <= '%d'
AND kind = 5
AND attribute_string_messaging$$system = '%s'
AND attributes_string['messaging.kafka.consumer.group'] = '%s'
AND attributes_string['messaging.consumer.group.name'] = '%s'
AND attributes_string['messaging.destination.partition.id'] = '%s'
GROUP BY service_name, client_id, service_instance_id
ORDER BY throughput DESC
`, timeRange, start, end, tsBucketStart, tsBucketEnd, queueType, consumerGroup, partitionID)
return query
return resolveKafkaSemconvSQL(query)
}
func onboardProducersSQL(start, end int64, queueType string) string {
@@ -398,7 +410,7 @@ WHERE
AND timestamp <= '%d'
AND ts_bucket_start >= '%d'
AND ts_bucket_start <= '%d';`, queueType, start, end, tsBucketStart, tsBucketEnd)
return query
return resolveKafkaSemconvSQL(query)
}
func onboardConsumerSQL(start, end int64, queueType string) string {
@@ -412,9 +424,9 @@ SELECT
COUNT(resource_string_service$$name) = 0 AS svc,
COUNT(IF(has(attributes_string, 'messaging.destination.name'), 1, NULL)) = 0 AS destination,
COUNT(IF(has(attributes_string, 'messaging.destination.partition.id'), 1, NULL)) = 0 AS partition,
COUNT(IF(has(attributes_string, 'messaging.kafka.consumer.group'), 1, NULL)) = 0 AS cgroup,
COUNT(IF(has(attributes_string, 'messaging.consumer.group.name'), 1, NULL)) = 0 AS cgroup,
COUNT(IF(has(attributes_number, 'messaging.message.body.size'), 1, NULL)) = 0 AS bodysize,
COUNT(IF(has(attributes_string, 'messaging.client_id'), 1, NULL)) = 0 AS clientid,
COUNT(IF(has(attributes_string, 'messaging.client.id'), 1, NULL)) = 0 AS clientid,
COUNT(IF(has(resources_string, 'service.instance.id'), 1, NULL)) = 0 AS instanceid
FROM signoz_traces.distributed_signoz_index_v3
WHERE
@@ -422,5 +434,5 @@ WHERE
AND timestamp <= '%d'
AND ts_bucket_start >= '%d'
AND ts_bucket_start <= '%d' ;`, queueType, start, end, tsBucketStart, tsBucketEnd)
return query
return resolveKafkaSemconvSQL(query)
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/semconvsql"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
format "github.com/SigNoz/signoz/pkg/query-service/utils"
)
@@ -59,10 +60,7 @@ WITH
ELSE 'undefined'
END AS messaging_system,
kind_string,
COALESCE(
NULLIF(attributes_string['messaging.destination.name'], ''),
NULLIF(attributes_string['messaging.destination'], '')
) AS destination,
attributes_string['messaging.destination.name'] AS destination,
durationNano,
status_code
FROM signoz_traces.distributed_signoz_index_v3
@@ -113,5 +111,5 @@ ORDER BY
whereSQL, timeRangeSecs,
)
return query
return semconvsql.ResolveTraceStringAttributes(query, "messaging.destination.name")
}

View File

@@ -0,0 +1,51 @@
// Package semconvsql expands current semantic-convention attribute names in
// integration-owned SQL into current-first compatibility expressions.
package semconvsql
import (
"fmt"
"strings"
"github.com/SigNoz/signoz/pkg/clickhousesql"
"github.com/SigNoz/signoz/pkg/semconv"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
)
var traceAttributeSelector = telemetrytypes.FieldKeySelector{
Signal: telemetrytypes.SignalTraces,
FieldContext: telemetrytypes.FieldContextAttribute,
}
// ResolveTraceStringAttributes replaces direct map reads and existence checks
// for currentNames. Callers keep only current names in their SQL templates;
// the generated family table supplies every historical spelling.
func ResolveTraceStringAttributes(query string, currentNames ...string) string {
for _, current := range currentNames {
selector := traceAttributeSelector
selector.Name = current
members := semconv.Members(semconv.KindAttribute, selector)
if len(members) < 2 {
continue
}
directRead := attributeRead(current)
reads := make([]string, 0, len(members))
exists := make([]string, 0, len(members))
for _, member := range members {
reads = append(reads, fmt.Sprintf("NULLIF(%s, '')", attributeRead(member)))
exists = append(exists, "has(attributes_string, "+clickhousesql.StringLiteral(member)+")")
}
query = strings.ReplaceAll(query, directRead, "COALESCE("+strings.Join(reads, ", ")+")")
query = strings.ReplaceAll(
query,
"has(attributes_string, "+clickhousesql.StringLiteral(current)+")",
"("+strings.Join(exists, " OR ")+")",
)
}
return query
}
func attributeRead(name string) string {
return "attributes_string[" + clickhousesql.StringLiteral(name) + "]"
}

View File

@@ -0,0 +1,42 @@
package semconvsql
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestResolveTraceStringAttributesExpandsDirectReads(t *testing.T) {
query := `SELECT attributes_string['messaging.destination.name'] AS destination
FROM spans`
resolved := ResolveTraceStringAttributes(query, "messaging.destination.name")
assert.Contains(t, resolved, "COALESCE(NULLIF(attributes_string['messaging.destination.name'], ''), NULLIF(attributes_string['messaging.destination'], ''))", "direct attribute read should coalesce every family member")
}
func TestResolveTraceStringAttributesExpandsExistenceChecks(t *testing.T) {
query := `SELECT count() FROM spans
WHERE has(attributes_string, 'messaging.destination.name')`
resolved := ResolveTraceStringAttributes(query, "messaging.destination.name")
assert.Contains(t, resolved, "(has(attributes_string, 'messaging.destination.name') OR has(attributes_string, 'messaging.destination'))", "existence check should cover every family member")
}
func TestResolveTraceStringAttributesKeepsCurrentNameFirst(t *testing.T) {
query := `SELECT attributes_string['messaging.destination.name'] FROM spans`
resolved := ResolveTraceStringAttributes(query, "messaging.destination.name")
assert.Less(t, strings.Index(resolved, "messaging.destination.name"), strings.Index(resolved, "messaging.destination'"), "current name should remain the first fallback")
}
func TestResolveTraceStringAttributesLeavesUnknownNameAlone(t *testing.T) {
query := "SELECT attributes_string['custom.attribute']"
resolved := ResolveTraceStringAttributes(query, "custom.attribute")
assert.Equal(t, query, resolved, "unknown attributes should not be rewritten")
}

View File

@@ -44,7 +44,7 @@ func ExpandKeySelectorsForFamilies(ctx context.Context, orgID valuer.UUID, fl fl
}
for _, selector := range selectors {
if selector.SelectorMatchType == telemetrytypes.FieldSelectorMatchTypeFuzzy {
if selector.SelectorMatchType == telemetrytypes.FieldSelectorMatchTypeFuzzy || selector.FieldResolution.IsExact() {
continue
}
members := familySpellings(telemetrytypes.FieldKeySelector{

View File

@@ -32,7 +32,7 @@ var friendly = map[string]string{
"BETWEEN": "BETWEEN", "IN": "IN", "EXISTS": "EXISTS",
"REGEXP": "REGEXP", "CONTAINS": "CONTAINS",
"HAS": "has()", "HASANY": "hasAny()", "HASALL": "hasAll()",
"HASTOKEN": "hasToken()", "SEARCH": "search()",
"HASTOKEN": "hasToken()", "SEARCH": "search()", "EXACT": "exact()",
// literals / identifiers
"NUMBER": "number",

View File

@@ -141,7 +141,9 @@ func SynthesizeKeys(field *telemetrytypes.TelemetryFieldKey, value any) []*telem
dataTypes := inferDataTypesFromOperand(value)
keys := make([]*telemetrytypes.TelemetryFieldKey, 0, len(dataTypes))
for _, dt := range dataTypes {
keys = append(keys, telemetrytypes.NewTelemetryFieldKey(field.Name, fieldContext, dt))
key := telemetrytypes.NewTelemetryFieldKey(field.Name, fieldContext, dt)
key.FieldResolution = field.FieldResolution
keys = append(keys, key)
}
return keys
}

View File

@@ -52,8 +52,12 @@ func TestSynthesizeKeys(t *testing.T) {
wantDataTypes: []telemetrytypes.FieldDataType{telemetrytypes.FieldDataTypeString},
},
{
name: "qualified resource context honored as single string key",
field: telemetrytypes.TelemetryFieldKey{Name: "k8s.cluster.name", FieldContext: telemetrytypes.FieldContextResource},
name: "qualified exact resource context honored as single string key",
field: telemetrytypes.TelemetryFieldKey{
Name: "k8s.cluster.name",
FieldContext: telemetrytypes.FieldContextResource,
FieldResolution: telemetrytypes.FieldResolutionExact,
},
value: nil,
wantContexts: []telemetrytypes.FieldContext{telemetrytypes.FieldContextResource},
wantDataTypes: []telemetrytypes.FieldDataType{telemetrytypes.FieldDataTypeString},
@@ -83,6 +87,7 @@ func TestSynthesizeKeys(t *testing.T) {
assert.Equal(t, tt.field.Name, k.Name, "name preserved")
assert.Equal(t, tt.wantContexts[i], k.FieldContext)
assert.Equal(t, tt.wantDataTypes[i], k.FieldDataType)
assert.Equal(t, tt.field.FieldResolution, k.FieldResolution, "field resolution should be preserved")
}
})
}

View File

@@ -342,4 +342,9 @@ func TestExpandKeySelectorsForFamiliesDeduplicatesAndSkipsFuzzy(t *testing.T) {
{Name: "deployment.environment.name", Signal: telemetrytypes.SignalTraces, SelectorMatchType: telemetrytypes.FieldSelectorMatchTypeFuzzy},
}
assert.Len(t, ExpandKeySelectorsForFamilies(context.Background(), valuer.UUID{}, familiesOn(t), fuzzy), 1, "fuzzy (search-style) selectors never expand")
exact := []*telemetrytypes.FieldKeySelector{
{Name: "deployment.environment.name", Signal: telemetrytypes.SignalTraces, SelectorMatchType: telemetrytypes.FieldSelectorMatchTypeExact, FieldResolution: telemetrytypes.FieldResolutionExact},
}
assert.Len(t, ExpandKeySelectorsForFamilies(context.Background(), valuer.UUID{}, familiesOn(t), exact), 1, "an exact selector reads its own spelling alone")
}

View File

@@ -204,11 +204,12 @@ func (d *LogicalContradictionDetector) VisitPrimary(ctx *grammar.PrimaryContext)
// VisitComparison extracts constraints from comparisons.
func (d *LogicalContradictionDetector) VisitComparison(ctx *grammar.ComparisonContext) any {
if ctx.Key() == nil {
if ctx.Field() == nil {
return nil
}
field := ctx.Key().GetText()
// exact(key) and key read different spellings, so they stay distinct.
field := ctx.Field().GetText()
notContext := d.inNotContext()
// Handle EXISTS

View File

@@ -28,6 +28,8 @@ import (
func QueryStringToKeysSelectors(query string) []*telemetrytypes.FieldKeySelector {
lexer := grammar.NewFilterQueryLexer(antlr.NewInputStream(query))
keys := []*telemetrytypes.FieldKeySelector{}
// exact(key) is EXACT LPAREN KEY; the two previous tokens mark the key.
previous := make([]int, 0, 2)
for {
tok := lexer.NextToken()
if tok.GetTokenType() == antlr.TokenEOF {
@@ -36,11 +38,15 @@ func QueryStringToKeysSelectors(query string) []*telemetrytypes.FieldKeySelector
if tok.GetTokenType() == grammar.FilterQueryLexerKEY {
key := telemetrytypes.GetFieldKeyFromKeyText(tok.GetText())
if len(previous) == 2 && previous[0] == grammar.FilterQueryLexerEXACT && previous[1] == grammar.FilterQueryLexerLPAREN {
key.FieldResolution = telemetrytypes.FieldResolutionExact
}
keys = append(keys, &telemetrytypes.FieldKeySelector{
Name: key.Name,
Signal: key.Signal,
FieldContext: key.FieldContext,
FieldDataType: key.FieldDataType,
Name: key.Name,
Signal: key.Signal,
FieldContext: key.FieldContext,
FieldDataType: key.FieldDataType,
FieldResolution: key.FieldResolution,
})
if key.FieldContext == telemetrytypes.FieldContextLog ||
@@ -50,10 +56,11 @@ func QueryStringToKeysSelectors(query string) []*telemetrytypes.FieldKeySelector
// span.kind in metrics or metric.max_count in span etc.. should get the search on span.kind
// see note in where_clause_visitor.go in VisitKey(...)
keys = append(keys, &telemetrytypes.FieldKeySelector{
Name: key.FieldContext.StringValue() + "." + key.Name,
Signal: key.Signal,
FieldContext: telemetrytypes.FieldContextAttribute, // do not keep the original context because this is attribute
FieldDataType: key.FieldDataType,
Name: key.FieldContext.StringValue() + "." + key.Name,
Signal: key.Signal,
FieldContext: telemetrytypes.FieldContextAttribute, // do not keep the original context because this is attribute
FieldDataType: key.FieldDataType,
FieldResolution: key.FieldResolution,
})
}
@@ -61,13 +68,18 @@ func QueryStringToKeysSelectors(query string) []*telemetrytypes.FieldKeySelector
// https://github.com/SigNoz/signoz/issues/11374
if key.FieldContext == telemetrytypes.FieldContextScope {
keys = append(keys, &telemetrytypes.FieldKeySelector{
Name: key.FieldContext.StringValue() + "." + key.Name,
Signal: key.Signal,
FieldContext: telemetrytypes.FieldContextUnspecified, // this allows 'scope.' prefix for keys with other context as well
FieldDataType: key.FieldDataType,
Name: key.FieldContext.StringValue() + "." + key.Name,
Signal: key.Signal,
FieldContext: telemetrytypes.FieldContextUnspecified, // this allows 'scope.' prefix for keys with other context as well
FieldDataType: key.FieldDataType,
FieldResolution: key.FieldResolution,
})
}
}
previous = append(previous, tok.GetTokenType())
if len(previous) > 2 {
previous = previous[1:]
}
}
return keys

View File

@@ -33,6 +33,24 @@ func TestQueryToKeys(t *testing.T) {
},
},
},
{
query: `exact(resource.deployment.environment) EXISTS AND deployment.environment.name="prod"`,
expectedKeys: []telemetrytypes.FieldKeySelector{
{
Name: "deployment.environment",
Signal: telemetrytypes.SignalUnspecified,
FieldContext: telemetrytypes.FieldContextResource,
FieldDataType: telemetrytypes.FieldDataTypeUnspecified,
FieldResolution: telemetrytypes.FieldResolutionExact,
},
{
Name: "deployment.environment.name",
Signal: telemetrytypes.SignalUnspecified,
FieldContext: telemetrytypes.FieldContextUnspecified,
FieldDataType: telemetrytypes.FieldDataTypeUnspecified,
},
},
},
{
query: `service.name="redis" AND http.status_code=200`,
expectedKeys: []telemetrytypes.FieldKeySelector{

View File

@@ -61,7 +61,8 @@ func Resolve(
traits := storage.Traits()
lookup := key
matches := matchingLogicalFields(q.FamiliesOn, q.Signal, q.Metric, key, fieldKeys)
familiesOn := q.FamiliesOn && !key.FieldResolution.IsExact()
matches := matchingLogicalFields(familiesOn, q.Signal, q.Metric, key, fieldKeys)
if len(matches) == 0 && slices.Contains(traits.OwnContexts, key.FieldContext) {
// a column the storage knows under the key's own context is the key
// as written, and only a miss corrects to the bare spelling
@@ -71,7 +72,7 @@ func Resolve(
}
}
lookup = telemetrytypes.NewTelemetryFieldKey(key.Name, telemetrytypes.FieldContextUnspecified, key.FieldDataType)
matches = matchingLogicalFields(q.FamiliesOn, q.Signal, q.Metric, lookup, fieldKeys)
matches = matchingLogicalFields(familiesOn, q.Signal, q.Metric, lookup, fieldKeys)
}
resolved := qbtypes.Resolved{Key: key, Ambiguous: len(matches) > 1}

View File

@@ -241,7 +241,7 @@ func (visitor *whereClauseNormalizer) visitPrimary(ctx grammar.IPrimaryContext)
}
func (visitor *whereClauseNormalizer) visitComparison(ctx grammar.IComparisonContext) normalizedPart {
key := normalizeKeyText(ctx.Key().GetText())
key := normalizeFieldText(ctx.Field())
if ctx.EXISTS() != nil {
operator := "EXISTS"
@@ -361,8 +361,8 @@ func (visitor *whereClauseNormalizer) visitFunctionCall(ctx grammar.IFunctionCal
raws := make([]string, 0)
for index, param := range ctx.FunctionParamList().AllFunctionParam() {
switch {
case param.Key() != nil:
keyText := normalizeKeyText(param.Key().GetText())
case param.Field() != nil:
keyText := normalizeFieldText(param.Field())
if index == 0 {
key = keyText
} else {
@@ -537,6 +537,13 @@ func normalizeKeyText(keyText string) string {
return telemetrytypes.TelemetryFieldKeyToText(&fieldKey)
}
func normalizeFieldText(field grammar.IFieldContext) string {
if exactCall := field.ExactCall(); exactCall != nil {
return "exact(" + normalizeKeyText(exactCall.Key().GetText()) + ")"
}
return normalizeKeyText(field.Key().GetText())
}
// FilterStringLiteral quotes value for the filter expression grammar, not for
// ClickHouse: the grammar's quoted text takes backslash escapes and no hex escapes.
func FilterStringLiteral(value string) string {

View File

@@ -243,6 +243,14 @@ func TestNormalizeWhereClauseAtoms(t *testing.T) {
assert.ElementsMatch(t, expected, canonical.Conditions)
}
func TestNormalizeWhereClausePreservesExactField(t *testing.T) {
normalized, err := NormalizeWhereClause("EXACT(resource.deployment.environment:string) EXISTS", nil)
require.NoError(t, err, "valid exact expression should normalize")
assert.Equal(t, "exact(resource.deployment.environment:string) EXISTS", normalized.Expression, "normalization should preserve the exact wrapper")
require.Len(t, normalized.Conditions, 1, "exact expression should produce one canonical condition")
assert.Equal(t, "exact(resource.deployment.environment:string)", normalized.Conditions[0].Key, "canonical condition key should retain exact resolution")
}
func TestNormalizeWhereClauseTopLevel(t *testing.T) {
testCases := []struct {
name string

Some files were not shown because too many files have changed in this diff Show More