1- import  type  {  MongoDBJSONSchema  }  from  'mongodb-schema' ; 
1+ import  type  {  JSONSchema ,   MongoDBJSONSchema  }  from  'mongodb-schema' ; 
22import  type  {  FieldPath  }  from  '../services/data-model-storage' ; 
33
44/** 
@@ -167,14 +167,16 @@ export const getFieldFromSchema = ({
167167} ; 
168168
169169type  UpdateOperationParameters  =  { 
170-   update : 'removeField'  |  'renameField' ; 
170+   update : 'removeField'  |  'renameField'   |   'changeFieldSchema' ; 
171171  newFieldName ?: string ; 
172+   newFieldSchema ?: MongoDBJSONSchema ; 
172173} ; 
173174
174175const  applySchemaUpdate  =  ( { 
175176  schema, 
176177  fieldName, 
177178  newFieldName, 
179+   newFieldSchema, 
178180  update, 
179181} : { 
180182  schema : MongoDBJSONSchema ; 
@@ -205,6 +207,22 @@ const applySchemaUpdate = ({
205207        ) , 
206208      } ; 
207209    } 
210+     case  'changeFieldSchema' : { 
211+       if  ( ! schema . properties  ||  ! schema . properties [ fieldName ] ) 
212+         throw  new  Error ( 'Field to change type does not exist' ) ; 
213+       if  ( ! newFieldSchema ) 
214+         throw  new  Error ( 
215+           'New field schema is required for the change operation' 
216+         ) ; 
217+       return  { 
218+         ...schema , 
219+         properties : Object . fromEntries ( 
220+           Object . entries ( schema . properties ) . map ( ( [ key ,  value ] )  => 
221+             key  ===  fieldName  ? [ key ,  newFieldSchema ]  : [ key ,  value ] 
222+           ) 
223+         ) , 
224+       } ; 
225+     } 
208226    default :
209227      return  schema ; 
210228  } 
@@ -216,12 +234,12 @@ const applySchemaUpdate = ({
216234export  const  updateSchema  =  ( { 
217235  jsonSchema, 
218236  fieldPath, 
219-   update, 
220-   newFieldName, 
237+   updateParameters, 
221238} : { 
222239  jsonSchema : MongoDBJSONSchema ; 
223240  fieldPath : FieldPath ; 
224- }  &  UpdateOperationParameters ) : MongoDBJSONSchema  =>  { 
241+   updateParameters : UpdateOperationParameters ; 
242+ } ) : MongoDBJSONSchema  =>  { 
225243  const  newSchema  =  { 
226244    ...jsonSchema , 
227245  } ; 
@@ -234,17 +252,15 @@ export const updateSchema = ({
234252      return  applySchemaUpdate ( { 
235253        schema : newSchema , 
236254        fieldName : nextInPath , 
237-         update, 
238-         newFieldName, 
255+         ...updateParameters , 
239256      } ) ; 
240257    } 
241258    newSchema . properties  =  { 
242259      ...newSchema . properties , 
243260      [ nextInPath ] : updateSchema ( { 
244261        jsonSchema : newSchema . properties [ nextInPath ] , 
245262        fieldPath : remainingFieldPath , 
246-         update, 
247-         newFieldName, 
263+         updateParameters, 
248264      } ) , 
249265    } ; 
250266  } 
@@ -253,8 +269,7 @@ export const updateSchema = ({
253269      updateSchema ( { 
254270        jsonSchema : variant , 
255271        fieldPath : fieldPath , 
256-         update, 
257-         newFieldName, 
272+         updateParameters, 
258273      } ) 
259274    ) ; 
260275  } 
@@ -263,20 +278,129 @@ export const updateSchema = ({
263278      newSchema . items  =  updateSchema ( { 
264279        jsonSchema : newSchema . items , 
265280        fieldPath : fieldPath , 
266-         update, 
267-         newFieldName, 
281+         updateParameters, 
268282      } ) ; 
269283    }  else  { 
270284      newSchema . items  =  newSchema . items . map ( ( item )  => 
271285        updateSchema ( { 
272286          jsonSchema : item , 
273287          fieldPath : fieldPath , 
274-           update, 
275-           newFieldName, 
288+           updateParameters, 
276289        } ) 
277290      ) ; 
278291    } 
279292  } 
280293
281294  return  newSchema ; 
282295} ; 
296+ 
297+ const  getMin1ArrayVariants  =  ( oldSchema : JSONSchema )  =>  { 
298+   const  arrayVariants  =  oldSchema . anyOf ?. filter ( 
299+     ( variant )  =>  variant . bsonType  ===  'array' 
300+   ) ; 
301+   if  ( arrayVariants  &&  arrayVariants . length  >  0 )  { 
302+     return  arrayVariants  as  [ MongoDBJSONSchema ,  ...MongoDBJSONSchema [ ] ] ; 
303+   } 
304+   return  [ 
305+     { 
306+       bsonType : 'array' , 
307+       items : oldSchema . items  ||  [ ] , 
308+     } , 
309+   ] ; 
310+ } ; 
311+ 
312+ const  getMin1ObjectVariants  =  ( 
313+   oldSchema : JSONSchema 
314+ ) : [ MongoDBJSONSchema ,  ...MongoDBJSONSchema [ ] ]  =>  { 
315+   const  objectVariants  =  oldSchema . anyOf ?. filter ( 
316+     ( variant )  =>  variant . bsonType  ===  'object' 
317+   ) ; 
318+   if  ( objectVariants  &&  objectVariants . length  >  0 )  { 
319+     return  objectVariants  as  [ MongoDBJSONSchema ,  ...MongoDBJSONSchema [ ] ] ; 
320+   } 
321+   return  [ 
322+     { 
323+       bsonType : 'object' , 
324+       items : oldSchema . properties  ||  { } , 
325+       required : oldSchema . required  ||  [ ] , 
326+     } , 
327+   ] ; 
328+ } ; 
329+ 
330+ const  getOtherVariants  =  ( 
331+   oldSchema : JSONSchema , 
332+   newTypes : string [ ] 
333+ ) : MongoDBJSONSchema [ ]  =>  { 
334+   const  existingAnyOfVariants  = 
335+     oldSchema . anyOf ?. filter ( 
336+       ( variant )  =>  variant . bsonType  !==  'object'  &&  variant . bsonType  !==  'array' 
337+     )  ||  [ ] ; 
338+   const  existingAnyOfTypes  =  existingAnyOfVariants 
339+     . map ( ( v )  =>  v . bsonType ) 
340+     . flat ( ) ; 
341+   const  existingBasicTypes  =  oldSchema . bsonType 
342+     ? [ ] 
343+     : Array . isArray ( oldSchema . bsonType ) 
344+     ? oldSchema . bsonType 
345+     : [ oldSchema . bsonType ] ; 
346+   const  existingBasicVariants  =  existingBasicTypes 
347+     . filter ( 
348+       ( type )  =>  newTypes . includes ( type )  &&  type  !==  'object'  &&  type  !==  'array' 
349+     ) 
350+     . map ( ( type )  =>  ( {  bsonType : type  } ) ) ; 
351+   const  newVariants  =  newTypes 
352+     . filter ( 
353+       ( type )  => 
354+         type  !==  'object'  && 
355+         type  !==  'array'  && 
356+         ! existingAnyOfTypes . includes ( type )  && 
357+         ! existingBasicTypes . includes ( type ) 
358+     ) 
359+     . map ( ( type )  =>  ( {  bsonType : type  } ) ) ; 
360+   return  [ ...existingAnyOfVariants ,  ...existingBasicVariants ,  ...newVariants ] ; 
361+ } ; 
362+ 
363+ export  function  getSchemaForNewTypes ( 
364+   field : { 
365+     fieldTypes : string [ ] ; 
366+     jsonSchema : MongoDBJSONSchema ; 
367+   } , 
368+   newTypes : string [ ] 
369+ ) : MongoDBJSONSchema  { 
370+   const  {  fieldTypes : oldTypes ,  jsonSchema : oldSchema  }  =  field ; 
371+   if  ( oldTypes . join ( ',' )  ===  newTypes . join ( ',' ) )  return  oldSchema ; 
372+   const  newSchema : MongoDBJSONSchema  =  {  ...oldSchema  } ; 
373+ 
374+   // Simple schema - new type does includes neither object nor array 
375+   if  ( ! newTypes . some ( ( t )  =>  t  ===  'object'  ||  t  ===  'array' ) )  { 
376+     newSchema . bsonType  =  newTypes ; 
377+     delete  newSchema . anyOf ; 
378+     delete  newSchema . properties ; 
379+     delete  newSchema . items ; 
380+     delete  newSchema . required ; 
381+     return  newSchema ; 
382+   } 
383+ 
384+   // Complex schema 
385+ 
386+   // We collect array sub-schemas we need to keep 
387+   // Then we add new sub-schemas if needed 
388+   const  arraySchemas : MongoDBJSONSchema [ ]  =  newTypes . includes ( 'array' ) 
389+     ? getMin1ArrayVariants ( oldSchema ) 
390+     : [ ] ; 
391+ 
392+   // We collect object sub-schemas we need to keep 
393+   const  objectSchemas : MongoDBJSONSchema [ ]  =  newTypes . includes ( 'object' ) 
394+     ? getMin1ObjectVariants ( oldSchema ) 
395+     : [ ] ; 
396+ 
397+   const  otherSchemas : MongoDBJSONSchema [ ]  =  getOtherVariants ( 
398+     oldSchema , 
399+     newTypes 
400+   ) ; 
401+ 
402+   // Finally we set the anyOf to the collected sub-schemas 
403+   newSchema . anyOf  =  [ ...arraySchemas ,  ...objectSchemas ,  ...otherSchemas ] ; 
404+ 
405+   return  newSchema ; 
406+ } 
0 commit comments