@@ -2886,6 +2886,92 @@ Schema.prototype._preCompile = function _preCompile() {
28862886 this . plugin ( idGetter , { deduplicate : true } ) ;
28872887} ;
28882888
2889+ /**
2890+ * Returns a JSON schema representation of this Schema.
2891+ *
2892+ * By default, returns normal [JSON schema representation](https://json-schema.org/learn/getting-started-step-by-step), which is not typically what you want to use with
2893+ * [MongoDB's `$jsonSchema` collection option](https://www.mongodb.com/docs/manual/core/schema-validation/specify-json-schema/).
2894+ * Use the `useBsonType: true` option to return MongoDB `$jsonSchema` syntax instead.
2895+ *
2896+ * In addition to types, `jsonSchema()` supports the following Mongoose validators:
2897+ * - `enum` for strings and numbers
2898+ *
2899+ * #### Example:
2900+ * const schema = new Schema({ name: String });
2901+ * // { required: ['_id'], properties: { name: { type: ['string', 'null'] }, _id: { type: 'string' } } }
2902+ * schema.toJSONSchema();
2903+ *
2904+ * // { required: ['_id'], properties: { name: { bsonType: ['string', 'null'] }, _id: { bsonType: 'objectId' } } }
2905+ * schema.toJSONSchema({ useBsonType: true });
2906+ *
2907+ * @param {Object } [options]
2908+ * @param [Boolean] [options.useBsonType=false] if true, specify each path's type using `bsonType` rather than `type` for MongoDB $jsonSchema support
2909+ */
2910+
2911+ Schema . prototype . toJSONSchema = function toJSONSchema ( options ) {
2912+ const useBsonType = options ?. useBsonType ?? false ;
2913+ const result = useBsonType ? { required : [ ] , properties : { } } : { type : 'object' , required : [ ] , properties : { } } ;
2914+ for ( const path of Object . keys ( this . paths ) ) {
2915+ const schemaType = this . paths [ path ] ;
2916+
2917+ // Skip Map embedded paths, maps will be handled seperately.
2918+ if ( schemaType . _presplitPath . indexOf ( '$*' ) !== - 1 ) {
2919+ continue ;
2920+ }
2921+
2922+ // Nested paths are stored as `nested.path` in the schema type, so create nested paths in the json schema
2923+ // when necessary.
2924+ const isNested = schemaType . _presplitPath . length > 1 ;
2925+ let jsonSchemaForPath = result ;
2926+ if ( isNested ) {
2927+ for ( let i = 0 ; i < schemaType . _presplitPath . length - 1 ; ++ i ) {
2928+ const subpath = schemaType . _presplitPath [ i ] ;
2929+ if ( jsonSchemaForPath . properties [ subpath ] == null ) {
2930+ jsonSchemaForPath . properties [ subpath ] = useBsonType
2931+ ? {
2932+ bsonType : [ 'object' , 'null' ] ,
2933+ properties : { }
2934+ }
2935+ : {
2936+ type : [ 'object' , 'null' ] ,
2937+ properties : { }
2938+ } ;
2939+ }
2940+ jsonSchemaForPath = jsonSchemaForPath . properties [ subpath ] ;
2941+ }
2942+ }
2943+
2944+ const lastSubpath = schemaType . _presplitPath [ schemaType . _presplitPath . length - 1 ] ;
2945+ let isRequired = false ;
2946+ if ( path === '_id' ) {
2947+ if ( ! jsonSchemaForPath . required ) {
2948+ jsonSchemaForPath . required = [ ] ;
2949+ }
2950+ jsonSchemaForPath . required . push ( '_id' ) ;
2951+ isRequired = true ;
2952+ } else if ( schemaType . options . required && typeof schemaType . options . required !== 'function' ) {
2953+ if ( ! jsonSchemaForPath . required ) {
2954+ jsonSchemaForPath . required = [ ] ;
2955+ }
2956+ // Only `required: true` paths are required, conditional required is not required
2957+ jsonSchemaForPath . required . push ( lastSubpath ) ;
2958+ isRequired = true ;
2959+ }
2960+ jsonSchemaForPath . properties [ lastSubpath ] = schemaType . toJSONSchema ( options ) ;
2961+ if ( schemaType . options . enum ) {
2962+ jsonSchemaForPath . properties [ lastSubpath ] . enum = isRequired
2963+ ? schemaType . options . enum
2964+ : [ ...schemaType . options . enum , null ] ;
2965+ }
2966+ }
2967+
2968+ // Otherwise MongoDB errors with "$jsonSchema keyword 'required' cannot be an empty array"
2969+ if ( result . required . length === 0 ) {
2970+ delete result . required ;
2971+ }
2972+ return result ;
2973+ } ;
2974+
28892975/*!
28902976 * Module exports.
28912977 */
0 commit comments