@@ -66,33 +66,22 @@ function extractFieldsFromGraphConfig(document: string, graphConfig: GraphConfig
6666 if ( field . type === 'lookup' || field . type === 'inheritLookup' ) {
6767 // For lookups, we only need _id - we'll populate the rest later
6868 fields . add ( `${ baseFieldName } ._id` ) ;
69- } else if ( field . type === 'address' ) {
70- const addressFields = [ 'city' , 'state' , 'country' , 'district' , 'place' , 'number' , 'postalCode' , 'complement' , 'placeType' ] ;
71- addressFields . forEach ( af => {
72- fields . add ( `${ fieldPath } .${ af } ` ) ;
73- } ) ;
74- } else if ( field . type === 'money' ) {
75- fields . add ( `${ fieldPath } .value` ) ;
76- fields . add ( `${ fieldPath } .currency` ) ;
77- } else if ( field . type === 'personName' ) {
78- fields . add ( `${ fieldPath } .full` ) ;
79- fields . add ( `${ fieldPath } .first` ) ;
80- fields . add ( `${ fieldPath } .last` ) ;
81- } else if ( field . type === 'phone' ) {
82- fields . add ( `${ fieldPath } .phoneNumber` ) ;
83- fields . add ( `${ fieldPath } .countryCode` ) ;
84- } else if ( field . type === 'email' ) {
85- fields . add ( `${ fieldPath } .address` ) ;
69+ } else if ( [ 'address' , 'money' , 'personName' , 'phone' , 'email' ] . includes ( field . type ) ) {
70+ // Use the parent field name to avoid clearProjectionPathCollision stripping sibling subfields
71+ fields . add ( baseFieldName ) ;
8672 } else {
8773 fields . add ( fieldPath ) ;
8874 }
8975 } ;
9076
9177 // Extract fields from xAxis, yAxis, categoryField, and series
78+ const hasSeries = Array . isArray ( graphConfig . series ) && graphConfig . series . length > 0 ;
79+
9280 if ( graphConfig . xAxis ?. field ) {
9381 expandField ( graphConfig . xAxis . field ) ;
9482 }
95- if ( graphConfig . yAxis ?. field ) {
83+ // yAxis is legacy — skip when series are present (series take precedence)
84+ if ( graphConfig . yAxis ?. field && ! hasSeries ) {
9685 expandField ( graphConfig . yAxis . field ) ;
9786 }
9887 if ( graphConfig . categoryField ) {
@@ -101,9 +90,8 @@ function extractFieldsFromGraphConfig(document: string, graphConfig: GraphConfig
10190 // System fields starting with _ are already handled by expandField, but ensure it's explicitly added
10291 fields . add ( graphConfig . categoryField ) ;
10392 }
104- // Extract fields from series
105- if ( graphConfig . series ) {
106- graphConfig . series . forEach ( serie => {
93+ if ( hasSeries ) {
94+ graphConfig . series ! . forEach ( serie => {
10795 if ( serie . field ) {
10896 expandField ( serie . field ) ;
10997 }
@@ -114,7 +102,8 @@ function extractFieldsFromGraphConfig(document: string, graphConfig: GraphConfig
114102 // This ensures _createdAt, _updatedAt, etc. are available even if projection filters them
115103 // NOTE: System fields that are lookups are handled by expandField above (they get _id suffix)
116104 // Here we only add non-lookup system fields (like _createdAt, _updatedAt, _id)
117- const systemFieldsToCheck = [ graphConfig . categoryField , graphConfig . xAxis ?. field , graphConfig . yAxis ?. field ] ;
105+ const systemFieldsToCheck : ( string | undefined ) [ ] = [ graphConfig . categoryField , graphConfig . xAxis ?. field ] ;
106+ if ( ! hasSeries && graphConfig . yAxis ?. field ) systemFieldsToCheck . push ( graphConfig . yAxis . field ) ;
118107 if ( graphConfig . series ) {
119108 graphConfig . series . forEach ( serie => {
120109 if ( serie . field ) systemFieldsToCheck . push ( serie . field ) ;
@@ -139,6 +128,38 @@ function extractFieldsFromGraphConfig(document: string, graphConfig: GraphConfig
139128 return Array . from ( fields ) ;
140129}
141130
131+ /**
132+ * Validate that every field path used in the graph config exists on the document meta.
133+ * Prevents RPC "Field not found" from Python when the document has no such field.
134+ */
135+ function validateGraphFieldsExist (
136+ document : string ,
137+ graphConfig : GraphConfig ,
138+ ) : { valid : true } | { valid : false ; field : string } {
139+ const meta = MetaObject . Meta [ document ] ;
140+ if ( meta == null ) return { valid : true } ;
141+
142+ const fieldPaths : string [ ] = [ ] ;
143+ const hasSeries = Array . isArray ( graphConfig . series ) && graphConfig . series . length > 0 ;
144+ if ( graphConfig . xAxis ?. field ) fieldPaths . push ( graphConfig . xAxis . field ) ;
145+ // yAxis is legacy — skip validation when series are present (series take precedence)
146+ if ( graphConfig . yAxis ?. field && ! hasSeries ) fieldPaths . push ( graphConfig . yAxis . field ) ;
147+ if ( graphConfig . categoryField ) fieldPaths . push ( graphConfig . categoryField ) ;
148+ if ( hasSeries ) {
149+ graphConfig . series ! . forEach ( serie => {
150+ if ( serie . field ) fieldPaths . push ( serie . field ) ;
151+ } ) ;
152+ }
153+
154+ const invalidField = fieldPaths . find ( path => {
155+ if ( ! path ) return false ;
156+ const baseFieldName = path . split ( '.' ) [ 0 ] ;
157+ if ( baseFieldName . startsWith ( '_' ) ) return false ;
158+ return meta . fields [ baseFieldName ] == null ;
159+ } ) ;
160+ return invalidField != null ? { valid : false , field : invalidField } : { valid : true } ;
161+ }
162+
142163/**
143164 * Identify lookup fields in the graph config
144165 */
@@ -177,19 +198,19 @@ function getLookupFieldsInfo(
177198 }
178199 } ;
179200
180- // Process xAxis, yAxis, categoryField, and series
201+ const hasSeries = Array . isArray ( graphConfig . series ) && graphConfig . series . length > 0 ;
202+
181203 if ( graphConfig . xAxis ?. field ) {
182204 processField ( graphConfig . xAxis . field ) ;
183205 }
184- if ( graphConfig . yAxis ?. field ) {
206+ if ( graphConfig . yAxis ?. field && ! hasSeries ) {
185207 processField ( graphConfig . yAxis . field ) ;
186208 }
187209 if ( graphConfig . categoryField ) {
188210 processField ( graphConfig . categoryField ) ;
189211 }
190- // Process series fields
191- if ( graphConfig . series ) {
192- graphConfig . series . forEach ( serie => {
212+ if ( hasSeries ) {
213+ graphConfig . series ! . forEach ( serie => {
193214 if ( serie . field ) {
194215 processField ( serie . field ) ;
195216 }
@@ -377,6 +398,16 @@ export default async function graphStream({
377398 const enrichedConfig = enrichGraphConfig ( findParams . document , graphConfig , findParams . lang || 'pt_BR' ) ;
378399 logger . debug ( { lang : findParams . lang || 'pt_BR' } , 'Enriched graph config' ) ;
379400
401+ const fieldValidation = validateGraphFieldsExist ( findParams . document , enrichedConfig ) ;
402+ if ( ! fieldValidation . valid ) {
403+ tracingSpan ?. end ( ) ;
404+ const errorMsg = getGraphErrorMessage ( 'GRAPH_FIELD_NOT_FOUND' , {
405+ field : fieldValidation . field ,
406+ document : findParams . document ,
407+ } ) ;
408+ return errorReturn ( [ { message : errorMsg . message , code : errorMsg . code , details : errorMsg . details } as KonectyError ] ) ;
409+ }
410+
380411 // 1.1 Extract fields from graph config for proper projection
381412 const graphFields = extractFieldsFromGraphConfig ( findParams . document , enrichedConfig ) ;
382413 tracingSpan ?. addEvent ( 'Extracted graph fields' , { fields : graphFields . join ( ',' ) } ) ;
0 commit comments