Spaces:
Running
Running
| // _ _ | |
| // __ _____ __ ___ ___ __ _| |_ ___ | |
| // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ | |
| // \ V V / __/ (_| |\ V /| | (_| | || __/ | |
| // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| | |
| // | |
| // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. | |
| // | |
| // CONTACT: [email protected] | |
| // | |
| package refcache | |
| import ( | |
| "context" | |
| "fmt" | |
| "testing" | |
| "time" | |
| "github.com/go-openapi/strfmt" | |
| "github.com/stretchr/testify/assert" | |
| "github.com/stretchr/testify/require" | |
| "github.com/weaviate/weaviate/entities/additional" | |
| "github.com/weaviate/weaviate/entities/models" | |
| "github.com/weaviate/weaviate/entities/multi" | |
| "github.com/weaviate/weaviate/entities/search" | |
| ) | |
| func TestResolver(t *testing.T) { | |
| id1 := "df5d4e49-0c56-4b87-ade1-3d46cc9b425f" | |
| id2 := "3a08d808-8eb5-49ee-86b2-68b6035e8b69" | |
| t.Run("with nil input", func(t *testing.T) { | |
| r := NewResolver(newFakeCacher()) | |
| res, err := r.Do(context.Background(), nil, nil, additional.Properties{}) | |
| require.Nil(t, err) | |
| assert.Nil(t, res) | |
| }) | |
| t.Run("with nil-schemas", func(t *testing.T) { | |
| r := NewResolver(newFakeCacher()) | |
| input := []search.Result{ | |
| { | |
| ID: "foo", | |
| ClassName: "BestClass", | |
| }, | |
| } | |
| expected := input | |
| res, err := r.Do(context.Background(), input, nil, additional.Properties{}) | |
| require.Nil(t, err) | |
| assert.Equal(t, expected, res) | |
| }) | |
| t.Run("with single ref but no select props", func(t *testing.T) { | |
| r := NewResolver(newFakeCacher()) | |
| input := []search.Result{ | |
| { | |
| ID: "foo", | |
| ClassName: "BestClass", | |
| Schema: map[string]interface{}{ | |
| "refProp": models.MultipleRef{ | |
| &models.SingleRef{ | |
| Beacon: "weaviate://localhost/123", | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| expected := input | |
| res, err := r.Do(context.Background(), input, nil, additional.Properties{}) | |
| require.Nil(t, err) | |
| assert.Equal(t, expected, res) | |
| }) | |
| t.Run("with single ref with vector and matching select prop", func(t *testing.T) { | |
| getInput := func() []search.Result { | |
| return []search.Result{ | |
| { | |
| ID: "foo", | |
| ClassName: "BestClass", | |
| Schema: map[string]interface{}{ | |
| "refProp": models.MultipleRef{ | |
| &models.SingleRef{ | |
| Beacon: strfmt.URI(fmt.Sprintf("weaviate://localhost/%s", id1)), | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| } | |
| getResolver := func() *Resolver { | |
| cacher := newFakeCacher() | |
| r := NewResolver(cacher) | |
| cacher.lookup[multi.Identifier{ID: id1, ClassName: "SomeClass"}] = search.Result{ | |
| ClassName: "SomeClass", | |
| ID: strfmt.UUID(id1), | |
| Schema: map[string]interface{}{ | |
| "bar": "some string", | |
| }, | |
| Vector: []float32{0.1, 0.2}, | |
| } | |
| return r | |
| } | |
| getSelectProps := func(withVector bool) search.SelectProperties { | |
| return search.SelectProperties{ | |
| search.SelectProperty{ | |
| Name: "refProp", | |
| Refs: []search.SelectClass{ | |
| { | |
| ClassName: "SomeClass", | |
| RefProperties: search.SelectProperties{ | |
| search.SelectProperty{ | |
| Name: "bar", | |
| IsPrimitive: true, | |
| }, | |
| }, | |
| AdditionalProperties: additional.Properties{ | |
| Vector: withVector, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| } | |
| getExpectedResult := func(withVector bool) []search.Result { | |
| fields := map[string]interface{}{ | |
| "bar": "some string", | |
| } | |
| if withVector { | |
| fields["vector"] = []float32{0.1, 0.2} | |
| } | |
| return []search.Result{ | |
| { | |
| ID: "foo", | |
| ClassName: "BestClass", | |
| Schema: map[string]interface{}{ | |
| "refProp": []interface{}{ | |
| search.LocalRef{ | |
| Class: "SomeClass", | |
| Fields: fields, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| } | |
| // ask for vector in ref property | |
| res, err := getResolver().Do(context.Background(), getInput(), getSelectProps(true), additional.Properties{}) | |
| require.Nil(t, err) | |
| assert.Equal(t, getExpectedResult(true), res) | |
| // don't ask for vector in ref property | |
| res, err = getResolver().Do(context.Background(), getInput(), getSelectProps(false), additional.Properties{}) | |
| require.Nil(t, err) | |
| assert.Equal(t, getExpectedResult(false), res) | |
| }) | |
| t.Run("with single ref with creation/update timestamps and matching select prop", func(t *testing.T) { | |
| now := time.Now().UnixMilli() | |
| getInput := func() []search.Result { | |
| return []search.Result{ | |
| { | |
| ID: "foo", | |
| ClassName: "BestClass", | |
| Schema: map[string]interface{}{ | |
| "refProp": models.MultipleRef{ | |
| &models.SingleRef{ | |
| Beacon: strfmt.URI(fmt.Sprintf("weaviate://localhost/%s", id1)), | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| } | |
| getResolver := func() *Resolver { | |
| cacher := newFakeCacher() | |
| r := NewResolver(cacher) | |
| cacher.lookup[multi.Identifier{ID: id1, ClassName: "SomeClass"}] = search.Result{ | |
| ClassName: "SomeClass", | |
| ID: strfmt.UUID(id1), | |
| Schema: map[string]interface{}{ | |
| "bar": "some string", | |
| }, | |
| Created: now, | |
| Updated: now, | |
| } | |
| return r | |
| } | |
| selectProps := search.SelectProperties{ | |
| search.SelectProperty{ | |
| Name: "refProp", | |
| Refs: []search.SelectClass{ | |
| { | |
| ClassName: "SomeClass", | |
| RefProperties: search.SelectProperties{ | |
| search.SelectProperty{ | |
| Name: "bar", | |
| IsPrimitive: true, | |
| }, | |
| }, | |
| AdditionalProperties: additional.Properties{ | |
| CreationTimeUnix: true, | |
| LastUpdateTimeUnix: true, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| expected := []search.Result{ | |
| { | |
| ID: "foo", | |
| ClassName: "BestClass", | |
| Schema: map[string]interface{}{ | |
| "refProp": []interface{}{ | |
| search.LocalRef{ | |
| Class: "SomeClass", | |
| Fields: map[string]interface{}{ | |
| "bar": "some string", | |
| "creationTimeUnix": now, | |
| "lastUpdateTimeUnix": now, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| res, err := getResolver().Do(context.Background(), getInput(), selectProps, additional.Properties{}) | |
| require.Nil(t, err) | |
| assert.Equal(t, expected, res) | |
| }) | |
| t.Run("with single ref and matching select prop", func(t *testing.T) { | |
| cacher := newFakeCacher() | |
| r := NewResolver(cacher) | |
| cacher.lookup[multi.Identifier{ID: id1, ClassName: "SomeClass"}] = search.Result{ | |
| ClassName: "SomeClass", | |
| ID: strfmt.UUID(id1), | |
| Schema: map[string]interface{}{ | |
| "bar": "some string", | |
| }, | |
| } | |
| input := []search.Result{ | |
| { | |
| ID: "foo", | |
| ClassName: "BestClass", | |
| Schema: map[string]interface{}{ | |
| "refProp": models.MultipleRef{ | |
| &models.SingleRef{ | |
| Beacon: strfmt.URI(fmt.Sprintf("weaviate://localhost/%s", id1)), | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| selectProps := search.SelectProperties{ | |
| search.SelectProperty{ | |
| Name: "refProp", | |
| Refs: []search.SelectClass{ | |
| { | |
| ClassName: "SomeClass", | |
| RefProperties: search.SelectProperties{ | |
| search.SelectProperty{ | |
| Name: "bar", | |
| IsPrimitive: true, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| expected := []search.Result{ | |
| { | |
| ID: "foo", | |
| ClassName: "BestClass", | |
| Schema: map[string]interface{}{ | |
| "refProp": []interface{}{ | |
| search.LocalRef{ | |
| Class: "SomeClass", | |
| Fields: map[string]interface{}{ | |
| "bar": "some string", | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| res, err := r.Do(context.Background(), input, selectProps, additional.Properties{}) | |
| require.Nil(t, err) | |
| assert.Equal(t, expected, res) | |
| }) | |
| t.Run("with a nested lookup", func(t *testing.T) { | |
| cacher := newFakeCacher() | |
| r := NewResolver(cacher) | |
| cacher.lookup[multi.Identifier{ID: id1, ClassName: "SomeClass"}] = search.Result{ | |
| ClassName: "SomeClass", | |
| ID: strfmt.UUID(id1), | |
| Schema: map[string]interface{}{ | |
| "primitive": "foobar", | |
| "ignoredRef": models.MultipleRef{ | |
| &models.SingleRef{ | |
| Beacon: strfmt.URI("weaviate://localhost/ignoreMe"), | |
| }, | |
| }, | |
| "nestedRef": models.MultipleRef{ | |
| &models.SingleRef{ | |
| Beacon: strfmt.URI(fmt.Sprintf("weaviate://localhost/%s", id2)), | |
| }, | |
| }, | |
| }, | |
| } | |
| cacher.lookup[multi.Identifier{ID: id2, ClassName: "SomeNestedClass"}] = search.Result{ | |
| ClassName: "SomeNestedClass", | |
| ID: strfmt.UUID(id2), | |
| Schema: map[string]interface{}{ | |
| "name": "John Doe", | |
| }, | |
| } | |
| input := []search.Result{ | |
| { | |
| ID: "foo", | |
| ClassName: "BestClass", | |
| Schema: map[string]interface{}{ | |
| "refProp": models.MultipleRef{ | |
| &models.SingleRef{ | |
| Beacon: strfmt.URI(fmt.Sprintf("weaviate://localhost/%s", id1)), | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| selectProps := search.SelectProperties{ | |
| search.SelectProperty{ | |
| Name: "refProp", | |
| Refs: []search.SelectClass{ | |
| { | |
| ClassName: "SomeClass", | |
| RefProperties: search.SelectProperties{ | |
| search.SelectProperty{ | |
| Name: "primitive", | |
| IsPrimitive: true, | |
| }, | |
| search.SelectProperty{ | |
| Name: "nestedRef", | |
| Refs: []search.SelectClass{ | |
| { | |
| ClassName: "SomeNestedClass", | |
| RefProperties: []search.SelectProperty{ | |
| { | |
| Name: "name", | |
| IsPrimitive: true, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| expected := []search.Result{ | |
| { | |
| ID: "foo", | |
| ClassName: "BestClass", | |
| Schema: map[string]interface{}{ | |
| "refProp": []interface{}{ | |
| search.LocalRef{ | |
| Class: "SomeClass", | |
| Fields: map[string]interface{}{ | |
| "primitive": "foobar", | |
| "ignoredRef": models.MultipleRef{ | |
| &models.SingleRef{ | |
| Beacon: strfmt.URI("weaviate://localhost/ignoreMe"), | |
| }, | |
| }, | |
| "nestedRef": []interface{}{ | |
| search.LocalRef{ | |
| Class: "SomeNestedClass", | |
| Fields: map[string]interface{}{ | |
| "name": "John Doe", | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| res, err := r.Do(context.Background(), input, selectProps, additional.Properties{}) | |
| require.Nil(t, err) | |
| assert.Equal(t, expected, res) | |
| }) | |
| t.Run("with single ref with vector and matching select prop and group", func(t *testing.T) { | |
| getInput := func() []search.Result { | |
| return []search.Result{ | |
| { | |
| ID: "foo", | |
| ClassName: "BestClass", | |
| Schema: map[string]interface{}{ | |
| "refProp": models.MultipleRef{ | |
| &models.SingleRef{ | |
| Beacon: strfmt.URI(fmt.Sprintf("weaviate://localhost/%s", id1)), | |
| }, | |
| }, | |
| }, | |
| AdditionalProperties: models.AdditionalProperties{ | |
| "group": &additional.Group{ | |
| Hits: []map[string]interface{}{ | |
| { | |
| "nestedRef": models.MultipleRef{ | |
| &models.SingleRef{ | |
| Beacon: strfmt.URI(fmt.Sprintf("weaviate://localhost/SomeNestedClass/%s", id2)), | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| } | |
| getResolver := func() *Resolver { | |
| cacher := newFakeCacher() | |
| r := NewResolverWithGroup(cacher) | |
| cacher.lookup[multi.Identifier{ID: id1, ClassName: "SomeClass"}] = search.Result{ | |
| ClassName: "SomeClass", | |
| ID: strfmt.UUID(id1), | |
| Schema: map[string]interface{}{ | |
| "bar": "some string", | |
| }, | |
| Vector: []float32{0.1, 0.2}, | |
| } | |
| cacher.lookup[multi.Identifier{ID: id2, ClassName: "SomeNestedClass"}] = search.Result{ | |
| ClassName: "SomeNestedClass", | |
| ID: strfmt.UUID(id2), | |
| Schema: map[string]interface{}{ | |
| "name": "John Doe", | |
| }, | |
| } | |
| return r | |
| } | |
| getSelectProps := func(withVector bool) search.SelectProperties { | |
| return search.SelectProperties{ | |
| search.SelectProperty{ | |
| Name: "refProp", | |
| Refs: []search.SelectClass{ | |
| { | |
| ClassName: "SomeClass", | |
| RefProperties: search.SelectProperties{ | |
| search.SelectProperty{ | |
| Name: "bar", | |
| IsPrimitive: true, | |
| }, | |
| }, | |
| AdditionalProperties: additional.Properties{ | |
| Vector: withVector, | |
| }, | |
| }, | |
| }, | |
| }, | |
| search.SelectProperty{ | |
| Name: "_additional:group:hits:nestedRef", | |
| Refs: []search.SelectClass{ | |
| { | |
| ClassName: "SomeNestedClass", | |
| RefProperties: []search.SelectProperty{ | |
| { | |
| Name: "name", | |
| IsPrimitive: true, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| } | |
| getExpectedResult := func(withVector bool) []search.Result { | |
| fields := map[string]interface{}{ | |
| "bar": "some string", | |
| } | |
| if withVector { | |
| fields["vector"] = []float32{0.1, 0.2} | |
| } | |
| return []search.Result{ | |
| { | |
| ID: "foo", | |
| ClassName: "BestClass", | |
| Schema: map[string]interface{}{ | |
| "refProp": []interface{}{ | |
| search.LocalRef{ | |
| Class: "SomeClass", | |
| Fields: fields, | |
| }, | |
| }, | |
| }, | |
| AdditionalProperties: models.AdditionalProperties{ | |
| "group": &additional.Group{ | |
| Hits: []map[string]interface{}{ | |
| { | |
| "nestedRef": []interface{}{ | |
| search.LocalRef{ | |
| Class: "SomeNestedClass", | |
| Fields: map[string]interface{}{ | |
| "name": "John Doe", | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| } | |
| // ask for vector in ref property | |
| res, err := getResolver().Do(context.Background(), getInput(), getSelectProps(true), additional.Properties{}) | |
| require.Nil(t, err) | |
| assert.Equal(t, getExpectedResult(true), res) | |
| // don't ask for vector in ref property | |
| res, err = getResolver().Do(context.Background(), getInput(), getSelectProps(false), additional.Properties{}) | |
| require.Nil(t, err) | |
| assert.Equal(t, getExpectedResult(false), res) | |
| }) | |
| } | |
| func newFakeCacher() *fakeCacher { | |
| return &fakeCacher{ | |
| lookup: map[multi.Identifier]search.Result{}, | |
| } | |
| } | |
| type fakeCacher struct { | |
| lookup map[multi.Identifier]search.Result | |
| } | |
| func (f *fakeCacher) Build(ctx context.Context, objects []search.Result, properties search.SelectProperties, | |
| additional additional.Properties, | |
| ) error { | |
| return nil | |
| } | |
| func (f *fakeCacher) Get(si multi.Identifier) (search.Result, bool) { | |
| res, ok := f.lookup[si] | |
| return res, ok | |
| } | |