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