github.com/weaviate/weaviate@v1.24.6/adapters/handlers/graphql/local/get/class_builder.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/weaviate/weaviate/adapters/handlers/graphql/local/common_filters" 18 19 "github.com/pkg/errors" 20 "github.com/sirupsen/logrus" 21 "github.com/tailor-inc/graphql" 22 "github.com/weaviate/weaviate/adapters/handlers/graphql/descriptions" 23 "github.com/weaviate/weaviate/entities/models" 24 "github.com/weaviate/weaviate/entities/schema" 25 ) 26 27 type classBuilder struct { 28 schema *schema.Schema 29 knownClasses map[string]*graphql.Object 30 beaconClass *graphql.Object 31 logger logrus.FieldLogger 32 modulesProvider ModulesProvider 33 } 34 35 func newClassBuilder(schema *schema.Schema, logger logrus.FieldLogger, 36 modulesProvider ModulesProvider, 37 ) *classBuilder { 38 b := &classBuilder{} 39 40 b.logger = logger 41 b.schema = schema 42 b.modulesProvider = modulesProvider 43 44 b.initKnownClasses() 45 b.initBeaconClass() 46 47 return b 48 } 49 50 func (b *classBuilder) initKnownClasses() { 51 b.knownClasses = map[string]*graphql.Object{} 52 } 53 54 func (b *classBuilder) initBeaconClass() { 55 b.beaconClass = graphql.NewObject(graphql.ObjectConfig{ 56 Name: "Beacon", 57 Fields: graphql.Fields{ 58 "beacon": &graphql.Field{ 59 Type: graphql.String, 60 }, 61 }, 62 }) 63 } 64 65 func (b *classBuilder) objects() (*graphql.Object, error) { 66 return b.kinds(b.schema.Objects) 67 } 68 69 func (b *classBuilder) kinds(kindSchema *models.Schema) (*graphql.Object, error) { 70 // needs to be defined outside the individual class as there can only be one definition of an enum 71 fusionAlgoEnum := graphql.NewEnum(graphql.EnumConfig{ 72 Name: "FusionEnum", 73 Values: graphql.EnumValueConfigMap{ 74 "rankedFusion": &graphql.EnumValueConfig{ 75 Value: common_filters.HybridRankedFusion, 76 }, 77 "relativeScoreFusion": &graphql.EnumValueConfig{ 78 Value: common_filters.HybridRelativeScoreFusion, 79 }, 80 }, 81 }) 82 classFields := graphql.Fields{} 83 for _, class := range kindSchema.Classes { 84 classField, err := b.classField(class, fusionAlgoEnum) 85 if err != nil { 86 return nil, fmt.Errorf("Could not build class for %s", class.Class) 87 } 88 classFields[class.Class] = classField 89 } 90 91 classes := graphql.NewObject(graphql.ObjectConfig{ 92 Name: "GetObjectsObj", 93 Fields: classFields, 94 Description: descriptions.GetObjectsActionsObj, 95 }) 96 97 return classes, nil 98 } 99 100 func (b *classBuilder) classField(class *models.Class, fusionEnum *graphql.Enum) (*graphql.Field, error) { 101 classObject := b.classObject(class) 102 b.knownClasses[class.Class] = classObject 103 classField := buildGetClassField(classObject, class, b.modulesProvider, fusionEnum) 104 return &classField, nil 105 } 106 107 func (b *classBuilder) classObject(class *models.Class) *graphql.Object { 108 return graphql.NewObject(graphql.ObjectConfig{ 109 Name: class.Class, 110 Fields: (graphql.FieldsThunk)(func() graphql.Fields { 111 classProperties := graphql.Fields{} 112 for _, property := range class.Properties { 113 propertyType, err := b.schema.FindPropertyDataType(property.DataType) 114 if err != nil { 115 if errors.Is(err, schema.ErrRefToNonexistentClass) { 116 // This is a common case when a class which is referenced 117 // by another class is deleted, leaving the referencing 118 // class with an invalid reference property. Panicking 119 // is not necessary here 120 b.logger.WithField("action", "graphql_rebuild"). 121 Warnf("ignoring ref prop %q on class %q, because it contains reference to nonexistent class %q", 122 property.Name, class.Class, property.DataType) 123 124 continue 125 } else { 126 // We can't return an error in this FieldsThunk function, so we need to panic 127 panic(fmt.Sprintf("buildGetClass: wrong propertyType for %s.%s; %s", 128 class.Class, property.Name, err.Error())) 129 } 130 } 131 132 if propertyType.IsPrimitive() { 133 classProperties[property.Name] = b.primitiveField(propertyType, property, 134 class.Class) 135 } else if propertyType.IsNested() { 136 classProperties[property.Name] = b.nestedField(propertyType, property, 137 class.Class) 138 } else { 139 classProperties[property.Name] = b.referenceField(propertyType, property, 140 class.Class) 141 } 142 } 143 144 b.additionalFields(classProperties, class) 145 146 return classProperties 147 }), 148 Description: class.Description, 149 }) 150 } 151 152 func (b *classBuilder) additionalFields(classProperties graphql.Fields, class *models.Class) { 153 additionalProperties := graphql.Fields{} 154 additionalProperties["classification"] = b.additionalClassificationField(class) 155 additionalProperties["certainty"] = b.additionalCertaintyField(class) 156 additionalProperties["distance"] = b.additionalDistanceField(class) 157 additionalProperties["vector"] = b.additionalVectorField(class) 158 additionalProperties["vectors"] = b.additionalVectorsField(class) 159 additionalProperties["id"] = b.additionalIDField() 160 additionalProperties["creationTimeUnix"] = b.additionalCreationTimeUnix() 161 additionalProperties["lastUpdateTimeUnix"] = b.additionalLastUpdateTimeUnix() 162 additionalProperties["score"] = b.additionalScoreField() 163 additionalProperties["explainScore"] = b.additionalExplainScoreField() 164 additionalProperties["group"] = b.additionalGroupField(classProperties, class) 165 if replicationEnabled(class) { 166 additionalProperties["isConsistent"] = b.isConsistentField() 167 } 168 // module specific additional properties 169 if b.modulesProvider != nil { 170 for name, field := range b.modulesProvider.GetAdditionalFields(class) { 171 additionalProperties[name] = field 172 } 173 } 174 classProperties["_additional"] = &graphql.Field{ 175 Type: graphql.NewObject(graphql.ObjectConfig{ 176 Name: fmt.Sprintf("%sAdditional", class.Class), 177 Fields: additionalProperties, 178 }), 179 } 180 } 181 182 func (b *classBuilder) additionalIDField() *graphql.Field { 183 return &graphql.Field{ 184 Description: descriptions.GetClassUUID, 185 Type: graphql.String, 186 } 187 } 188 189 func (b *classBuilder) additionalClassificationField(class *models.Class) *graphql.Field { 190 return &graphql.Field{ 191 Type: graphql.NewObject(graphql.ObjectConfig{ 192 Name: fmt.Sprintf("%sAdditionalClassification", class.Class), 193 Fields: graphql.Fields{ 194 "id": &graphql.Field{Type: graphql.String}, 195 "basedOn": &graphql.Field{Type: graphql.NewList(graphql.String)}, 196 "scope": &graphql.Field{Type: graphql.NewList(graphql.String)}, 197 "classifiedFields": &graphql.Field{Type: graphql.NewList(graphql.String)}, 198 "completed": &graphql.Field{Type: graphql.String}, 199 }, 200 }), 201 } 202 } 203 204 func (b *classBuilder) additionalCertaintyField(class *models.Class) *graphql.Field { 205 return &graphql.Field{ 206 Type: graphql.Float, 207 } 208 } 209 210 func (b *classBuilder) additionalDistanceField(class *models.Class) *graphql.Field { 211 return &graphql.Field{ 212 Type: graphql.Float, 213 } 214 } 215 216 func (b *classBuilder) additionalVectorField(class *models.Class) *graphql.Field { 217 return &graphql.Field{ 218 Type: graphql.NewList(graphql.Float), 219 } 220 } 221 222 func (b *classBuilder) additionalVectorsField(class *models.Class) *graphql.Field { 223 if len(class.VectorConfig) > 0 { 224 fields := graphql.Fields{} 225 for targetVector := range class.VectorConfig { 226 fields[targetVector] = &graphql.Field{ 227 Name: fmt.Sprintf("%sAdditionalVectors%s", class.Class, targetVector), 228 Type: graphql.NewList(graphql.Float), 229 } 230 } 231 return &graphql.Field{ 232 Type: graphql.NewObject( 233 graphql.ObjectConfig{ 234 Name: fmt.Sprintf("%sAdditionalVectors", class.Class), 235 Fields: fields, 236 }, 237 ), 238 } 239 } 240 return nil 241 } 242 243 func (b *classBuilder) additionalCreationTimeUnix() *graphql.Field { 244 return &graphql.Field{ 245 Type: graphql.String, 246 } 247 } 248 249 func (b *classBuilder) additionalScoreField() *graphql.Field { 250 return &graphql.Field{ 251 Type: graphql.String, 252 } 253 } 254 255 func (b *classBuilder) additionalExplainScoreField() *graphql.Field { 256 return &graphql.Field{ 257 Type: graphql.String, 258 } 259 } 260 261 func (b *classBuilder) additionalLastUpdateTimeUnix() *graphql.Field { 262 return &graphql.Field{ 263 Type: graphql.String, 264 } 265 } 266 267 func (b *classBuilder) isConsistentField() *graphql.Field { 268 return &graphql.Field{ 269 Type: graphql.Boolean, 270 } 271 } 272 273 func (b *classBuilder) additionalGroupField(classProperties graphql.Fields, class *models.Class) *graphql.Field { 274 hitsFields := graphql.Fields{ 275 "_additional": &graphql.Field{ 276 Type: graphql.NewObject( 277 graphql.ObjectConfig{ 278 Name: fmt.Sprintf("%sAdditionalGroupHitsAdditional", class.Class), 279 Fields: graphql.Fields{ 280 "id": &graphql.Field{Type: graphql.String}, 281 "vector": &graphql.Field{Type: graphql.NewList(graphql.Float)}, 282 "distance": &graphql.Field{Type: graphql.Float}, 283 }, 284 }, 285 ), 286 }, 287 } 288 for name, field := range classProperties { 289 hitsFields[name] = field 290 } 291 return &graphql.Field{ 292 Type: graphql.NewObject(graphql.ObjectConfig{ 293 Name: fmt.Sprintf("%sAdditionalGroup", class.Class), 294 Fields: graphql.Fields{ 295 "id": &graphql.Field{Type: graphql.Int}, 296 "groupedBy": &graphql.Field{ 297 Type: graphql.NewObject(graphql.ObjectConfig{ 298 Name: fmt.Sprintf("%sAdditionalGroupGroupedBy", class.Class), 299 Fields: graphql.Fields{ 300 "path": &graphql.Field{ 301 Type: graphql.NewList(graphql.String), 302 }, 303 "value": &graphql.Field{ 304 Type: graphql.String, 305 }, 306 }, 307 }), 308 }, 309 310 "minDistance": &graphql.Field{Type: graphql.Float}, 311 "maxDistance": &graphql.Field{Type: graphql.Float}, 312 "count": &graphql.Field{Type: graphql.Int}, 313 "hits": &graphql.Field{ 314 Type: graphql.NewList(graphql.NewObject( 315 graphql.ObjectConfig{ 316 Name: fmt.Sprintf("%sAdditionalGroupHits", class.Class), 317 Fields: hitsFields, 318 }, 319 )), 320 }, 321 }, 322 }), 323 } 324 }