github.com/weaviate/weaviate@v1.24.6/adapters/handlers/graphql/local/get/class_builder_references.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package get 13 14 import ( 15 "fmt" 16 17 "github.com/tailor-inc/graphql" 18 "github.com/weaviate/weaviate/entities/models" 19 "github.com/weaviate/weaviate/entities/schema" 20 "github.com/weaviate/weaviate/entities/search" 21 "golang.org/x/text/cases" 22 "golang.org/x/text/language" 23 ) 24 25 func (b *classBuilder) referenceField(propertyType schema.PropertyDataType, 26 property *models.Property, className string, 27 ) *graphql.Field { 28 refClasses := propertyType.Classes() 29 propertyName := cases.Title(language.Und, cases.NoLower).String(property.Name) 30 dataTypeClasses := []*graphql.Object{} 31 32 for _, refClassName := range refClasses { 33 // is a local ref 34 refClass, ok := b.knownClasses[string(refClassName)] 35 if !ok { 36 panic(fmt.Sprintf("buildGetClass: unknown referenced class type for %s.%s; %s", 37 className, property.Name, refClassName)) 38 } 39 40 dataTypeClasses = append(dataTypeClasses, refClass) 41 } 42 43 if (len(dataTypeClasses)) == 0 { 44 // this could be the case when we only have network-refs, but all network 45 // refs were invalid (e.g. because the peers are gone). In this case we 46 // must return (nil) early, otherwise graphql will error because it has a 47 // union field with an empty list of unions. 48 return nil 49 } 50 51 dataTypeClasses = append(dataTypeClasses, b.beaconClass) 52 53 classUnion := graphql.NewUnion(graphql.UnionConfig{ 54 Name: fmt.Sprintf("%s%s%s", className, propertyName, "Obj"), 55 Types: dataTypeClasses, 56 ResolveType: makeResolveClassUnionType(&b.knownClasses), 57 Description: property.Description, 58 }) 59 60 return &graphql.Field{ 61 Type: graphql.NewList(classUnion), 62 Description: property.Description, 63 Resolve: makeResolveRefField(), 64 } 65 } 66 67 func makeResolveClassUnionType(knownClasses *map[string]*graphql.Object) graphql.ResolveTypeFn { 68 return func(p graphql.ResolveTypeParams) *graphql.Object { 69 valueMap := p.Value.(map[string]interface{}) 70 refType := valueMap["__refClassType"].(string) 71 switch refType { 72 case "local": 73 className := valueMap["__refClassName"].(string) 74 classObj, ok := (*knownClasses)[className] 75 if !ok { 76 panic(fmt.Errorf( 77 "local ref refers to class '%s', but no such kind exists in the peer network", className)) 78 } 79 return classObj 80 default: 81 panic(fmt.Sprintf("unknown ref type %#v", refType)) 82 } 83 } 84 } 85 86 func makeResolveRefField() graphql.FieldResolveFn { 87 return func(p graphql.ResolveParams) (interface{}, error) { 88 if p.Source.(map[string]interface{})[p.Info.FieldName] == nil { 89 return nil, nil 90 } 91 92 items, ok := p.Source.(map[string]interface{})[p.Info.FieldName].([]interface{}) 93 if !ok { 94 // could be a models.MultipleRef which would indicate that we found only 95 // unresolved references, this is the case when accepts refs to types 96 // ClassA and ClassB and the object only contains refs to one type (e.g. 97 // ClassA). Now if the user only asks for resolving all of the other type 98 // (i.e. ClassB), then all results would be returned unresolved (as 99 // models.MultipleRef). 100 101 return nil, nil 102 } 103 results := make([]interface{}, len(items)) 104 for i, item := range items { 105 switch v := item.(type) { 106 case search.LocalRef: 107 // inject some meta data so the ResolveType can determine the type 108 localRef := v.Fields 109 localRef["__refClassType"] = "local" 110 localRef["__refClassName"] = v.Class 111 results[i] = localRef 112 default: 113 return nil, fmt.Errorf("unsupported type, expected search.LocalRef or NetworkRef, got %T", v) 114 } 115 } 116 return results, nil 117 } 118 }