@@ -31,15 +31,13 @@ type FieldInfo struct {
3131 GraphQLType graphql.Type
3232 JSONType reflect.Kind
3333 IsArray bool
34- SampleValues []interface {}
3534 NestedFields map [string ]* FieldInfo // For array-of-objects, stores object structure
3635}
3736
3837// SchemaInfo holds discovered schema information
3938type SchemaInfo struct {
4039 Fields map [string ]* FieldInfo
4140 TotalObjects int
42- SampleObject map [string ]interface {}
4341}
4442
4543// CatalogSchema holds the complete discovered schema
@@ -334,8 +332,9 @@ func determineFieldType(value interface{}) (reflect.Kind, bool) {
334332 return reflect .TypeOf (firstElem ).Kind (), true
335333}
336334
335+ // maxSampleElements limits how many slice elements are examined when inferring
336+ // GraphQL types from catalog data, bounding work on large arrays.
337337const maxSampleElements = 10
338- const maxSampleValues = 10
339338
340339// analyzeFieldValue analyzes a field value and returns type info, sample value, and nested fields.
341340// For slices, it examines up to maxSampleElements to detect heterogeneous element types
@@ -427,7 +426,6 @@ func analyzeNestedObject(obj map[string]interface{}) map[string]*FieldInfo {
427426 GraphQLType : jsonTypeToGraphQL (jsonType , isArray , sampleValue ),
428427 JSONType : jsonType ,
429428 IsArray : isArray ,
430- SampleValues : []interface {}{value },
431429 }
432430 }
433431
@@ -437,15 +435,7 @@ func analyzeNestedObject(obj map[string]interface{}) map[string]*FieldInfo {
437435// mergeNestedFields merges discovered nested fields into existing ones
438436func mergeNestedFields (existing , new map [string ]* FieldInfo ) {
439437 for fieldName , newInfo := range new {
440- if existingInfo , ok := existing [fieldName ]; ok {
441- // Merge sample values
442- for _ , sample := range newInfo .SampleValues {
443- if len (existingInfo .SampleValues ) >= maxSampleValues {
444- break
445- }
446- existingInfo .SampleValues = appendUnique (existingInfo .SampleValues , sample )
447- }
448- } else {
438+ if _ , ok := existing [fieldName ]; ! ok {
449439 existing [fieldName ] = newInfo
450440 }
451441 }
@@ -472,7 +462,6 @@ func analyzeJSONObject(obj map[string]interface{}, info *SchemaInfo) {
472462 GraphQLType : jsonTypeToGraphQL (jsonType , isArray , sampleValue ),
473463 JSONType : jsonType ,
474464 IsArray : isArray ,
475- SampleValues : []interface {}{sampleValue },
476465 NestedFields : nestedFields ,
477466 }
478467 continue
@@ -484,11 +473,6 @@ func analyzeJSONObject(obj map[string]interface{}, info *SchemaInfo) {
484473 "graphqlField" , fieldName , "existingKey" , existing .OriginalName , "newKey" , key )
485474 }
486475
487- // Update existing field
488- if len (existing .SampleValues ) < maxSampleValues {
489- existing .SampleValues = appendUnique (existing .SampleValues , sampleValue )
490- }
491-
492476 // Merge nested fields if discovered
493477 if nestedFields == nil {
494478 continue
@@ -501,110 +485,48 @@ func analyzeJSONObject(obj map[string]interface{}, info *SchemaInfo) {
501485 }
502486}
503487
504- // appendUnique adds a value to slice if not already present, using JSON string as key for uniqueness
505- func appendUnique (slice []interface {}, value interface {}) []interface {} {
506- seen := make (map [string ]struct {}, len (slice ))
507-
508- for _ , existing := range slice {
509- key , err := json .Marshal (existing )
510- if err != nil {
511- continue // skip values that can't be marshaled
512- }
513- seen [string (key )] = struct {}{}
488+ // processMetaIntoSchema parses one meta blob and updates the catalog schema.
489+ // TotalObjects is incremented only on successful parse so pagination counts
490+ // reflect objects that can actually be returned by the ObjectLoader.
491+ func processMetaIntoSchema (catalogSchema * CatalogSchema , meta * declcfg.Meta ) {
492+ if meta .Schema == "" {
493+ return
514494 }
515-
516- valueKey , err := json . Marshal ( value )
517- if err != nil {
518- return slice // skip value if it can't be marshaled
495+ if catalogSchema . Schemas [ meta . Schema ] == nil {
496+ catalogSchema . Schemas [ meta . Schema ] = & SchemaInfo {
497+ Fields : make ( map [ string ] * FieldInfo ),
498+ }
519499 }
500+ info := catalogSchema .Schemas [meta .Schema ]
520501
521- if _ , exists := seen [string (valueKey )]; exists {
522- return slice
502+ var obj map [string ]interface {}
503+ if err := json .Unmarshal (meta .Blob , & obj ); err != nil {
504+ klog .V (4 ).InfoS ("skipping malformed meta blob during schema discovery" ,
505+ "schema" , meta .Schema , "name" , meta .Name , "error" , err )
506+ return
523507 }
524508
525- return append (slice , value )
509+ info .TotalObjects ++
510+ analyzeJSONObject (obj , info )
526511}
527512
528- // DiscoverSchemaFromMetas analyzes Meta objects to discover schema structure
513+ // DiscoverSchemaFromMetas analyzes Meta objects to discover schema structure.
529514func DiscoverSchemaFromMetas (metas []* declcfg.Meta ) (* CatalogSchema , error ) {
530- catalogSchema := & CatalogSchema {
531- Schemas : make (map [string ]* SchemaInfo ),
532- }
533-
534- // Process each meta object
515+ catalogSchema := & CatalogSchema {Schemas : make (map [string ]* SchemaInfo )}
535516 for _ , meta := range metas {
536- if meta .Schema == "" {
537- continue
538- }
539-
540- // Ensure schema info exists
541- if catalogSchema .Schemas [meta .Schema ] == nil {
542- catalogSchema .Schemas [meta .Schema ] = & SchemaInfo {
543- Fields : make (map [string ]* FieldInfo ),
544- TotalObjects : 0 ,
545- }
546- }
547-
548- info := catalogSchema .Schemas [meta .Schema ]
549- info .TotalObjects ++
550-
551- // Parse the JSON blob
552- var obj map [string ]interface {}
553- if err := json .Unmarshal (meta .Blob , & obj ); err != nil {
554- klog .V (4 ).InfoS ("skipping malformed meta blob during schema discovery" ,
555- "schema" , meta .Schema , "name" , meta .Name , "error" , err )
556- continue
557- }
558-
559- // Store a sample object for reference
560- if info .SampleObject == nil {
561- info .SampleObject = obj
562- }
563-
564- // Analyze general fields (including nested structures)
565- analyzeJSONObject (obj , info )
517+ processMetaIntoSchema (catalogSchema , meta )
566518 }
567-
568519 return catalogSchema , nil
569520}
570521
571522// DiscoverSchemaFromChannel performs streaming schema discovery, processing
572523// one meta at a time through a channel. Each meta's blob is parsed, analyzed,
573524// and then goes out of scope — avoiding accumulation of all blobs in memory.
574525func DiscoverSchemaFromChannel (metasChan <- chan * declcfg.Meta ) (* CatalogSchema , error ) {
575- catalogSchema := & CatalogSchema {
576- Schemas : make (map [string ]* SchemaInfo ),
577- }
578-
526+ catalogSchema := & CatalogSchema {Schemas : make (map [string ]* SchemaInfo )}
579527 for meta := range metasChan {
580- if meta .Schema == "" {
581- continue
582- }
583-
584- if catalogSchema .Schemas [meta .Schema ] == nil {
585- catalogSchema .Schemas [meta .Schema ] = & SchemaInfo {
586- Fields : make (map [string ]* FieldInfo ),
587- TotalObjects : 0 ,
588- }
589- }
590-
591- info := catalogSchema .Schemas [meta .Schema ]
592- info .TotalObjects ++
593-
594- var obj map [string ]interface {}
595- if err := json .Unmarshal (meta .Blob , & obj ); err != nil {
596- klog .V (4 ).InfoS ("skipping malformed meta blob during schema discovery" ,
597- "schema" , meta .Schema , "name" , meta .Name , "error" , err )
598- continue
599- }
600-
601- if info .SampleObject == nil {
602- info .SampleObject = obj
603- }
604-
605- analyzeJSONObject (obj , info )
528+ processMetaIntoSchema (catalogSchema , meta )
606529 }
607-
608530 return catalogSchema , nil
609531}
610532
0 commit comments