@datastax/astra-mongoose
Version:
Astra's NodeJS Mongoose compatibility client
263 lines • 11.5 kB
JavaScript
;
// Copyright DataStax, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = convertSchemaToColumns;
const astraMongooseError_1 = require("./astraMongooseError");
const getUDTNameFromSchemaType_1 = __importDefault(require("./udt/getUDTNameFromSchemaType"));
/**
* Given a Mongoose schema, create an equivalent Data API table definition for use with `createTable()`
*/
function convertSchemaToColumns(schema, udtName) {
const versionKey = schema.options.versionKey;
const columns = {};
if (schema.options._id !== false) {
columns._id = { type: 'text' };
}
if (typeof versionKey === 'string') {
columns[versionKey] = { type: 'int' };
}
const schemaTypesForNestedPath = {};
udtName = udtName ?? schema.options?.udtName;
for (const path of Object.keys(schema.paths)) {
const schemaType = schema.paths[path];
const type = mongooseTypeToDataAPIType(schemaType.instance);
const isNestedOrMap = path.indexOf('.') !== -1;
if (isNestedOrMap) {
const split = schemaType.path.split('.');
if (split.length > 2) {
throw new astraMongooseError_1.AstraMongooseError(`Cannot convert schema to Data API table definition: schemas with paths that are more than 2 levels deep are not supported (found ${path})`, {
path,
type,
schema
});
}
// Nested paths aren't listed in `schema.paths`, so store their subpaths to process them later on a
// per-nested-path basis.
if (schema.pathType(split[0]) === 'nested') {
const nestedPath = split[0];
if (schemaTypesForNestedPath[nestedPath] == null) {
schemaTypesForNestedPath[nestedPath] = [];
}
schemaTypesForNestedPath[nestedPath].push(schemaType);
}
}
else if (type) {
columns[path] = { type };
}
else if (schemaType.instance === 'Array' || schemaType.instance === 'Vectorize') {
if (udtName != null) {
throw new astraMongooseError_1.AstraMongooseError('Cannot convert schema to Data API table definition: cannot store an array in a UDT', { path, type });
}
if (schemaType.schema) {
// If `schema` is set, this is a DocumentArray
const schemaTypeUDTName = (0, getUDTNameFromSchemaType_1.default)(schemaType);
if (schemaTypeUDTName) {
columns[path] = {
type: 'list',
valueType: { type: 'userDefined', udtName: schemaTypeUDTName }
};
}
else {
throw new astraMongooseError_1.AstraMongooseError(`Cannot convert schema to Data API table definition: DocumentArray "${path}" is not supported`, {
path,
type,
schema
});
}
}
else {
// Arrays always have an embedded schema type
const embeddedSchemaType = schemaType.getEmbeddedSchemaType();
if (schemaType.options.dimension != null) {
// If dimension, assume we're creating a vector column
if (embeddedSchemaType.instance !== 'Number') {
throw new astraMongooseError_1.AstraMongooseError(`Cannot convert schema to Data API table definition: vector column at "${path}" must be an array of numbers`, {
path,
type,
schema
});
}
columns[path] = { type: 'vector', dimension: schemaType.options.dimension };
if (schemaType.instance === 'Vectorize' && schemaType.options.service != null) {
columns[path].service = schemaType.options.service;
}
}
else {
const valueType = mongooseTypeToDataAPIType(embeddedSchemaType.instance);
if (valueType == null) {
throw new astraMongooseError_1.AstraMongooseError(`Cannot convert schema to Data API table definition: unsupported array type at path "${path}"`, {
path,
valueType,
type,
schema
});
}
columns[path] = { type: 'list', valueType };
}
}
}
else if (schemaType.instance === 'Embedded') {
if (udtName != null) {
throw new astraMongooseError_1.AstraMongooseError('Cannot convert schema to Data API table definition: cannot store a subdocument in a UDT', {
path,
type
});
}
const schemaTypeUDTName = (0, getUDTNameFromSchemaType_1.default)(schemaType);
if (schemaTypeUDTName) {
columns[path] = { type: 'userDefined', udtName: schemaTypeUDTName };
}
else {
columns[path] = {
type: 'map',
keyType: 'text',
valueType: getValueTypeFromNestedSchemaTypes(path, Object.values(schemaType.schema.paths), true)
};
}
}
else if (schemaType.instance === 'Map') {
if (udtName != null) {
throw new astraMongooseError_1.AstraMongooseError('Cannot convert schema to Data API table definition: cannot store a map in a UDT', {
path,
type
});
}
const embeddedSchemaType = schemaType.getEmbeddedSchemaType();
if (!isSchemaTypeRequired(embeddedSchemaType)) {
throw new astraMongooseError_1.AstraMongooseError(`Cannot convert schema to Data API table definition: values for map path "${path}" must be required`, { path, type });
}
const valueType = mongooseTypeToDataAPIType(embeddedSchemaType.instance);
const schemaTypeUDTName = (0, getUDTNameFromSchemaType_1.default)(schemaType);
if (valueType != null) {
columns[path] = { type: 'map', keyType: 'text', valueType };
}
else if (schemaTypeUDTName) {
// Special handling for maps of UDTs
columns[path] = {
type: 'map',
keyType: 'text',
valueType: {
type: 'userDefined',
udtName: schemaTypeUDTName
}
};
}
else {
throw new astraMongooseError_1.AstraMongooseError(`Cannot convert schema to Data API table definition: unsupported type at path "${schemaType.path}"`, { path, type });
}
}
else {
throw new astraMongooseError_1.AstraMongooseError(`Cannot convert schema to Data API table definition: unsupported type at path "${path}"`, {
path,
schema
});
}
}
for (const nestedPath of Object.keys(schemaTypesForNestedPath)) {
columns[nestedPath] = {
type: 'map',
keyType: 'text',
valueType: getValueTypeFromNestedSchemaTypes(nestedPath, schemaTypesForNestedPath[nestedPath], false)
};
}
return columns;
}
function getValueTypeFromNestedSchemaTypes(nestedPath, schemaTypes, isSubdocument) {
const dataAPITypes = new Set();
for (const schemaType of schemaTypes) {
const hasNested = schemaType.path.indexOf('.') !== -1;
if (hasNested && isSubdocument) {
throw new astraMongooseError_1.AstraMongooseError(`Cannot convert schema to Data API table definition: unsupported nested path underneath subdocument at path "${nestedPath}.${schemaType.path}"`, {
nestedPath
});
}
const type = mongooseTypeToDataAPIType(schemaType.instance);
const fullPath = isSubdocument ? `${nestedPath}.${schemaType.path}` : schemaType.path;
if (type == null) {
throw new astraMongooseError_1.AstraMongooseError(`Cannot convert schema to Data API table definition: unsupported type at path "${fullPath}"`, { nestedPath, type });
}
if (!isSchemaTypeRequired(schemaType)) {
throw new astraMongooseError_1.AstraMongooseError(`Cannot convert schema to Data API table definition: nested path "${fullPath}" must be required`, { nestedPath, type });
}
dataAPITypes.add(type);
}
if (dataAPITypes.has('blob')) {
throw new astraMongooseError_1.AstraMongooseError(`Cannot convert schema to Data API table definition: nested Buffer at "${nestedPath}" is not supported`, {
nestedPath
});
}
// If all keys have same data type, then we can use that data type
if (dataAPITypes.size === 1) {
return Array.from(dataAPITypes)[0];
}
else {
throw new astraMongooseError_1.AstraMongooseError(`Cannot convert schema to Data API table definition: nested paths with different data types "${nestedPath}" are not supported`, {
nestedPath
});
}
}
function mongooseTypeToDataAPIType(type) {
if (type === 'String') {
return 'text';
}
else if (type === 'Number') {
return 'double';
}
else if (type === 'Date') {
return 'timestamp';
}
else if (type === 'Boolean') {
return 'boolean';
}
else if (type === 'Decimal128') {
return 'decimal';
}
else if (type === 'BigInt') {
return 'varint';
}
else if (type === 'Buffer') {
return 'blob';
}
else if (type === 'ObjectId') {
return 'text';
}
else if (type === 'UUID') {
return 'uuid';
}
else if (type === 'Int32') {
return 'int';
}
else if (type === 'Double') {
return 'double';
}
return null;
}
function isSchemaTypeRequired(schemaType) {
const requiredOption = schemaType.options.required;
if (typeof requiredOption === 'boolean') {
return requiredOption;
}
if (typeof requiredOption === 'function') {
return false;
}
if (Array.isArray(requiredOption)) {
return requiredOption[0];
}
return false;
}
//# sourceMappingURL=convertSchemaToColumns.js.map