Skip to content

Commit

Permalink
fix: Enable filtering doc by fields of JSON and Blob types (#2841)
Browse files Browse the repository at this point in the history
## Relevant issue(s)

Resolves #2840

## Description

Fixes the issue not being able to filter by JSON and Blob fields
  • Loading branch information
islamaliev authored Jul 17, 2024
1 parent fe9fae6 commit f6b6485
Show file tree
Hide file tree
Showing 8 changed files with 502 additions and 2 deletions.
11 changes: 9 additions & 2 deletions internal/request/graphql/schema/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ func defaultTypes(
crdtEnum *gql.Enum,
explainEnum *gql.Enum,
) []gql.Type {
blobScalarType := schemaTypes.BlobScalarType()
jsonScalarType := schemaTypes.JSONScalarType()

return []gql.Type{
// Base Scalar types
gql.Boolean,
Expand All @@ -176,8 +179,8 @@ func defaultTypes(
gql.String,

// Custom Scalar types
schemaTypes.BlobScalarType(),
schemaTypes.JSONScalarType(),
blobScalarType,
jsonScalarType,

// Base Query types

Expand All @@ -195,6 +198,10 @@ func defaultTypes(
schemaTypes.NotNullIntOperatorBlock(),
schemaTypes.StringOperatorBlock(),
schemaTypes.NotNullstringOperatorBlock(),
schemaTypes.JSONOperatorBlock(jsonScalarType),
schemaTypes.NotNullJSONOperatorBlock(jsonScalarType),
schemaTypes.BlobOperatorBlock(blobScalarType),
schemaTypes.NotNullBlobOperatorBlock(blobScalarType),

commitsOrderArg,
commitLinkObject,
Expand Down
167 changes: 167 additions & 0 deletions internal/request/graphql/schema/types/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,173 @@ func NotNullstringOperatorBlock() *gql.InputObject {
})
}

// JSONOperatorBlock filter block for string types.
func JSONOperatorBlock(jsonScalarType *gql.Scalar) *gql.InputObject {
return gql.NewInputObject(gql.InputObjectConfig{
Name: "JSONOperatorBlock",
Description: stringOperatorBlockDescription,
Fields: gql.InputObjectConfigFieldMap{
"_eq": &gql.InputObjectFieldConfig{
Description: eqOperatorDescription,
Type: jsonScalarType,
},
"_ne": &gql.InputObjectFieldConfig{
Description: neOperatorDescription,
Type: jsonScalarType,
},
"_in": &gql.InputObjectFieldConfig{
Description: inOperatorDescription,
Type: gql.NewList(jsonScalarType),
},
"_nin": &gql.InputObjectFieldConfig{
Description: ninOperatorDescription,
Type: gql.NewList(jsonScalarType),
},
"_like": &gql.InputObjectFieldConfig{
Description: likeStringOperatorDescription,
Type: gql.String,
},
"_nlike": &gql.InputObjectFieldConfig{
Description: nlikeStringOperatorDescription,
Type: gql.String,
},
"_ilike": &gql.InputObjectFieldConfig{
Description: ilikeStringOperatorDescription,
Type: gql.String,
},
"_nilike": &gql.InputObjectFieldConfig{
Description: nilikeStringOperatorDescription,
Type: gql.String,
},
},
})
}

// NotNullJSONOperatorBlock filter block for string! types.
func NotNullJSONOperatorBlock(jsonScalarType *gql.Scalar) *gql.InputObject {
return gql.NewInputObject(gql.InputObjectConfig{
Name: "NotNullJSONOperatorBlock",
Description: notNullStringOperatorBlockDescription,
Fields: gql.InputObjectConfigFieldMap{
"_eq": &gql.InputObjectFieldConfig{
Description: eqOperatorDescription,
Type: jsonScalarType,
},
"_ne": &gql.InputObjectFieldConfig{
Description: neOperatorDescription,
Type: jsonScalarType,
},
"_in": &gql.InputObjectFieldConfig{
Description: inOperatorDescription,
Type: gql.NewList(gql.NewNonNull(jsonScalarType)),
},
"_nin": &gql.InputObjectFieldConfig{
Description: ninOperatorDescription,
Type: gql.NewList(gql.NewNonNull(jsonScalarType)),
},
"_like": &gql.InputObjectFieldConfig{
Description: likeStringOperatorDescription,
Type: gql.String,
},
"_nlike": &gql.InputObjectFieldConfig{
Description: nlikeStringOperatorDescription,
Type: gql.String,
},
"_ilike": &gql.InputObjectFieldConfig{
Description: ilikeStringOperatorDescription,
Type: gql.String,
},
"_nilike": &gql.InputObjectFieldConfig{
Description: nilikeStringOperatorDescription,
Type: gql.String,
},
},
})
}

func BlobOperatorBlock(blobScalarType *gql.Scalar) *gql.InputObject {
return gql.NewInputObject(gql.InputObjectConfig{
Name: "BlobOperatorBlock",
Description: stringOperatorBlockDescription,
Fields: gql.InputObjectConfigFieldMap{
"_eq": &gql.InputObjectFieldConfig{
Description: eqOperatorDescription,
Type: blobScalarType,
},
"_ne": &gql.InputObjectFieldConfig{
Description: neOperatorDescription,
Type: blobScalarType,
},
"_in": &gql.InputObjectFieldConfig{
Description: inOperatorDescription,
Type: gql.NewList(blobScalarType),
},
"_nin": &gql.InputObjectFieldConfig{
Description: ninOperatorDescription,
Type: gql.NewList(blobScalarType),
},
"_like": &gql.InputObjectFieldConfig{
Description: likeStringOperatorDescription,
Type: gql.String,
},
"_nlike": &gql.InputObjectFieldConfig{
Description: nlikeStringOperatorDescription,
Type: gql.String,
},
"_ilike": &gql.InputObjectFieldConfig{
Description: ilikeStringOperatorDescription,
Type: gql.String,
},
"_nilike": &gql.InputObjectFieldConfig{
Description: nilikeStringOperatorDescription,
Type: gql.String,
},
},
})
}

// NotNullJSONOperatorBlock filter block for string! types.
func NotNullBlobOperatorBlock(blobScalarType *gql.Scalar) *gql.InputObject {
return gql.NewInputObject(gql.InputObjectConfig{
Name: "NotNullBlobOperatorBlock",
Description: notNullStringOperatorBlockDescription,
Fields: gql.InputObjectConfigFieldMap{
"_eq": &gql.InputObjectFieldConfig{
Description: eqOperatorDescription,
Type: blobScalarType,
},
"_ne": &gql.InputObjectFieldConfig{
Description: neOperatorDescription,
Type: blobScalarType,
},
"_in": &gql.InputObjectFieldConfig{
Description: inOperatorDescription,
Type: gql.NewList(gql.NewNonNull(blobScalarType)),
},
"_nin": &gql.InputObjectFieldConfig{
Description: ninOperatorDescription,
Type: gql.NewList(gql.NewNonNull(blobScalarType)),
},
"_like": &gql.InputObjectFieldConfig{
Description: likeStringOperatorDescription,
Type: gql.String,
},
"_nlike": &gql.InputObjectFieldConfig{
Description: nlikeStringOperatorDescription,
Type: gql.String,
},
"_ilike": &gql.InputObjectFieldConfig{
Description: ilikeStringOperatorDescription,
Type: gql.String,
},
"_nilike": &gql.InputObjectFieldConfig{
Description: nilikeStringOperatorDescription,
Type: gql.String,
},
},
})
}

// IdOperatorBlock filter block for ID types.
func IdOperatorBlock() *gql.InputObject {
return gql.NewInputObject(gql.InputObjectConfig{
Expand Down
54 changes: 54 additions & 0 deletions tests/integration/query/simple/with_filter/with_eq_blob_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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 simple

import (
"testing"

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

func TestQuerySimple_WithEqOpOnBlobField_ShouldFilter(t *testing.T) {
test := testUtils.TestCase{
Actions: []any{
testUtils.SchemaUpdate{
Schema: `
type Users {
name: String
data: Blob
}
`,
},
testUtils.CreateDoc{
Doc: `{
"name": "John",
"data": "00FF99AA"
}`,
},
testUtils.CreateDoc{
Doc: `{
"name": "Andy",
"data": "FA02CC45"
}`,
},
testUtils.Request{
Request: `query {
Users(filter: {data: {_eq: "00FF99AA"}}) {
name
}
}`,
Results: []map[string]any{{"name": "John"}},
},
},
}

testUtils.ExecuteTestCase(t, test)
}
55 changes: 55 additions & 0 deletions tests/integration/query/simple/with_filter/with_eq_json_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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 simple

import (
"testing"

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

func TestQuerySimple_WithEqOpOnJSONField_ShouldFilter(t *testing.T) {
test := testUtils.TestCase{
Actions: []any{
testUtils.SchemaUpdate{
Schema: `
type Users {
name: String
custom: JSON
}
`,
},
testUtils.CreateDoc{
DocMap: map[string]any{
"name": "John",
"custom": "{\"tree\": \"maple\", \"age\": 250}",
},
},
testUtils.CreateDoc{
DocMap: map[string]any{
"name": "Andy",
"custom": "{\"tree\": \"oak\", \"age\": 450}",
},
},
testUtils.Request{
// the filtered-by JSON has no spaces, because this is now it's stored.
Request: `query {
Users(filter: {custom: {_eq: "{\"tree\":\"oak\",\"age\":450}"}}) {
name
}
}`,
Results: []map[string]any{{"name": "Andy"}},
},
},
}

testUtils.ExecuteTestCase(t, test)
}
54 changes: 54 additions & 0 deletions tests/integration/query/simple/with_filter/with_in_blob_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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 simple

import (
"testing"

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

func TestQuerySimple_WithInOpOnBlobField_ShouldFilter(t *testing.T) {
test := testUtils.TestCase{
Actions: []any{
testUtils.SchemaUpdate{
Schema: `
type Users {
name: String
data: Blob
}
`,
},
testUtils.CreateDoc{
Doc: `{
"name": "John",
"data": "00FF99AA"
}`,
},
testUtils.CreateDoc{
Doc: `{
"name": "Andy",
"data": "FA02CC45"
}`,
},
testUtils.Request{
Request: `query {
Users(filter: {data: {_in: ["00FF99AA"]}}) {
name
}
}`,
Results: []map[string]any{{"name": "John"}},
},
},
}

testUtils.ExecuteTestCase(t, test)
}
Loading

0 comments on commit f6b6485

Please sign in to comment.