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  }