diff --git a/.github/workflows/start-binary.yml b/.github/workflows/start-binary.yml index 7268faae7b..8a0e56484f 100644 --- a/.github/workflows/start-binary.yml +++ b/.github/workflows/start-binary.yml @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-latest steps: - + - name: Checkout code into the directory uses: actions/checkout@v3 diff --git a/.gitignore b/.gitignore index 40eac1780c..7c2a6a25de 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,8 @@ crash.log # Ignore .zip files, such as Lambda Function code slugs. **.zip + +# Ignore compiled dynamic libraries. +libs/*.so +libs/*.dylib +libs/*.dll diff --git a/Makefile b/Makefile index c4c02da3d7..1b82d6bd2f 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,44 @@ else ifeq ($(OS_GENERAL),Windows) OS_PACKAGE_MANAGER := choco endif +# Directory where binaries will be placed +BIN_DIR := ./libs + +# Set the library file name based on the operating system +ifeq ($(OS_GENERAL),Linux) + LIB_EXT := .so +endif +ifeq ($(OS_GENERAL),Darwin) + LIB_EXT := .dylib +endif +ifeq ($(OS_GENERAL),Windows) + LIB_EXT := .dll +endif + +RUST_REPO_URL := git@github.com:sourcenetwork/defradb-rs.git +#RUST_REPO_URL := https://github.com/sourcenetwork/defradb-rs.git +RUST_REPO_BRANCH := main +RUST_DIR := ./build/defradb-rs + +# Initialize the Rust library by checking out the repository and compiling the library. +.PHONY: init +init_rust: checkout_rust compile_rust + +# Checkout the Rust repository if it doesn't exist, otherwise pull the latest changes. +.PHONY: checkout_rust +checkout_rust: $(BIN_DIR) + @if [ -d "$(RUST_DIR)" ]; then \ + cd $(RUST_DIR) && git pull; \ + else \ + git clone --branch $(RUST_REPO_BRANCH) $(RUST_REPO_URL) $(RUST_DIR); \ + fi + +# Compile the Rust library and copy it to the libs directory. +.PHONY: compile_rust +compile_rust: $(BIN_DIR) + cargo build --release --manifest-path=$(RUST_DIR)/Cargo.toml + cp $(RUST_DIR)/target/release/libabi$(LIB_EXT) $(BIN_DIR)/libabi$(LIB_EXT) + # Provide info from git to the version package using linker flags. ifeq (, $(shell which git)) $(error "No git in $(PATH), version information won't be included") @@ -95,7 +133,7 @@ endif # - make build # - make build path="path/to/defradb-binary" .PHONY: build -build: +build: init_rust ifeq ($(path),) @go build $(BUILD_FLAGS) -o build/defradb cmd/defradb/main.go else @@ -205,7 +243,7 @@ tidy: go mod tidy -go=1.21.3 .PHONY: clean -clean: +clean: clean\:rust go clean cmd/defradb/main.go rm -f build/defradb @@ -218,6 +256,11 @@ clean\:coverage: rm -rf $(COVERAGE_DIRECTORY) rm -f $(COVERAGE_FILE) +.PHONY: clean\:rust +clean\:rust: + cargo clean --manifest-path=${RUST_DIR}/Cargo.toml + rm -f $(BIN_DIR)/*$(LIB_EXT) + # Example: `make tls-certs path="~/.defradb/certs"` .PHONY: tls-certs tls-certs: diff --git a/internal/connor/connor.go b/internal/connor/connor.go index 927b1dfffd..1674175655 100644 --- a/internal/connor/connor.go +++ b/internal/connor/connor.go @@ -11,6 +11,10 @@ func Match(conditions map[FilterKey]any, data any) (bool, error) { return eq(conditions, data) } +func MatchJSON(conditions, data string) (bool, error) { + return callMatchConditionsABI(conditions, data) +} + // matchWith can be used to specify the exact operator to use when performing // a match operation. This is primarily used when building custom operators or // if you wish to override the behavior of another operator. diff --git a/internal/connor/connor_test.go b/internal/connor/connor_test.go new file mode 100644 index 0000000000..a8db1caf26 --- /dev/null +++ b/internal/connor/connor_test.go @@ -0,0 +1,341 @@ +package connor + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +type fakeFilterKey struct { + operator string + value any +} + +func (f fakeFilterKey) GetProp(data any) any { + if f.value != nil { + return f.value + } + return data +} + +func (f fakeFilterKey) GetOperatorOrDefault(defaultOp string) string { + if f.operator != "" { + return f.operator + } + return defaultOp +} + +func (f fakeFilterKey) Equal(other FilterKey) bool { + if otherKey, isOk := other.(fakeFilterKey); isOk && f.operator == otherKey.operator && f.value == otherKey.value { + return true + } + return false +} + +var _ FilterKey = (*fakeFilterKey)(nil) + +func TestMatchWithLogicalOperators(t *testing.T) { + testCases := []struct { + name string + conditions map[FilterKey]any + data any + wantMatch bool + wantErr bool + }{ + { + name: "_eq", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_eq", nil}: "hello", + }, + data: "hello", + wantMatch: true, + }, + { + name: "_gt (true)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_gt", nil}: 10, + }, + data: 20, + wantMatch: true, + }, + { + name: "_gt (false)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_gt", nil}: 50, + }, + data: 20, + wantMatch: false, + }, + { + name: "_lt (true)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_lt", nil}: 5, + }, + data: 3, + wantMatch: true, + }, + { + name: "_lt (false)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_lt", nil}: 2, + }, + data: 3, + wantMatch: false, + }, + { + name: "_le (true)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_le", nil}: 5, + }, + data: 5, + wantMatch: true, + }, + { + name: "_le (false)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_le", nil}: 5, + }, + data: 6, + wantMatch: false, + }, + { + name: "_ge (true)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_ge", nil}: 5, + }, + data: 5, + wantMatch: true, + }, + { + name: "_ge (false)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_ge", nil}: 5, + }, + data: 4, + wantMatch: false, + }, + { + name: "Unsupported operator", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_xyz", nil}: "test", + }, + data: map[string]any{ + "key": "test", + }, + wantErr: true, + }, + { + name: "_in match", + conditions: map[FilterKey]any{ + fakeFilterKey{"_in", "item1"}: []any{"item1", "item2", "item3"}, + }, + wantMatch: true, + }, + { + name: "_in no match", + conditions: map[FilterKey]any{ + fakeFilterKey{"_in", "item4"}: []any{"item1", "item2", "item3"}, + }, + wantMatch: false, + }, + { + name: "_nin match", + conditions: map[FilterKey]any{ + fakeFilterKey{"_nin", "item4"}: []any{"item1", "item2", "item3"}, + }, + wantMatch: true, + }, + { + name: "_nin no match", + conditions: map[FilterKey]any{ + fakeFilterKey{"_nin", "item1"}: []any{"item1", "item2", "item3"}, + }, + wantMatch: false, + }, + { + name: "_and match", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_and", nil}: []any{ + map[FilterKey]any{&fakeFilterKey{"", "value1"}: "value1"}, + map[FilterKey]any{&fakeFilterKey{"", "value2"}: "value2"}, + }, + }, + wantMatch: true, + }, + { + name: "_and no match (first false)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_and", nil}: []any{ + map[FilterKey]any{&fakeFilterKey{"", "value1"}: "wrong"}, + map[FilterKey]any{&fakeFilterKey{"", "value2"}: "value2"}, + }, + }, + wantMatch: false, + }, + { + name: "_and no match (second false)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_and", nil}: []any{ + map[FilterKey]any{&fakeFilterKey{"", "value1"}: "value1"}, + map[FilterKey]any{&fakeFilterKey{"", "value2"}: "wrong"}, + }, + }, + wantMatch: false, + }, + { + name: "_and no match (both false)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_and", nil}: []any{ + map[FilterKey]any{&fakeFilterKey{"", "value1"}: "wrong"}, + map[FilterKey]any{&fakeFilterKey{"", "value2"}: "wrong"}, + }, + }, + wantMatch: false, + }, + { + name: "_or match (both true)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_or", nil}: []any{ + map[FilterKey]any{&fakeFilterKey{"", "value1"}: "value1"}, + map[FilterKey]any{&fakeFilterKey{"", "value2"}: "value2"}, + }, + }, + wantMatch: true, + }, + { + name: "_or match (first true)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_or", nil}: []any{ + map[FilterKey]any{&fakeFilterKey{"", "value1"}: "value1"}, + map[FilterKey]any{&fakeFilterKey{"", "value2"}: "wrong"}, + }, + }, + wantMatch: true, + }, + { + name: "_or match (second true)", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_or", nil}: []any{ + map[FilterKey]any{&fakeFilterKey{"", "value1"}: "wrong"}, + map[FilterKey]any{&fakeFilterKey{"", "value2"}: "value2"}, + }, + }, + wantMatch: true, + }, + { + name: "_or no match", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_or", nil}: []any{ + map[FilterKey]any{&fakeFilterKey{"", "value1"}: "wrong"}, + map[FilterKey]any{&fakeFilterKey{"", "value2"}: "wrong"}, + }, + }, + wantMatch: false, + }, + { + name: "_like match", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_like", nil}: "value", + }, + data: "value", + wantMatch: true, + }, + { + name: "_like no match", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_like", nil}: "value", + }, + data: "Value", + wantMatch: false, + }, + { + name: "_nlike match", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_nlike", nil}: "value", + }, + data: "Value", + wantMatch: true, + }, + { + name: "_nlike no match", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_nlike", nil}: "value", + }, + data: "value", + wantMatch: false, + }, + { + name: "_ilike match", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_ilike", nil}: "Value", + }, + data: "VALUE", + wantMatch: true, + }, + { + name: "_ilike no match", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_ilike", nil}: "Value", + }, + data: "wrong", + wantMatch: false, + }, + { + name: "_not match", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_not", nil}: map[FilterKey]any{ + &fakeFilterKey{"_eq", nil}: "value", + }, + }, + data: "wrong", + wantMatch: true, + }, + { + name: "_not no match", + conditions: map[FilterKey]any{ + &fakeFilterKey{"_not", nil}: map[FilterKey]any{ + &fakeFilterKey{"_eq", nil}: "value", + }, + }, + data: "value", + wantMatch: false, + }, + { + name: "nested properties", + conditions: map[FilterKey]any{ + &fakeFilterKey{"", nil}: map[FilterKey]any{ + &fakeFilterKey{"", nil}: map[FilterKey]any{ + &fakeFilterKey{"_eq", nil}: "value", + }, + }}, + data: "value", + wantMatch: true, + }, + { + name: "nested properties no match", + conditions: map[FilterKey]any{ + &fakeFilterKey{"", nil}: map[FilterKey]any{ + &fakeFilterKey{"", nil}: map[FilterKey]any{ + &fakeFilterKey{"_eq", nil}: "value", + }, + }}, + data: "other", + wantMatch: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + match, err := Match(tc.conditions, tc.data) + if tc.wantErr { + require.Error(t, err, "Test '%s' failed: Match returned an unexpected error: %v", tc.name, err) + return + } + require.NoError(t, err, "Test '%s' failed: Match returned an unexpected error: %v", tc.name, err) + + if match != tc.wantMatch { + t.Errorf("Test '%s' failed: Expected match result %v, got %v", tc.name, tc.wantMatch, match) + } + }) + } +} diff --git a/internal/connor/errors.go b/internal/connor/errors.go index 9b614fbde0..3813081098 100644 --- a/internal/connor/errors.go +++ b/internal/connor/errors.go @@ -16,6 +16,7 @@ import ( const ( errUnknownOperator string = "unknown operator" + errEmptyObject string = "can not use empty object" ) // Errors returnable from this package. @@ -24,8 +25,13 @@ const ( // Errors returned from this package may be tested against these errors with errors.Is. var ( ErrUnknownOperator = errors.New(errUnknownOperator) + ErrEmptyObject = errors.New(errEmptyObject) ) func NewErrUnknownOperator(operator string) error { return errors.New(errUnknownOperator, errors.NewKV("Operator", operator)) } + +func NewErrEmptyObject() error { + return errors.New(errEmptyObject) +} diff --git a/internal/connor/ge.go b/internal/connor/ge.go index 851c59c53f..4cd7548e3e 100644 --- a/internal/connor/ge.go +++ b/internal/connor/ge.go @@ -11,8 +11,7 @@ import ( // value is strictly larger than or equal to another. func ge(condition, data any) (bool, error) { if condition == nil { - // Everything is greater than or equal to nil - return true, nil + return false, nil } switch c := condition.(type) { diff --git a/internal/connor/gt.go b/internal/connor/gt.go index d5689ebd52..2c3710ab30 100644 --- a/internal/connor/gt.go +++ b/internal/connor/gt.go @@ -11,7 +11,7 @@ import ( // value is strictly larger than another. func gt(condition, data any) (bool, error) { if condition == nil { - return data != nil, nil + return false, nil } switch c := condition.(type) { diff --git a/internal/connor/le.go b/internal/connor/le.go index 0c89c8ffbf..b0dc5163ae 100644 --- a/internal/connor/le.go +++ b/internal/connor/le.go @@ -11,8 +11,7 @@ import ( // value is strictly less than another. func le(condition, data any) (bool, error) { if condition == nil { - // Only nil is less than or equal to nil - return data == nil, nil + return false, nil } switch c := condition.(type) { diff --git a/internal/connor/lt.go b/internal/connor/lt.go index a7e5e7bb03..a81b2f3e47 100644 --- a/internal/connor/lt.go +++ b/internal/connor/lt.go @@ -11,7 +11,6 @@ import ( // value is strictly less than another. func lt(condition, data any) (bool, error) { if condition == nil { - // Nothing is less than nil return false, nil } diff --git a/internal/connor/not.go b/internal/connor/not.go index 96fcd87ff8..d1ecadd4ee 100644 --- a/internal/connor/not.go +++ b/internal/connor/not.go @@ -3,6 +3,9 @@ package connor // not is an operator which performs object equality test // and returns the inverse of the result. func not(condition, data any) (bool, error) { + if m, ok := condition.(map[FilterKey]any); ok && len(m) == 0 { + return false, NewErrEmptyObject() + } m, err := eq(condition, data) if err != nil { return false, err diff --git a/internal/connor/rust.go b/internal/connor/rust.go new file mode 100644 index 0000000000..3d6bc5445c --- /dev/null +++ b/internal/connor/rust.go @@ -0,0 +1,59 @@ +// 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 connor + +/* +#include +#include +#cgo LDFLAGS: -L./../../libs -labi + +typedef struct { + char* data; + size_t cap; +} OutputString; + +extern bool match_conditions(const char* cond_json, const char* doc_json, OutputString* error); +*/ +import "C" + +import ( + "fmt" + "unsafe" +) + +const strBufferSize = 256 + +func allocateOutputString() (C.OutputString, func()) { + str := C.OutputString{ + data: (*C.char)(C.malloc(C.size_t(strBufferSize))), + cap: strBufferSize, + } + return str, func() { C.free(unsafe.Pointer(str.data)) } +} + +func callMatchConditionsABI(conditions, data string) (bool, error) { + cCond := C.CString(conditions) + cDoc := C.CString(data) + defer C.free(unsafe.Pointer(cCond)) + defer C.free(unsafe.Pointer(cDoc)) + + var cError, freeCError = allocateOutputString() + defer freeCError() + + result := C.match_conditions(cCond, cDoc, &cError) + errorStr := C.GoString(cError.data) + + if errorStr == "" { + return bool(result), nil + } + + return false, fmt.Errorf(errorStr) +} diff --git a/internal/planner/mapper/mapper.go b/internal/planner/mapper/mapper.go index be52066b54..a975fbb52c 100644 --- a/internal/planner/mapper/mapper.go +++ b/internal/planner/mapper/mapper.go @@ -1371,7 +1371,21 @@ func RunFilter(doc any, filter *Filter) (bool, error) { return true, nil } - return connor.Match(filter.Conditions, doc) + s := connorJSONSerializer{} + condJSON, err := s.serializeConditions(filter.Conditions) + if err != nil { + return false, err + } + var valJSON []byte + if d, ok := doc.(core.Doc); ok { + valJSON, err = s.serializeDoc(d) + } else { + valJSON, err = s.serializeDocField(doc) + } + if err != nil { + return false, err + } + return connor.MatchJSON(string(condJSON), string(valJSON)) } // equal compares the given Targetables and returns true if they can be considered equal. diff --git a/internal/planner/mapper/serialize.go b/internal/planner/mapper/serialize.go new file mode 100644 index 0000000000..d896c7af71 --- /dev/null +++ b/internal/planner/mapper/serialize.go @@ -0,0 +1,196 @@ +// 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 mapper + +import ( + "encoding/json" + "strings" + "time" + + "github.com/sourcenetwork/immutable" + + "github.com/sourcenetwork/defradb/internal/connor" + "github.com/sourcenetwork/defradb/internal/core" +) + +type connorJSONSerializer struct{} + +func (s connorJSONSerializer) serializeConditions(conditions map[connor.FilterKey]any) ([]byte, error) { + return json.Marshal(s.processCondition(conditions)) +} + +func (s connorJSONSerializer) processCondition(conditions any) map[string]any { + serEl := func(key connor.FilterKey, value any) map[string]any { + if op, ok := key.(*Operator); ok { + return map[string]any{strings.ToUpper(op.Operation[1:]): s.processOp(op.Operation, value)} + } else if prop, ok := key.(*PropertyIndex); ok { + return map[string]any{"PROP": s.processProp(prop, value)} + } + return nil + } + + if m, ok := conditions.(map[connor.FilterKey]any); ok { + if len(m) > 1 { + andCont := make([]any, 0, len(m)) + for key, value := range m { + andCont = append(andCont, serEl(key, value)) + } + return map[string]any{"AND": andCont} + } else { + for key, value := range m { + return serEl(key, value) + } + } + } + return nil +} + +func (s connorJSONSerializer) processProp(prop *PropertyIndex, val any) map[string]any { + return map[string]any{ + "index": prop.Index, + "condition": s.processCondition(val), + } +} + +func (s connorJSONSerializer) processOp(op string, val any) any { + switch op { + case "_and", "_or": + if arr, ok := val.([]any); ok { + arrResult := make([]any, len(arr)) + for i, v := range arr { + arrResult[i] = s.processCondition(v) + } + return arrResult + } + case "_not": + return s.processCondition(val) + } + return s.processField(val) +} + +func (s connorJSONSerializer) processField(val any) map[string]any { + if val == nil { + return nil + } + switch v := val.(type) { + case bool: + return map[string]any{"Bool": v} + case int32, int64: + return map[string]any{"Int": v} + case float32, float64: + return map[string]any{"Float": v} + case string: + return map[string]any{"String": v} + case time.Time: + return map[string]any{"DateTime": v} + case core.Doc: + return map[string]any{"Doc": s.processDoc(v)} + case []bool: + return map[string]any{"BoolArray": v} + case []int32, []int64: + return map[string]any{"IntArray": v} + case []float32, []float64: + return map[string]any{"FloatArray": v} + case []string: + return map[string]any{"StringArray": v} + case []time.Time: + return map[string]any{"DateTimeArray": v} + case []core.Doc: + return map[string]any{"DocArray": s.processDocArray(v)} + case immutable.Option[bool]: + return unwrapOption("Bool", v) + case immutable.Option[int64]: + return unwrapOption("Int", v) + case immutable.Option[float64]: + return unwrapOption("Float", v) + case immutable.Option[string]: + return unwrapOption("String", v) + case immutable.Option[time.Time]: + return unwrapOption("DateTime", v) + case []any: + return s.processAnyArr(v) + default: + return nil + } +} + +func unwrapOption[T any](t string, opt immutable.Option[T]) map[string]any { + if opt.HasValue() { + return map[string]any{t: opt.Value()} + } + return nil +} + +func (s connorJSONSerializer) processAnyArr(arr []any) map[string]any { + if len(arr) == 0 { + return nil + } + var el any + for _, v := range arr { + if v != nil { + el = v + break + } + } + switch el.(type) { + case bool: + return s.toArrayOrOptArray("BoolArray", arr) + case int32, int64: + return s.toArrayOrOptArray("IntArray", arr) + case float32, float64: + return s.toArrayOrOptArray("FloatArray", arr) + case string: + return s.toArrayOrOptArray("StringArray", arr) + case time.Time: + return s.toArrayOrOptArray("DateTimeArray", arr) + case core.Doc: + result := make([]map[string]any, len(arr)) + for i, doc := range arr { + result[i] = s.processDoc(doc.(core.Doc)) + } + return map[string]any{"DocArray": result} + default: + return nil + } +} + +func (s connorJSONSerializer) toArrayOrOptArray(fieldType string, arr []any) map[string]any { + for _, v := range arr { + if v == nil { + return map[string]any{"Optional" + fieldType: arr} + } + } + return map[string]any{fieldType: arr} +} + +func (s connorJSONSerializer) processDocArray(docs []core.Doc) []map[string]any { + result := make([]map[string]any, len(docs)) + for i, doc := range docs { + result[i] = s.processDoc(doc) + } + return result +} + +func (s connorJSONSerializer) serializeDoc(doc core.Doc) ([]byte, error) { + return json.Marshal(s.processDoc(doc)) +} + +func (s connorJSONSerializer) serializeDocField(field any) ([]byte, error) { + return json.Marshal(s.processField(field)) +} + +func (s connorJSONSerializer) processDoc(doc core.Doc) map[string]any { + fields := make([]any, len(doc.Fields)) + for i, field := range doc.Fields { + fields[i] = s.processField(field) + } + return map[string]any{"fields": fields} +} diff --git a/libs/.gitkeep b/libs/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/integration/query/simple/with_filter/with_ge_datetime_test.go b/tests/integration/query/simple/with_filter/with_ge_datetime_test.go index 69eddcd9c4..2854ad335e 100644 --- a/tests/integration/query/simple/with_filter/with_ge_datetime_test.go +++ b/tests/integration/query/simple/with_filter/with_ge_datetime_test.go @@ -127,14 +127,7 @@ func TestQuerySimpleWithDateTimeGEFilterBlockWithNilValue(t *testing.T) { }`, }, }, - Results: []map[string]any{ - { - "Name": "Bob", - }, - { - "Name": "John", - }, - }, + Results: []map[string]any{}, } executeTestCase(t, test) diff --git a/tests/integration/query/simple/with_filter/with_ge_float_test.go b/tests/integration/query/simple/with_filter/with_ge_float_test.go index f35726bfa2..81a63347fb 100644 --- a/tests/integration/query/simple/with_filter/with_ge_float_test.go +++ b/tests/integration/query/simple/with_filter/with_ge_float_test.go @@ -125,14 +125,7 @@ func TestQuerySimpleWithHeightMGEFilterBlockWithNilValue(t *testing.T) { }`, }, }, - Results: []map[string]any{ - { - "Name": "Bob", - }, - { - "Name": "John", - }, - }, + Results: []map[string]any{}, } executeTestCase(t, test) diff --git a/tests/integration/query/simple/with_filter/with_ge_int_test.go b/tests/integration/query/simple/with_filter/with_ge_int_test.go index e780bef687..65415f1c2b 100644 --- a/tests/integration/query/simple/with_filter/with_ge_int_test.go +++ b/tests/integration/query/simple/with_filter/with_ge_int_test.go @@ -95,14 +95,7 @@ func TestQuerySimpleWithIntGEFilterBlockWithNilValue(t *testing.T) { }`, }, }, - Results: []map[string]any{ - { - "Name": "Bob", - }, - { - "Name": "John", - }, - }, + Results: []map[string]any{}, } executeTestCase(t, test) diff --git a/tests/integration/query/simple/with_filter/with_gt_datetime_test.go b/tests/integration/query/simple/with_filter/with_gt_datetime_test.go index 468dcf07e5..c8a73f8b09 100644 --- a/tests/integration/query/simple/with_filter/with_gt_datetime_test.go +++ b/tests/integration/query/simple/with_filter/with_gt_datetime_test.go @@ -127,11 +127,7 @@ func TestQuerySimpleWithDateTimeGTFilterBlockWithNilValue(t *testing.T) { }`, }, }, - Results: []map[string]any{ - { - "Name": "John", - }, - }, + Results: []map[string]any{}, } executeTestCase(t, test) diff --git a/tests/integration/query/simple/with_filter/with_gt_float_test.go b/tests/integration/query/simple/with_filter/with_gt_float_test.go index c0152b976e..2450e7c5a7 100644 --- a/tests/integration/query/simple/with_filter/with_gt_float_test.go +++ b/tests/integration/query/simple/with_filter/with_gt_float_test.go @@ -148,11 +148,7 @@ func TestQuerySimpleWithFloatGreaterThanFilterBlockWithNullFilterValue(t *testin }`, }, }, - Results: []map[string]any{ - { - "Name": "John", - }, - }, + Results: []map[string]any{}, } executeTestCase(t, test) diff --git a/tests/integration/query/simple/with_filter/with_gt_int_test.go b/tests/integration/query/simple/with_filter/with_gt_int_test.go index ecafd44ee4..2338960bc4 100644 --- a/tests/integration/query/simple/with_filter/with_gt_int_test.go +++ b/tests/integration/query/simple/with_filter/with_gt_int_test.go @@ -124,11 +124,7 @@ func TestQuerySimpleWithIntGreaterThanFilterBlockWithNullFilterValue(t *testing. }`, }, }, - Results: []map[string]any{ - { - "Name": "John", - }, - }, + Results: []map[string]any{}, } executeTestCase(t, test) diff --git a/tests/integration/query/simple/with_filter/with_le_datetime_test.go b/tests/integration/query/simple/with_filter/with_le_datetime_test.go index 051a97de43..e72270412a 100644 --- a/tests/integration/query/simple/with_filter/with_le_datetime_test.go +++ b/tests/integration/query/simple/with_filter/with_le_datetime_test.go @@ -101,11 +101,7 @@ func TestQuerySimpleWithDateTimeLEFilterBlockWithNullValue(t *testing.T) { }`, }, }, - Results: []map[string]any{ - { - "Name": "Bob", - }, - }, + Results: []map[string]any{}, } executeTestCase(t, test) diff --git a/tests/integration/query/simple/with_filter/with_le_float_test.go b/tests/integration/query/simple/with_filter/with_le_float_test.go index 45be20cb44..f8ef18972c 100644 --- a/tests/integration/query/simple/with_filter/with_le_float_test.go +++ b/tests/integration/query/simple/with_filter/with_le_float_test.go @@ -125,11 +125,7 @@ func TestQuerySimpleWithFloatLEFilterBlockWithNullValue(t *testing.T) { }`, }, }, - Results: []map[string]any{ - { - "Name": "Bob", - }, - }, + Results: []map[string]any{}, } executeTestCase(t, test) diff --git a/tests/integration/query/simple/with_filter/with_le_int_test.go b/tests/integration/query/simple/with_filter/with_le_int_test.go index 2d9f543b12..860cb15578 100644 --- a/tests/integration/query/simple/with_filter/with_le_int_test.go +++ b/tests/integration/query/simple/with_filter/with_le_int_test.go @@ -95,11 +95,7 @@ func TestQuerySimpleWithIntLEFilterBlockWithNullValue(t *testing.T) { }`, }, }, - Results: []map[string]any{ - { - "Name": "Bob", - }, - }, + Results: []map[string]any{}, } executeTestCase(t, test) diff --git a/tests/integration/query/simple/with_filter/with_not_test.go b/tests/integration/query/simple/with_filter/with_not_test.go index 2ce454a358..278de118c1 100644 --- a/tests/integration/query/simple/with_filter/with_not_test.go +++ b/tests/integration/query/simple/with_filter/with_not_test.go @@ -177,7 +177,7 @@ func TestQuerySimple_WithEmptyNotFilter_ReturnError(t *testing.T) { }`, }, }, - Results: []map[string]any{}, + ExpectedError: "object", } executeTestCase(t, test)