Skip to content

Commit b46bb94

Browse files
committed
jsonpath: optimize executeAnyItem by iterating over JSON containers
This commit replaces the usage of `AllPathsWithDepth` with direct iteration over JSON arrays and objects in `executeAnyItem`. This avoids unnecessary JSON decoding of the entire object structure (`jsonEncoded.decode()`) when only direct children are needed. Preliminary testing shows this change reduces execution time for queries with any array/object unwrapping by approximately 50%. Release note: None
1 parent 6a48f58 commit b46bb94

File tree

1 file changed

+28
-32
lines changed

1 file changed

+28
-32
lines changed

pkg/util/jsonpath/eval/eval.go

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -144,51 +144,47 @@ func (ctx *jsonpathCtx) unwrapCurrentTargetAndEval(
144144
func (ctx *jsonpathCtx) executeAnyItem(
145145
jsonPath jsonpath.Path, jsonValue json.JSON, unwrapNext bool,
146146
) ([]json.JSON, error) {
147-
childItems, err := json.AllPathsWithDepth(jsonValue, 1 /* depth */)
148-
if err != nil {
149-
return nil, err
147+
if jsonValue.Len() == 0 {
148+
return []json.JSON{}, nil
150149
}
151150
var agg []json.JSON
152-
for _, item := range childItems {
153-
// The case when this will happen is if jsonValue is an empty array or empty
154-
// object, in which case we just skip the evaluation.
155-
if item.Len() == 0 {
156-
continue
151+
processItem := func(item json.JSON) error {
152+
if jsonPath == nil {
153+
agg = append(agg, item)
154+
return nil
157155
}
158-
if item.Len() != 1 {
159-
return nil, errors.AssertionFailedf("unexpected path length")
156+
evalResults, err := ctx.eval(jsonPath, item, unwrapNext)
157+
if err != nil {
158+
return err
160159
}
161-
162-
var unwrappedItem json.JSON
163-
switch item.Type() {
164-
case json.ArrayJSONType:
165-
unwrappedItem, err = item.FetchValIdx(0 /* idx */)
160+
agg = append(agg, evalResults...)
161+
return nil
162+
}
163+
// TODO(normanchenn): Consider creating some kind of unified iterator interface
164+
// for json arrays and objects.
165+
switch jsonValue.Type() {
166+
case json.ArrayJSONType:
167+
for i := 0; i < jsonValue.Len(); i++ {
168+
item, err := jsonValue.FetchValIdx(i)
166169
if err != nil {
167170
return nil, err
168171
}
169-
if unwrappedItem == nil {
170-
return nil, errors.AssertionFailedf("unwrapping json element")
172+
if item == nil {
173+
return nil, errors.AssertionFailedf("fetching json array element at index %d", i)
171174
}
172-
case json.ObjectJSONType:
173-
iter, _ := item.ObjectIter()
174-
// Guaranteed to have one item.
175-
ok := iter.Next()
176-
if !ok {
177-
return nil, errors.AssertionFailedf("unexpected empty json object")
175+
if err = processItem(item); err != nil {
176+
return nil, err
178177
}
179-
unwrappedItem = iter.Value()
180-
default:
181-
panic(errors.AssertionFailedf("unexpected json type"))
182178
}
183-
if jsonPath == nil {
184-
agg = append(agg, unwrappedItem)
185-
} else {
186-
evalResults, err := ctx.eval(jsonPath, unwrappedItem, unwrapNext)
187-
if err != nil {
179+
case json.ObjectJSONType:
180+
iter, _ := jsonValue.ObjectIter()
181+
for iter.Next() {
182+
if err := processItem(iter.Value()); err != nil {
188183
return nil, err
189184
}
190-
agg = append(agg, evalResults...)
191185
}
186+
default:
187+
panic(errors.AssertionFailedf("executeAnyItem called with type: %s", jsonValue.Type()))
192188
}
193189
return agg, nil
194190
}

0 commit comments

Comments
 (0)