Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
d5ac192
feat: integration tests for new JSON QB
piyushsingariya Jan 19, 2026
4864c3b
feat: has JSON QB
piyushsingariya Jan 20, 2026
e2500cf
fix: tests expected queries and values
piyushsingariya Jan 20, 2026
5a00e6c
Merge branch 'main' into has-jsonqb
piyushsingariya Jan 20, 2026
96fb88a
fix: ignored .vscode in gitignore
piyushsingariya Jan 20, 2026
e8e4897
fix: tests GroupBy
piyushsingariya Jan 20, 2026
0210f02
Merge branch 'has-jsonqb' into json-qb-integration
piyushsingariya Jan 20, 2026
3402203
Merge branch 'main' into has-jsonqb
srikanthccv Jan 21, 2026
55ead8f
test: integration tests fixed
piyushsingariya Jan 21, 2026
24d1ee3
revert: gitignore change
piyushsingariya Jan 22, 2026
a4eed9f
fix: build json plans in metadata
piyushsingariya Jan 22, 2026
1966a7a
fix: empty filteredArrays condition
piyushsingariya Jan 22, 2026
349bbbb
Merge branch 'main' into has-jsonqb
piyushsingariya Jan 22, 2026
a7bc55b
revert: old json QB
piyushsingariya Jan 22, 2026
62942a4
fix: tests
piyushsingariya Jan 22, 2026
b77f97f
fix: tests
piyushsingariya Jan 22, 2026
7b5b902
Merge branch 'main' into has-jsonqb
srikanthccv Jan 22, 2026
8f2c506
fix: json qb test fix
piyushsingariya Jan 22, 2026
20b53d7
fix: review based on tushar
piyushsingariya Jan 27, 2026
62b10f8
Merge branch 'main' into has-jsonqb
piyushsingariya Jan 27, 2026
56de92d
fix: changes based on review from Srikanth
piyushsingariya Jan 27, 2026
8e71de0
fix: remove unnecessary bool checking
piyushsingariya Jan 27, 2026
36becfc
fix: removed comment
piyushsingariya Jan 27, 2026
57c51f0
fix: merge json body columns together
piyushsingariya Jan 28, 2026
ffb6243
chore: var renamed
piyushsingariya Jan 28, 2026
6a48bdc
Merge branch 'main' into has-jsonqb
piyushsingariya Jan 28, 2026
d8e116a
fix: merge conflict
piyushsingariya Jan 28, 2026
58d1d84
test: fix
piyushsingariya Jan 28, 2026
cb2aa4c
fix: tests
piyushsingariya Jan 28, 2026
27ff102
Merge branch 'main' into has-jsonqb
piyushsingariya Jan 28, 2026
37557f7
Merge branch 'main' into has-jsonqb
piyushsingariya Jan 29, 2026
e718182
fix: go test flakiness
piyushsingariya Jan 29, 2026
d45bb52
Merge branch 'has-jsonqb' into merge-json-col-fields
piyushsingariya Jan 29, 2026
8e5c3d5
chore: merge json fields
piyushsingariya Jan 29, 2026
8dd33c1
Merge branch 'main' into merge-json-col-fields
piyushsingariya Jan 29, 2026
d204c89
Merge branch 'main' into merge-json-col-fields
srikanthccv Jan 29, 2026
0d34360
fix: handle datatype collision
piyushsingariya Jan 30, 2026
e7923b7
Merge branch 'main' into json-qb-integration
piyushsingariya Feb 2, 2026
c0deb2a
fix: tests
piyushsingariya Feb 2, 2026
4013c7e
revert: few unrelated changes
piyushsingariya Feb 16, 2026
14fe874
Merge branch 'main' into merge-json-col-fields
piyushsingariya Feb 16, 2026
de1aac6
revert: more unrelated change
piyushsingariya Feb 16, 2026
86bccaa
test: blocked on pr #10153
piyushsingariya Feb 16, 2026
075cfab
feat: mapping body_v2.message:string map to body
piyushsingariya Feb 17, 2026
8bbafb5
fix: go.mod required changes
piyushsingariya Feb 17, 2026
a1d2ec8
fix: remove unused function
piyushsingariya Feb 17, 2026
cd7e1bb
Merge branch 'main' into merge-json-col-fields
piyushsingariya Feb 17, 2026
2c691b5
fix: test fixed
piyushsingariya Feb 17, 2026
93621c2
fix: go mod changes
piyushsingariya Feb 17, 2026
13cbe03
fix: tests
piyushsingariya Feb 17, 2026
bb8c874
fix: go lint
piyushsingariya Feb 17, 2026
265e337
Merge branch 'main' into merge-json-col-fields
piyushsingariya Feb 19, 2026
a039956
revert: remvoing unused function
piyushsingariya Feb 19, 2026
15cfcca
revert: change ReadMultiple is needed
piyushsingariya Feb 20, 2026
1963d58
Merge branch 'main' into merge-json-col-fields
piyushsingariya Feb 20, 2026
5d20019
fix: body.message not being mapped correctly
piyushsingariya Feb 20, 2026
24afdad
fix: append warnings from fieldkeys
piyushsingariya Feb 20, 2026
2a492cc
fix: change warning to a const to fix tests
piyushsingariya Feb 20, 2026
51bd760
Merge branch 'main' into merge-json-col-fields
nityanandagohain Feb 20, 2026
82b82b0
chore: addressing comments from Nitya
piyushsingariya Feb 23, 2026
9101d51
Merge branch 'main' into merge-json-col-fields
piyushsingariya Feb 23, 2026
426d8e2
Merge branch 'main' into json-qb-integration
piyushsingariya Feb 24, 2026
d5e2d14
Merge branch 'main' into json-qb-integration
piyushsingariya Feb 24, 2026
cb57b1f
Merge branch 'main' into json-qb-integration
piyushsingariya Feb 24, 2026
2d3060b
chore: remove unnecessary change
piyushsingariya Feb 25, 2026
4777b13
fix: shift warning attachment to getKeySelectors
piyushsingariya Mar 3, 2026
23c247a
Merge branch 'main' into merge-json-col-fields
piyushsingariya Mar 10, 2026
4c7aba6
fix: lint error
piyushsingariya Mar 10, 2026
ab0852b
feat: update message as typehint in JSON Column (#10545)
piyushsingariya Mar 11, 2026
d9a1d64
feat: work in progress
piyushsingariya Mar 11, 2026
3d6c0c6
fix: test run success
piyushsingariya Mar 11, 2026
66e800a
fix: in progress
piyushsingariya Mar 11, 2026
fe94f6a
fix: excluding message from metadata fetch
piyushsingariya Mar 12, 2026
8bd5ca8
test: cleared
piyushsingariya Mar 12, 2026
d9cad40
fix: key name in metadata
piyushsingariya Mar 12, 2026
b2f660b
Merge branch 'fix/message-keysearch' into json-qb-integration
piyushsingariya Mar 12, 2026
d8c37e7
feat: string search tests
piyushsingariya Mar 12, 2026
8b65c85
fix: column name fix
piyushsingariya Mar 12, 2026
c1e71ba
fix: column change
piyushsingariya Mar 12, 2026
fabfe0e
fix: integration tests
piyushsingariya Mar 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ require (
github.com/SigNoz/signoz-otel-collector v0.144.2
github.com/antlr4-go/antlr/v4 v4.13.1
github.com/antonmedv/expr v1.15.3
github.com/bytedance/sonic v1.14.1
github.com/cespare/xxhash/v2 v2.3.0
github.com/coreos/go-oidc/v3 v3.17.0
github.com/dgraph-io/ristretto/v2 v2.3.0
Expand Down Expand Up @@ -106,6 +105,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 // indirect
github.com/aws/smithy-go v1.24.0 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.14.1 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
Expand Down
6 changes: 5 additions & 1 deletion pkg/errors/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ type responseerroradditional struct {

func AsJSON(cause error) *JSON {
// See if this is an instance of the base error or not
_, c, m, _, u, a := Unwrapb(cause)
_, c, m, err, u, a := Unwrapb(cause)

rea := make([]responseerroradditional, len(a))
for k, v := range a {
rea[k] = responseerroradditional{v}
}

if err != nil {
rea = append(rea, responseerroradditional{err.Error()})
}

return &JSON{
Code: c.String(),
Message: m,
Expand Down
4 changes: 2 additions & 2 deletions pkg/modules/promote/implpromote/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (m *module) ListPromotedAndIndexedPaths(ctx context.Context) ([]promotetype

// add the paths that are not promoted but have indexes
for path, indexes := range aggr {
path := strings.TrimPrefix(path, telemetrylogs.BodyJSONColumnPrefix)
path := strings.TrimPrefix(path, telemetrylogs.BodyV2ColumnPrefix)
path = telemetrytypes.BodyJSONStringSearchPrefix + path
response = append(response, promotetypes.PromotePath{
Path: path,
Expand Down Expand Up @@ -163,7 +163,7 @@ func (m *module) PromoteAndIndexPaths(
}
}
if len(it.Indexes) > 0 {
parentColumn := telemetrylogs.LogsV2BodyJSONColumn
parentColumn := telemetrylogs.LogsV2BodyV2Column
// if the path is already promoted or is being promoted, add it to the promoted column
if _, promoted := existingPromotedPaths[it.Path]; promoted || it.Promote {
parentColumn = telemetrylogs.LogsV2BodyPromotedColumn
Expand Down
51 changes: 0 additions & 51 deletions pkg/querier/builder_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ import (

"github.com/ClickHouse/clickhouse-go/v2"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/telemetrylogs"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/types/instrumentationtypes"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/bytedance/sonic"
)

type builderQuery[T any] struct {
Expand Down Expand Up @@ -262,40 +260,6 @@ func (q *builderQuery[T]) executeWithContext(ctx context.Context, query string,
return nil, err
}

// merge body_json and promoted into body
if q.spec.Signal == telemetrytypes.SignalLogs {
switch typedPayload := payload.(type) {
case *qbtypes.RawData:
for _, rr := range typedPayload.Rows {
seeder := func() error {
body, ok := rr.Data[telemetrylogs.LogsV2BodyJSONColumn].(map[string]any)
if !ok {
return nil
}
promoted, ok := rr.Data[telemetrylogs.LogsV2BodyPromotedColumn].(map[string]any)
if !ok {
return nil
}
seed(promoted, body)
str, err := sonic.MarshalString(body)
if err != nil {
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to marshal body")
}
rr.Data["body"] = str
return nil
}
err := seeder()
if err != nil {
return nil, err
}

delete(rr.Data, telemetrylogs.LogsV2BodyJSONColumn)
delete(rr.Data, telemetrylogs.LogsV2BodyPromotedColumn)
}
payload = typedPayload
}
}

return &qbtypes.Result{
Type: q.kind,
Value: payload,
Expand Down Expand Up @@ -423,18 +387,3 @@ func decodeCursor(cur string) (int64, error) {
}
return strconv.ParseInt(string(b), 10, 64)
}

func seed(promoted map[string]any, body map[string]any) {
for key, fromValue := range promoted {
if toValue, ok := body[key]; !ok {
body[key] = fromValue
} else {
if fromValue, ok := fromValue.(map[string]any); ok {
if toValue, ok := toValue.(map[string]any); ok {
seed(fromValue, toValue)
body[key] = toValue
}
}
}
}
}
11 changes: 2 additions & 9 deletions pkg/querier/consume.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/bytedance/sonic"
)

var (
Expand Down Expand Up @@ -394,17 +393,11 @@ func readAsRaw(rows driver.Rows, queryName string) (*qbtypes.RawData, error) {

// de-reference the typed pointer to any
val := reflect.ValueOf(cellPtr).Elem().Interface()

// Post-process JSON columns: normalize into structured values
// Post-process JSON columns: normalize into String value
if strings.HasPrefix(strings.ToUpper(colTypes[i].DatabaseTypeName()), "JSON") {
switch x := val.(type) {
case []byte:
if len(x) > 0 {
var v any
if err := sonic.Unmarshal(x, &v); err == nil {
val = v
}
}
val = string(x)
default:
// already a structured type (map[string]any, []any, etc.)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/query-service/rules/base_rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ func TestBaseRule_FilterNewSeries(t *testing.T) {
telemetryStore := telemetrystoretest.New(telemetrystore.Config{}, &queryMatcherAny{})

// Setup mock metadata store
mockMetadataStore := telemetrytypestest.NewMockMetadataStore()
mockMetadataStore := telemetrytypestest.NewMockMetadataStore(nil)

// Create query parser
queryParser := queryparser.New(settings)
Expand Down
6 changes: 4 additions & 2 deletions pkg/querybuilder/fallback_expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,10 @@ func DataTypeCollisionHandledFieldName(key *telemetrytypes.TelemetryFieldKey, va
// While we expect user not to send the mixed data types, it inevitably happens
// So we handle the data type collisions here
switch key.FieldDataType {
case telemetrytypes.FieldDataTypeString, telemetrytypes.FieldDataTypeArrayString:
case telemetrytypes.FieldDataTypeString, telemetrytypes.FieldDataTypeArrayString, telemetrytypes.FieldDataTypeJSON:
if key.FieldDataType == telemetrytypes.FieldDataTypeJSON {
tblFieldName = fmt.Sprintf("toString(%s)", tblFieldName)
}
switch v := value.(type) {
case float64:
// try to convert the string value to to number
Expand All @@ -219,7 +222,6 @@ func DataTypeCollisionHandledFieldName(key *telemetrytypes.TelemetryFieldKey, va
// we don't have a toBoolOrNull in ClickHouse, so we need to convert the bool to a string
value = fmt.Sprintf("%t", v)
}

case telemetrytypes.FieldDataTypeInt64,
telemetrytypes.FieldDataTypeArrayInt64,
telemetrytypes.FieldDataTypeNumber,
Expand Down
34 changes: 14 additions & 20 deletions pkg/querybuilder/where_clause_visitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,37 +313,30 @@ func (v *filterExpressionVisitor) VisitPrimary(ctx *grammar.PrimaryContext) any
return ""
}
child := ctx.GetChild(0)
var searchText string
if keyCtx, ok := child.(*grammar.KeyContext); ok {
// create a full text search condition on the body field

keyText := keyCtx.GetText()
cond, err := v.conditionBuilder.ConditionFor(context.Background(), v.fullTextColumn, qbtypes.FilterOperatorRegexp, FormatFullTextSearch(keyText), v.builder, v.startNs, v.endNs)
if err != nil {
v.errors = append(v.errors, fmt.Sprintf("failed to build full text search condition: %s", err.Error()))
return ""
}
return cond
searchText = keyCtx.GetText()
} else if valCtx, ok := child.(*grammar.ValueContext); ok {
var text string
if valCtx.QUOTED_TEXT() != nil {
text = trimQuotes(valCtx.QUOTED_TEXT().GetText())
searchText = trimQuotes(valCtx.QUOTED_TEXT().GetText())
} else if valCtx.NUMBER() != nil {
text = valCtx.NUMBER().GetText()
searchText = valCtx.NUMBER().GetText()
} else if valCtx.BOOL() != nil {
text = valCtx.BOOL().GetText()
searchText = valCtx.BOOL().GetText()
} else if valCtx.KEY() != nil {
text = valCtx.KEY().GetText()
searchText = valCtx.KEY().GetText()
} else {
v.errors = append(v.errors, fmt.Sprintf("unsupported value type: %s", valCtx.GetText()))
return ""
}
cond, err := v.conditionBuilder.ConditionFor(context.Background(), v.fullTextColumn, qbtypes.FilterOperatorRegexp, FormatFullTextSearch(text), v.builder, v.startNs, v.endNs)
if err != nil {
v.errors = append(v.errors, fmt.Sprintf("failed to build full text search condition: %s", err.Error()))
return ""
}
return cond
}
cond, err := v.conditionBuilder.ConditionFor(context.Background(), v.fullTextColumn, qbtypes.FilterOperatorRegexp, FormatFullTextSearch(searchText), v.builder, v.startNs, v.endNs)
if err != nil {
v.errors = append(v.errors, fmt.Sprintf("failed to build full text search condition: %s", err.Error()))
return ""
}
return cond
}

return "" // Should not happen with valid input
Expand Down Expand Up @@ -383,6 +376,7 @@ func (v *filterExpressionVisitor) VisitComparison(ctx *grammar.ComparisonContext
for _, key := range keys {
condition, err := v.conditionBuilder.ConditionFor(context.Background(), key, op, nil, v.builder, v.startNs, v.endNs)
if err != nil {
v.errors = append(v.errors, fmt.Sprintf("failed to build condition: %s", err.Error()))
return ""
}
conds = append(conds, condition)
Expand Down Expand Up @@ -648,7 +642,6 @@ func (v *filterExpressionVisitor) VisitValueList(ctx *grammar.ValueListContext)

// VisitFullText handles standalone quoted strings for full-text search
func (v *filterExpressionVisitor) VisitFullText(ctx *grammar.FullTextContext) any {

if v.skipFullTextFilter {
return ""
}
Expand All @@ -670,6 +663,7 @@ func (v *filterExpressionVisitor) VisitFullText(ctx *grammar.FullTextContext) an
v.errors = append(v.errors, fmt.Sprintf("failed to build full text search condition: %s", err.Error()))
return ""
}

return cond
}

Expand Down
43 changes: 30 additions & 13 deletions pkg/telemetrylogs/condition_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
import (
"context"
"fmt"
"slices"

schema "github.com/SigNoz/signoz-otel-collector/cmd/signozschemamigrator/schema_migrator"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/querybuilder"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"golang.org/x/exp/maps"

"github.com/huandu/go-sqlbuilder"
)
Expand All @@ -35,7 +33,7 @@
return "", err
}

if column.Type.GetType() == schema.ColumnTypeEnumJSON && querybuilder.BodyJSONQueryEnabled {
if column.Type.GetType() == schema.ColumnTypeEnumJSON && querybuilder.BodyJSONQueryEnabled && !key.Materialized {
valueType, value := InferDataType(value, operator, key)
cond, err := NewJSONConditionBuilder(key, valueType).buildJSONCondition(operator, value, sb)
if err != nil {
Expand All @@ -54,7 +52,7 @@
}

// Check if this is a body JSON search - either by FieldContext
if key.FieldContext == telemetrytypes.FieldContextBody {
if key.FieldContext == telemetrytypes.FieldContextBody && !querybuilder.BodyJSONQueryEnabled {
tblFieldName, value = GetBodyJSONKey(ctx, key, operator, value)
}

Expand Down Expand Up @@ -108,7 +106,6 @@
return sb.ILike(tblFieldName, fmt.Sprintf("%%%s%%", value)), nil
case qbtypes.FilterOperatorNotContains:
return sb.NotILike(tblFieldName, fmt.Sprintf("%%%s%%", value)), nil

case qbtypes.FilterOperatorRegexp:
// Note: Escape $$ to $$$$ to avoid sqlbuilder interpreting materialized $ signs
// Only needed because we are using sprintf instead of sb.Match (not implemented in sqlbuilder)
Expand Down Expand Up @@ -176,9 +173,16 @@
var value any
switch column.Type.GetType() {
case schema.ColumnTypeEnumJSON:
if operator == qbtypes.FilterOperatorExists {
return sb.IsNotNull(tblFieldName), nil
} else {
switch key.FieldDataType {
case telemetrytypes.FieldDataTypeJSON:
if operator == qbtypes.FilterOperatorExists {
return sb.EQ(fmt.Sprintf("empty(%s)", tblFieldName), false), nil
}
return sb.EQ(fmt.Sprintf("empty(%s)", tblFieldName), true), nil
default:
if operator == qbtypes.FilterOperatorExists {
return sb.IsNotNull(tblFieldName), nil
}
return sb.IsNull(tblFieldName), nil
}
case schema.ColumnTypeEnumLowCardinality:
Expand Down Expand Up @@ -247,19 +251,32 @@
return "", err
}

if !(key.FieldContext == telemetrytypes.FieldContextBody && querybuilder.BodyJSONQueryEnabled) && operator.AddDefaultExistsFilter() {
// skip adding exists filter for intrinsic fields
// with an exception for body json search
field, _ := c.fm.FieldFor(ctx, key)
if slices.Contains(maps.Keys(IntrinsicFields), field) && key.FieldContext != telemetrytypes.FieldContextBody {
// Skip adding exists filter for intrinsic fields i.e. Table level log context fields
buildExistCondition := operator.AddDefaultExistsFilter()
switch key.FieldContext {
case telemetrytypes.FieldContextLog, telemetrytypes.FieldContextScope:
// pass; No need to build exist condition for top level columns
// immidiately return

Check failure on line 259 in pkg/telemetrylogs/condition_builder.go

View workflow job for this annotation

GitHub Actions / lint / go

`immidiately` is a misspelling of `immediately` (misspell)
return condition, nil
case telemetrytypes.FieldContextResource, telemetrytypes.FieldContextAttribute:
buildExistCondition = buildExistCondition && true
case telemetrytypes.FieldContextBody:
// Querying JSON fields already account for Nullability of fields
// so additional exists checks are not needed
if querybuilder.BodyJSONQueryEnabled {
return condition, nil
}

buildExistCondition = buildExistCondition && true
}

if buildExistCondition {
existsCondition, err := c.conditionFor(ctx, key, qbtypes.FilterOperatorExists, nil, sb)
if err != nil {
return "", err
}
return sb.And(condition, existsCondition), nil
}

return condition, nil
}
3 changes: 2 additions & 1 deletion pkg/telemetrylogs/condition_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ func TestConditionFor(t *testing.T) {
{
name: "Contains operator - body",
key: telemetrytypes.TelemetryFieldKey{
Name: "body",
Name: "body",
FieldContext: telemetrytypes.FieldContextLog,
},
operator: qbtypes.FilterOperatorContains,
value: 521509198310,
Expand Down
Loading
Loading