-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: internal to mongodb conversion COMPASS-8701
- Loading branch information
1 parent
6c7d147
commit 2fbcd8b
Showing
5 changed files
with
312 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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? | ||
// } | ||
// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |