From 14800153bffc7b141aa28d2075489f07a4b3f896 Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Mon, 20 Jan 2025 12:47:47 +0100 Subject: [PATCH] wip --- src/schema-convertors/index.ts | 12 +- .../internalToMongodb.test.ts | 501 ++++++++++++------ src/schema-convertors/internalToMongodb.ts | 33 +- 3 files changed, 355 insertions(+), 191 deletions(-) diff --git a/src/schema-convertors/index.ts b/src/schema-convertors/index.ts index d385596..2b203b5 100644 --- a/src/schema-convertors/index.ts +++ b/src/schema-convertors/index.ts @@ -1,14 +1,14 @@ -import internalSchemaToStandard from '../internalToStandard'; +import internalSchemaToMongoDB from './internalToMongodb'; import { Schema as InternalSchema } from '../schema-analyzer'; -import { ExtendedJSONSchema, MongoDBJSONSchema } from '../types'; +import { ExtendedJSONSchema, StandardJSONSchema } from '../types'; -function internalSchemaToMongoDB( +function internalSchemaToStandard( internalSchema: InternalSchema, options: { signal?: AbortSignal -}): MongoDBJSONSchema { - // TODO: COMPASS-8701 - return {} as MongoDBJSONSchema; +}): StandardJSONSchema { + // TODO: COMPASS-8700 + return {} as StandardJSONSchema; } function internalSchemaToExtended( diff --git a/src/schema-convertors/internalToMongodb.test.ts b/src/schema-convertors/internalToMongodb.test.ts index dbd9731..be07930 100644 --- a/src/schema-convertors/internalToMongodb.test.ts +++ b/src/schema-convertors/internalToMongodb.test.ts @@ -2,108 +2,108 @@ 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: { + describe('Converts: ', function() { + it('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, { bsonType: 'object', required: ['author'], properties: { @@ -120,87 +120,248 @@ describe.only('internalSchemaToStandard', function() { } } } - } + }); }); - }); - 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 + it('array - single type', 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, { + bsonType: 'object', + required: [], + properties: { + genres: { + bsonType: 'array', + items: { + bsonType: 'string' } - ] + } } - ] - }; - const standard = internalSchemaToStandard(internal); - assert.deepStrictEqual(standard, { - $jsonSchema: { + }); + }); + + it('array - complex mixed type', 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: 0.6666666666666666, + unique: 2, + hasDuplicates: false, + values: [ + 'crimi', + 'comedy' + ], + bsonType: 'String' + }, + { + name: 'Document', + path: [ + 'genres' + ], + count: 1, + probability: 0.3333333333333333, + bsonType: 'Document', + fields: [ + { + name: 'long', + path: [ + 'genres', + 'long' + ], + count: 1, + type: 'String', + probability: 1, + hasDuplicates: false, + types: [ + { + name: 'String', + path: [ + 'genres', + 'long' + ], + count: 1, + probability: 1, + unique: 1, + hasDuplicates: false, + values: [ + 'science fiction' + ], + bsonType: 'String' + } + ] + }, + { + name: 'short', + path: [ + 'genres', + 'short' + ], + count: 1, + type: 'String', + probability: 1, + hasDuplicates: false, + types: [ + { + name: 'String', + path: [ + 'genres', + 'short' + ], + count: 1, + probability: 1, + unique: 1, + hasDuplicates: false, + values: [ + 'scifi' + ], + bsonType: 'String' + } + ] + } + ] + } + ], + totalCount: 3, + lengths: [ + 3 + ], + averageLength: 3 + }, + { + name: 'Undefined', + bsonType: 'Undefined', + unique: 1, + hasDuplicates: false, + path: [ + 'genres' + ], + count: 1, + probability: 0.5 + } + ] + } + ] + }; + const standard = internalSchemaToStandard(internal); + assert.deepStrictEqual(standard, { bsonType: 'object', required: [], properties: { genres: { bsonType: 'array', items: { - bsonType: 'string' + anyOf: [ + { + bsonType: 'string' + }, + { + bsonType: 'object', + required: ['long', 'short'], + properties: { + long: { + bsonType: 'string' + }, + short: { + bsonType: 'string' + } + } + } + ] } } } - } + }); }); + + // TODO: array - simple mixed type }); }); diff --git a/src/schema-convertors/internalToMongodb.ts b/src/schema-convertors/internalToMongodb.ts index fae2cb4..38220e3 100644 --- a/src/schema-convertors/internalToMongodb.ts +++ b/src/schema-convertors/internalToMongodb.ts @@ -1,11 +1,11 @@ import { ArraySchemaType, DocumentSchemaType, Schema as InternalSchema, SchemaType } from '../schema-analyzer'; -import { StandardJSONSchema } from '../types'; +import { MongodbJSONSchema } from '../types'; const internalTypeToBsonType = (type: string) => type === 'Document' ? 'object' : type.toLowerCase(); -function parseType(type: SchemaType, signal?: AbortSignal): StandardJSONSchema { +function parseType(type: SchemaType, signal?: AbortSignal): MongodbJSONSchema { if (signal?.aborted) throw new Error('Operation aborted'); - const schema: StandardJSONSchema = { + const schema: MongodbJSONSchema = { bsonType: internalTypeToBsonType(type.bsonType) }; switch (type.bsonType) { @@ -22,25 +22,30 @@ function parseType(type: SchemaType, signal?: AbortSignal): StandardJSONSchema { return schema; } -function parseTypes(types: SchemaType[], signal?: AbortSignal): StandardJSONSchema { +function parseTypes(types: SchemaType[], signal?: AbortSignal): MongodbJSONSchema { 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 + const parsedTypes = definedTypes.map(type => parseType(type, signal)); + if (definedTypes.some(type => ['Document', 'Array'].includes(type.bsonType))) { + return { + anyOf: parsedTypes + }; + } return { - anyOf: definedTypes.map(type => parseType(type, signal)) + bsonType: definedTypes.map((type) => type.bsonType) }; } function parseFields(fields: DocumentSchemaType['fields'], signal?: AbortSignal): { - required: StandardJSONSchema['required'], - properties: StandardJSONSchema['properties'], + required: MongodbJSONSchema['required'], + properties: MongodbJSONSchema['properties'], } { const required = []; - const properties: StandardJSONSchema['properties'] = {}; + const properties: MongodbJSONSchema['properties'] = {}; for (const field of fields) { if (signal?.aborted) throw new Error('Operation aborted'); if (field.probability === 1) required.push(field.name); @@ -54,12 +59,10 @@ export default function internalSchemaToMongodb( internalSchema: InternalSchema, options: { signal?: AbortSignal -} = {}): StandardJSONSchema { - const schema: StandardJSONSchema = { - $jsonSchema: { - bsonType: 'object', - ...parseFields(internalSchema.fields, options.signal) - } +} = {}): MongodbJSONSchema { + const schema: MongodbJSONSchema = { + bsonType: 'object', + ...parseFields(internalSchema.fields, options.signal) }; return schema; }