Skip to content

Commit

Permalink
feat: internal to mongodb conversion COMPASS-8701
Browse files Browse the repository at this point in the history
  • Loading branch information
paula-stacho committed Jan 20, 2025
1 parent 6c7d147 commit 2fbcd8b
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 12 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
".esm-wrapper.mjs"
],
"scripts": {
"test": "nyc mocha --timeout 5000 --colors -r ts-node/register test/*.ts",
"test": "nyc mocha --timeout 5000 --colors -r ts-node/register src/**/*.test.ts",
"test-example-parse-from-file": "ts-node examples/parse-from-file.ts",
"test-example-parse-schema": "ts-node examples/parse-schema.ts",
"test-time": "ts-node ./test/time-testing.ts",
Expand Down
37 changes: 37 additions & 0 deletions src/schema-convertors/bsontypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// function parseType(type: SchemaType, signal?: AbortSignal): StandardJSONSchema {
// switch (type.bsonType) {
// case 'Array': return {
// type: 'array',
// items: parseTypes((type as ArraySchemaType).types)
// };
// case 'Binary': return {
// type: 'string'
// // contentEncoding: // TODO: can we get this?
// };
// case 'Boolean': return {
// type: 'boolean'
// };
// case 'Document': return {
// type: 'object',
// ...parseFields((type as DocumentSchemaType).fields, signal)
// };
// case 'Double': return {
// type: 'number'
// };
// case 'Null': return {
// type: 'null'
// };
// case 'ObjectId': return {
// type: 'string',
// contentEncoding: 'base64' // TODO: confirm
// };
// case 'String': return {
// type: 'string'
// };
// case 'Timestamp': return {
// type: 'string'
// // TODO
// };
// default: throw new Error('Type unknown ' + type.bsonType); // TODO: unknown + telemetry?
// }
// }
14 changes: 3 additions & 11 deletions src/schema-convertors.ts → src/schema-convertors/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import { Schema as InternalSchema } from './schema-analyzer';
import { ExtendedJSONSchema, MongoDBJSONSchema, StandardJSONSchema } from './types';

function internalSchemaToStandard(
internalSchema: InternalSchema,
options: {
signal?: AbortSignal
}): StandardJSONSchema {
// TODO: COMPASS-8700
return {};
}
import internalSchemaToStandard from '../internalToStandard';
import { Schema as InternalSchema } from '../schema-analyzer';
import { ExtendedJSONSchema, MongoDBJSONSchema } from '../types';

function internalSchemaToMongoDB(
internalSchema: InternalSchema,
Expand Down
206 changes: 206 additions & 0 deletions src/schema-convertors/internalToMongodb.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import assert from 'assert';
import internalSchemaToStandard from './internalToMongodb';

describe.only('internalSchemaToStandard', function() {
it('converts a document/object', function() {
const internal = {
count: 2,
fields: [
{
name: 'author',
path: [
'author'
],
count: 1,
type: [
'Document',
'Undefined'
],
probability: 1,
hasDuplicates: false,
types: [
{
name: 'Document',
path: [
'author'
],
count: 1,
probability: 0.5,
bsonType: 'Document',
fields: [
{
name: 'name',
path: [
'author',
'name'
],
count: 1,
type: 'String',
probability: 1,
hasDuplicates: false,
types: [
{
name: 'String',
path: [
'author',
'name'
],
count: 1,
probability: 1,
unique: 1,
hasDuplicates: false,
values: [
'Peter Sonder'
],
bsonType: 'String'
}
]
},
{
name: 'rating',
path: [
'author',
'rating'
],
count: 1,
type: 'Double',
probability: 1,
hasDuplicates: false,
types: [
{
name: 'Double',
path: [
'author',
'rating'
],
count: 1,
probability: 1,
unique: 1,
hasDuplicates: false,
values: [
1.3
],
bsonType: 'Double'
}
]
}
]
},
{
name: 'Undefined',
bsonType: 'Undefined',
unique: 1,
hasDuplicates: false,
path: [
'author'
],
count: 1,
probability: 0.5
}
]
}
]
};
const standard = internalSchemaToStandard(internal);
assert.deepStrictEqual(standard, {
$jsonSchema: {
bsonType: 'object',
required: ['author'],
properties: {
author: {
bsonType: 'object',
required: ['name', 'rating'],
properties: {
name: {
bsonType: 'string'
},
rating: {
bsonType: 'double'
}
}
}
}
}
});
});

it('converts an array', function() {
const internal = {
count: 2,
fields: [
{
name: 'genres',
path: [
'genres'
],
count: 1,
type: [
'array',
'Undefined'
],
probability: 0.5,
hasDuplicates: false,
types: [
{
name: 'array',
path: [
'genres'
],
count: 1,
probability: 0.5,
bsonType: 'Array',
types: [
{
name: 'String',
path: [
'genres'
],
count: 2,
probability: 1,
unique: 2,
hasDuplicates: false,
values: [
'crimi',
'comedy'
],
bsonType: 'String'
}
],
totalCount: 2,
lengths: [
2
],
averageLength: 2
},
{
name: 'Undefined',
bsonType: 'Undefined',
unique: 1,
hasDuplicates: false,
path: [
'genres'
],
count: 1,
probability: 0.5
}
]
}
]
};
const standard = internalSchemaToStandard(internal);
assert.deepStrictEqual(standard, {
$jsonSchema: {
bsonType: 'object',
required: [],
properties: {
genres: {
bsonType: 'array',
items: {
bsonType: 'string'
}
}
}
}
});
});
});
65 changes: 65 additions & 0 deletions src/schema-convertors/internalToMongodb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { ArraySchemaType, DocumentSchemaType, Schema as InternalSchema, SchemaType } from '../schema-analyzer';
import { StandardJSONSchema } from '../types';

const internalTypeToBsonType = (type: string) => type === 'Document' ? 'object' : type.toLowerCase();

function parseType(type: SchemaType, signal?: AbortSignal): StandardJSONSchema {
if (signal?.aborted) throw new Error('Operation aborted');
const schema: StandardJSONSchema = {
bsonType: internalTypeToBsonType(type.bsonType)
};
switch (type.bsonType) {
case 'Array':
schema.items = parseTypes((type as ArraySchemaType).types);
break;
case 'Document':
Object.assign(schema,
parseFields((type as DocumentSchemaType).fields, signal)
);
break;
}

return schema;
}

function parseTypes(types: SchemaType[], signal?: AbortSignal): StandardJSONSchema {
if (signal?.aborted) throw new Error('Operation aborted');
const definedTypes = types.filter(type => type.bsonType.toLowerCase() !== 'undefined');
const isSingleType = definedTypes.length === 1;
if (isSingleType) {
return parseType(definedTypes[0], signal);
}
// TODO: array of types for simple types
return {
anyOf: definedTypes.map(type => parseType(type, signal))
};
}

function parseFields(fields: DocumentSchemaType['fields'], signal?: AbortSignal): {
required: StandardJSONSchema['required'],
properties: StandardJSONSchema['properties'],
} {
const required = [];
const properties: StandardJSONSchema['properties'] = {};
for (const field of fields) {
if (signal?.aborted) throw new Error('Operation aborted');
if (field.probability === 1) required.push(field.name);
properties[field.name] = parseTypes(field.types, signal);
}

return { required, properties };
}

export default function internalSchemaToMongodb(
internalSchema: InternalSchema,
options: {
signal?: AbortSignal
} = {}): StandardJSONSchema {
const schema: StandardJSONSchema = {
$jsonSchema: {
bsonType: 'object',
...parseFields(internalSchema.fields, options.signal)
}
};
return schema;
}

0 comments on commit 2fbcd8b

Please sign in to comment.