mongodb-schema
Version:
Infer the probabilistic schema for a MongoDB collection.
318 lines • 9.43 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertInternalToStandard = exports.RELAXED_EJSON_DEFINITIONS = exports.InternalTypeToStandardTypeMap = void 0;
const util_1 = require("../util");
exports.InternalTypeToStandardTypeMap = {
Double: { $ref: '#/$defs/Double' },
Number: { $ref: '#/$defs/Double' },
String: { type: 'string' },
Document: { type: 'object' },
Array: { type: 'array' },
Binary: { $ref: '#/$defs/Binary' },
Undefined: { $ref: '#/$defs/Undefined' },
ObjectId: { $ref: '#/$defs/ObjectId' },
Boolean: { type: 'boolean' },
Date: { $ref: '#/$defs/Date' },
Null: { type: 'null' },
RegExp: { $ref: '#/$defs/RegExp' },
BSONRegExp: { $ref: '#/$defs/RegExp' },
DBRef: { $ref: '#/$defs/DBRef' },
DBPointer: { $ref: '#/$defs/DBPointer' },
BSONSymbol: { $ref: '#/$defs/BSONSymbol' },
Code: { $ref: '#/$defs/Code' },
CodeWScope: { $ref: '#/$defs/CodeWScope' },
Int32: { type: 'integer' },
Timestamp: { $ref: '#/$defs/Timestamp' },
Long: { type: 'integer' },
Decimal128: { $ref: '#/$defs/Decimal128' },
MinKey: { $ref: '#/$defs/MinKey' },
MaxKey: { $ref: '#/$defs/MaxKey' }
};
exports.RELAXED_EJSON_DEFINITIONS = {
ObjectId: {
type: 'object',
properties: {
$oid: {
type: 'string',
pattern: '^[0-9a-fA-F]{24}$'
}
},
required: ['$oid'],
additionalProperties: false
},
BSONSymbol: {
type: 'object',
properties: {
$symbol: {
type: 'string'
}
},
required: ['$symbol'],
additionalProperties: false
},
Double: {
oneOf: [
{ type: 'number' },
{
type: 'object',
properties: {
$numberDouble: {
enum: ['Infinity', '-Infinity', 'NaN']
}
}
}
]
},
Decimal128: {
type: 'object',
properties: {
$numberDecimal: {
type: 'string'
}
},
required: ['$numberDecimal'],
additionalProperties: false
},
Binary: {
type: 'object',
properties: {
$binary: {
type: 'object',
properties: {
base64: {
type: 'string'
},
subType: {
type: 'string',
pattern: '^[0-9a-fA-F]{1,2}$'
}
},
required: ['base64', 'subType'],
additionalProperties: false
}
},
required: ['$binary'],
additionalProperties: false
},
Code: {
type: 'object',
properties: {
$code: {
type: 'string'
}
},
required: ['$code'],
additionalProperties: false
},
CodeWScope: {
type: 'object',
properties: {
$code: {
type: 'string'
},
$scope: {
type: 'object'
}
},
required: ['$code', '$scope'],
additionalProperties: false
},
Timestamp: {
type: 'object',
properties: {
$timestamp: {
type: 'object',
properties: {
t: {
type: 'integer',
minimum: 0
},
i: {
type: 'integer',
minimum: 0
}
},
required: ['t', 'i'],
additionalProperties: false
}
},
required: ['$timestamp'],
additionalProperties: false
},
RegExp: {
type: 'object',
properties: {
$regularExpression: {
type: 'object',
properties: {
pattern: {
type: 'string'
},
options: {
type: 'string',
pattern: '^[gimuy]*$'
}
},
required: ['pattern'],
additionalProperties: false
}
},
required: ['$regularExpression'],
additionalProperties: false
},
DBPointer: {
type: 'object',
properties: {
$dbPointer: {
type: 'object',
properties: {
$ref: {
type: 'string'
},
$id: {
$ref: '#/$defs/ObjectId'
}
},
required: ['$ref', '$id'],
additionalProperties: false
}
},
required: ['$dbPointer'],
additionalProperties: false
},
Date: {
type: 'object',
properties: {
$date: {
type: 'string',
format: 'date-time'
}
},
required: ['$date'],
additionalProperties: false
},
DBRef: {
type: 'object',
properties: {
$ref: {
type: 'string'
},
$id: {},
$db: {
type: 'string'
}
},
required: ['$ref', '$id'],
additionalProperties: true
},
MinKey: {
type: 'object',
properties: {
$minKey: {
type: 'integer',
const: 1
}
},
required: ['$minKey'],
additionalProperties: false
},
MaxKey: {
type: 'object',
properties: {
$maxKey: {
type: 'integer',
const: 1
}
},
required: ['$maxKey'],
additionalProperties: false
},
Undefined: {
type: 'object',
properties: {
$undefined: {
type: 'boolean',
const: true
}
},
required: ['$undefined'],
additionalProperties: false
}
};
const createConvertInternalToStandard = function () {
const usedDefinitions = new Set();
function getUsedDefinitions() {
const filteredDefinitions = Object.fromEntries(Object.entries(exports.RELAXED_EJSON_DEFINITIONS).filter(([key]) => usedDefinitions.has(key)));
return Object.freeze(filteredDefinitions);
}
function markUsedDefinition(ref) {
usedDefinitions.add(ref.split('/')[2]);
}
function convertInternalType(internalType) {
const type = exports.InternalTypeToStandardTypeMap[internalType];
if (!type)
throw new Error(`Encountered unknown type: ${internalType}`);
return { ...type };
}
async function parseType(type, signal) {
await (0, util_1.allowAbort)(signal);
const schema = convertInternalType(type.bsonType);
if (schema.$ref)
markUsedDefinition(schema.$ref);
switch (type.bsonType) {
case 'Array':
schema.items = await parseTypes(type.types, signal);
break;
case 'Document':
Object.assign(schema, await parseFields(type.fields, signal));
break;
}
return schema;
}
function isPlainTypesOnly(types) {
return types.every(definition => !!definition.type && Object.keys(definition).length === 1);
}
async function parseTypes(types, signal) {
await (0, util_1.allowAbort)(signal);
const definedTypes = types.filter(type => type.bsonType.toLowerCase() !== 'undefined');
const isSingleType = definedTypes.length === 1;
if (isSingleType) {
return parseType(definedTypes[0], signal);
}
const parsedTypes = await Promise.all(definedTypes.map(type => parseType(type, signal)));
if (isPlainTypesOnly(parsedTypes)) {
return {
type: parsedTypes.map(({ type }) => type)
};
}
return {
anyOf: parsedTypes
};
}
async function parseFields(fields, signal) {
const required = [];
const properties = {};
for (const field of fields) {
if (field.probability === 1)
required.push(field.name);
properties[field.name] = await parseTypes(field.types, signal);
}
return { required, properties };
}
return async function convert(internalSchema, options = {}) {
const { required, properties } = await parseFields(internalSchema.fields, options.signal);
const schema = {
$schema: 'https://json-schema.org/draft/2020-12/schema',
type: 'object',
required,
properties,
$defs: getUsedDefinitions()
};
return schema;
};
};
function convertInternalToStandard(internalSchema, options = {}) {
return createConvertInternalToStandard()(internalSchema, options);
}
exports.convertInternalToStandard = convertInternalToStandard;
//# sourceMappingURL=internalToStandard.js.map