Skip to content

Commit

Permalink
fix(i): Aggregate json filter (#3150)
Browse files Browse the repository at this point in the history
## Relevant issue(s)

Resolves #3149

## Description

This PR fixes an issue with aggregate JSON filtering.

## Tasks

- [x] I made sure the code is well commented, particularly
hard-to-understand areas.
- [x] I made sure the repository-held documentation is changed
accordingly.
- [x] I made sure the pull request title adheres to the conventional
commit style (the subset used in the project can be found in
[tools/configs/chglog/config.yml](tools/configs/chglog/config.yml)).
- [x] I made sure to discuss its limitations such as threats to
validity, vulnerability to mistake and misuse, robustness to
invalidation of assumptions, resource requirements, ...

## How has this been tested?

Added integration test.

Specify the platform(s) on which this was tested:
- MacOS
  • Loading branch information
nasdf authored Oct 17, 2024
1 parent f528a6b commit e81133e
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 0 deletions.
27 changes: 27 additions & 0 deletions internal/planner/mapper/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ func resolveAggregates(
if err != nil {
return nil, err
}
removeJSONSubFields(childMapping, hostSelectRequest)

childFields, _, err := getRequestables(
ctx,
Expand Down Expand Up @@ -569,6 +570,32 @@ func resolveAggregates(
return fields, nil
}

// removeJSONSubFields ensures that selections of
// JSON objects are not interpreted as joins.
//
// This can happen when an aggregate contains a filter
// on a JSON object, but we can't tell if it is a relation
// until the child mapping is created.
func removeJSONSubFields(
mapping *core.DocumentMapping,
hostSelectRequest *request.Select,
) {
var fields []request.Selection
for _, field := range hostSelectRequest.Fields {
switch f := field.(type) {
case *request.Select:
_, isMapped := mapping.IndexesByName[f.Name]
if !isMapped {
fields = append(fields, field)
}

default:
fields = append(fields, field)
}
}
hostSelectRequest.Fields = fields
}

func mapAggregateNestedTargets(
target *aggregateRequestTarget,
hostSelectRequest *request.Select,
Expand Down
65 changes: 65 additions & 0 deletions tests/integration/query/json/with_aggregate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2024 Democratized Data Foundation
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package json

import (
"testing"

testUtils "github.com/sourcenetwork/defradb/tests/integration"
)

func TestQueryJSON_WithAggregateFilter_Succeeds(t *testing.T) {
test := testUtils.TestCase{
Description: "Simple JSON, aggregate with filter",
Actions: []any{
testUtils.SchemaUpdate{
Schema: `type Users {
name: String
custom: JSON
}`,
},
testUtils.CreateDoc{
Doc: `{
"name": "John",
"custom": {
"tree": "maple",
"age": 250
}
}`,
},
testUtils.CreateDoc{
Doc: `{
"name": "Andy",
"custom": {
"tree": "oak",
"age": 450
}
}`,
},
testUtils.CreateDoc{
Doc: `{
"name": "Shahzad",
"custom": null
}`,
},
testUtils.Request{
Request: `query {
_count(Users: {filter: {custom: {tree: {_eq: "oak"}}}})
}`,
Results: map[string]any{
"_count": 1,
},
},
},
}

testUtils.ExecuteTestCase(t, test)
}
83 changes: 83 additions & 0 deletions tests/integration/query/one_to_many/with_count_filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,86 @@ func TestQueryOneToManyWithCountWithFilterAndChildFilter(t *testing.T) {

executeTestCase(t, test)
}

func TestQueryOneToMany_WithCountWithJSONFilterAndChildFilter_Succeeds(t *testing.T) {
test := testUtils.TestCase{
Description: "One-to-many relation query from many side with count with JSON filter",
Actions: []any{
testUtils.SchemaUpdate{
Schema: `
type Book {
name: String
rating: Float
author: Author
}
type Author {
name: String
age: Int
verified: Boolean
published: [Book]
metadata: JSON
}`,
},
testUtils.CreateDoc{
CollectionID: 1,
Doc: `{
"name": "John Grisham",
"age": 65,
"verified": true,
"metadata": {
"yearOfBirth": 1955
}
}`,
},
testUtils.CreateDoc{
CollectionID: 1,
Doc: `{
"name": "Cornelia Funke",
"age": 62,
"verified": false,
"metadata": {
"yearOfBirth": 1958
}
}`,
},
testUtils.CreateDoc{
CollectionID: 0,
DocMap: map[string]any{
"name": "Painted House",
"rating": 4.9,
"author_id": testUtils.NewDocIndex(1, 0),
},
},
testUtils.CreateDoc{
CollectionID: 0,
DocMap: map[string]any{
"name": "A Time for Mercy",
"rating": 4.5,
"author_id": testUtils.NewDocIndex(1, 0),
},
},
testUtils.CreateDoc{
CollectionID: 0,
DocMap: map[string]any{
"name": "Theif Lord",
"rating": 4.8,
"author_id": testUtils.NewDocIndex(1, 1),
},
},
testUtils.Request{
Request: `query {
_count(Author: {filter: {
metadata: {yearOfBirth: {_eq: 1958}},
published: {name: {_ilike: "%lord%"}}
}})
}`,
Results: map[string]any{
"_count": 1,
},
},
},
}

testUtils.ExecuteTestCase(t, test)
}

0 comments on commit e81133e

Please sign in to comment.