querymongo 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +61 -0
- package/.vscode/settings.json +3 -0
- package/ARCHITECTURE.md +286 -0
- package/dist/application/MongoConverter.js +20 -0
- package/dist/application/MongoConverter.js.map +1 -0
- package/dist/application/MongoConverter.spec.js +59 -0
- package/dist/application/MongoConverter.spec.js.map +1 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/domain/interfaces/Converter.js +2 -0
- package/dist/domain/interfaces/Converter.js.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/converters/ConverterFactory.js +14 -0
- package/dist/infrastructure/converters/ConverterFactory.js.map +1 -0
- package/dist/infrastructure/converters/CreateConverter.js +27 -0
- package/dist/infrastructure/converters/CreateConverter.js.map +1 -0
- package/dist/infrastructure/converters/DeleteConverter.js +21 -0
- package/dist/infrastructure/converters/DeleteConverter.js.map +1 -0
- package/dist/infrastructure/converters/SelectConverter.js +24 -0
- package/dist/infrastructure/converters/SelectConverter.js.map +1 -0
- package/dist/infrastructure/converters/UpdateConverter.js +23 -0
- package/dist/infrastructure/converters/UpdateConverter.js.map +1 -0
- package/dist/infrastructure/parser/sqlParser.js +13 -0
- package/dist/infrastructure/parser/sqlParser.js.map +1 -0
- package/dist/infrastructure/utils/queryUtils.js +71 -0
- package/dist/infrastructure/utils/queryUtils.js.map +1 -0
- package/dist/types/application/MongoConverter.d.ts +5 -0
- package/dist/types/application/MongoConverter.d.ts.map +1 -0
- package/dist/types/application/MongoConverter.spec.d.ts +2 -0
- package/dist/types/application/MongoConverter.spec.d.ts.map +1 -0
- package/dist/types/cli/main.d.ts +3 -0
- package/dist/types/cli/main.d.ts.map +1 -0
- package/{src/domain/interfaces/Converter.ts → dist/types/domain/interfaces/Converter.d.ts} +3 -2
- package/dist/types/domain/interfaces/Converter.d.ts.map +1 -0
- package/{src/index.ts → dist/types/index.d.ts} +2 -4
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/infrastructure/converters/ConverterFactory.d.ts +8 -0
- package/dist/types/infrastructure/converters/ConverterFactory.d.ts.map +1 -0
- package/dist/types/infrastructure/converters/CreateConverter.d.ts +7 -0
- package/dist/types/infrastructure/converters/CreateConverter.d.ts.map +1 -0
- package/dist/types/infrastructure/converters/DeleteConverter.d.ts +7 -0
- package/dist/types/infrastructure/converters/DeleteConverter.d.ts.map +1 -0
- package/dist/types/infrastructure/converters/SelectConverter.d.ts +7 -0
- package/dist/types/infrastructure/converters/SelectConverter.d.ts.map +1 -0
- package/dist/types/infrastructure/converters/UpdateConverter.d.ts +7 -0
- package/dist/types/infrastructure/converters/UpdateConverter.d.ts.map +1 -0
- package/dist/types/infrastructure/parser/sqlParser.d.ts +11 -0
- package/dist/types/infrastructure/parser/sqlParser.d.ts.map +1 -0
- package/dist/types/infrastructure/utils/queryUtils.d.ts +9 -0
- package/dist/types/infrastructure/utils/queryUtils.d.ts.map +1 -0
- package/examples.ts +79 -0
- package/package.json +4 -9
- package/src/application/MongoConverter.spec.ts +0 -73
- package/src/application/MongoConverter.ts +0 -29
- package/src/cli/main.ts +0 -64
- package/src/infrastructure/converters/ConverterFactory.ts +0 -21
- package/src/infrastructure/converters/CreateConverter.ts +0 -37
- package/src/infrastructure/converters/DeleteConverter.ts +0 -32
- package/src/infrastructure/converters/SelectConverter.ts +0 -37
- package/src/infrastructure/converters/UpdateConverter.ts +0 -36
- package/src/infrastructure/parser/sqlParser.ts +0 -17
- package/src/infrastructure/utils/queryUtils.ts +0 -93
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilidades para conversión de queries SQL a MongoDB
|
|
3
|
+
* Centraliza lógica reutilizable siguiendo principio DRY
|
|
4
|
+
*/
|
|
5
|
+
import _ from "lodash";
|
|
6
|
+
export const removeUndefinedFields = (obj) => {
|
|
7
|
+
return _.omitBy(obj, _.isUndefined);
|
|
8
|
+
};
|
|
9
|
+
export const normalizeFieldName = (field) => {
|
|
10
|
+
return field.trim().replace(/`|"/g, "");
|
|
11
|
+
};
|
|
12
|
+
const normalizeFieldNames = (fields) => {
|
|
13
|
+
return fields.map(normalizeFieldName);
|
|
14
|
+
};
|
|
15
|
+
export const buildProjection = (fields) => {
|
|
16
|
+
if (fields === "*")
|
|
17
|
+
return {};
|
|
18
|
+
return Object.fromEntries(normalizeFieldNames(fields).map((f) => [f, 1]));
|
|
19
|
+
};
|
|
20
|
+
export const buildFilter = (condition) => {
|
|
21
|
+
if (!condition)
|
|
22
|
+
return {};
|
|
23
|
+
const { operator, left, right } = condition;
|
|
24
|
+
if (!operator)
|
|
25
|
+
return {};
|
|
26
|
+
if (operator.toUpperCase() === "AND") {
|
|
27
|
+
return {
|
|
28
|
+
$and: [buildFilter(left), buildFilter(right)],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (operator.toUpperCase() === "OR") {
|
|
32
|
+
return {
|
|
33
|
+
$or: [buildFilter(left), buildFilter(right)],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const field = normalizeFieldName((left === null || left === void 0 ? void 0 : left.column) || (left === null || left === void 0 ? void 0 : left.value) || left || "");
|
|
37
|
+
const value = (right === null || right === void 0 ? void 0 : right.value) !== undefined ? right.value : right;
|
|
38
|
+
const operatorMap = {
|
|
39
|
+
"=": "$eq",
|
|
40
|
+
"!=": "$ne",
|
|
41
|
+
"<>": "$ne",
|
|
42
|
+
"<": "$lt",
|
|
43
|
+
">": "$gt",
|
|
44
|
+
"<=": "$lte",
|
|
45
|
+
">=": "$gte",
|
|
46
|
+
like: "$regex",
|
|
47
|
+
in: "$in",
|
|
48
|
+
"not in": "$nin",
|
|
49
|
+
};
|
|
50
|
+
const mongoOp = operatorMap[operator.toLowerCase()];
|
|
51
|
+
return mongoOp ? { [field]: { [mongoOp]: value } } : {};
|
|
52
|
+
};
|
|
53
|
+
export function determineOperation(where, operations) {
|
|
54
|
+
var _a;
|
|
55
|
+
const uniqueFields = ["id", "_id", "email", "username"];
|
|
56
|
+
const { operator, left } = where;
|
|
57
|
+
if (!where)
|
|
58
|
+
return operations.many;
|
|
59
|
+
if (["and", "or"].includes(operator === null || operator === void 0 ? void 0 : operator.toLowerCase())) {
|
|
60
|
+
return operations.many;
|
|
61
|
+
}
|
|
62
|
+
const field = (left === null || left === void 0 ? void 0 : left.column) || (left === null || left === void 0 ? void 0 : left.value) || left || "";
|
|
63
|
+
const isUniqueField = uniqueFields.includes((_a = field === null || field === void 0 ? void 0 : field.toLowerCase) === null || _a === void 0 ? void 0 : _a.call(field));
|
|
64
|
+
if (isUniqueField && operator === "=") {
|
|
65
|
+
return operations.one;
|
|
66
|
+
}
|
|
67
|
+
return [">", "<", ">=", "<=", "!=", "<>", "like", "in"].includes(operator === null || operator === void 0 ? void 0 : operator.toLowerCase())
|
|
68
|
+
? operations.many
|
|
69
|
+
: operations.one;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=queryUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryUtils.js","sourceRoot":"","sources":["../../../src/infrastructure/utils/queryUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MongoConverter.d.ts","sourceRoot":"","sources":["../../../src/application/MongoConverter.ts"],"names":[],"mappings":"AAUA,qBAAa,cAAc;IACzB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAcxC;CACF;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MongoConverter.spec.d.ts","sourceRoot":"","sources":["../../../src/application/MongoConverter.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/cli/main.ts"],"names":[],"mappings":""}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Implementa Strategy Pattern para permitir diferentes estrategias de conversión
|
|
4
4
|
*/
|
|
5
5
|
export interface IConverter {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
can(statement: string): boolean;
|
|
7
|
+
convert(query: any): Record<string, any>;
|
|
8
8
|
}
|
|
9
|
+
//# sourceMappingURL=Converter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Converter.d.ts","sourceRoot":"","sources":["../../../../src/domain/interfaces/Converter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,OAAO,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC1C"}
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
* API pública del módulo
|
|
3
3
|
* Exporta las funcionalidades principales para uso como librería
|
|
4
4
|
*/
|
|
5
|
-
export {
|
|
6
|
-
MongoConverter,
|
|
7
|
-
mongoConverter,
|
|
8
|
-
} from "./application/MongoConverter.ts";
|
|
5
|
+
export { MongoConverter, mongoConverter, } from "./application/MongoConverter.ts";
|
|
9
6
|
export type { IConverter } from "./domain/interfaces/Converter.ts";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,cAAc,EACd,cAAc,GACf,MAAM,iCAAiC,CAAC;AACzC,YAAY,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory que orquesta los conversores
|
|
3
|
+
* Implementa Strategy Pattern + Factory Pattern
|
|
4
|
+
* Centraliza la lógica de selección de conversor
|
|
5
|
+
*/
|
|
6
|
+
import type { IConverter } from "../../domain/interfaces/Converter.ts";
|
|
7
|
+
export declare const getConverter: (statement: string) => IConverter | null;
|
|
8
|
+
//# sourceMappingURL=ConverterFactory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConverterFactory.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/converters/ConverterFactory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAavE,eAAO,MAAM,YAAY,cAAe,MAAM,KAAG,UAAU,GAAG,IAE7D,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversor para queries CREATE/INSERT
|
|
3
|
+
* Convierte CREATE/INSERT SQL a insertOne/insertMany de MongoDB
|
|
4
|
+
*/
|
|
5
|
+
import type { IConverter } from "../../domain/interfaces/Converter.ts";
|
|
6
|
+
export declare const CreateConverter: IConverter;
|
|
7
|
+
//# sourceMappingURL=CreateConverter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreateConverter.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/converters/CreateConverter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAGvE,eAAO,MAAM,eAAe,EAAE,UA6B7B,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversor para queries DELETE
|
|
3
|
+
* Convierte DELETE SQL a deleteOne/deleteMany de MongoDB
|
|
4
|
+
*/
|
|
5
|
+
import type { IConverter } from "../../domain/interfaces/Converter.ts";
|
|
6
|
+
export declare const DeleteConverter: IConverter;
|
|
7
|
+
//# sourceMappingURL=DeleteConverter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeleteConverter.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/converters/DeleteConverter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAOvE,eAAO,MAAM,eAAe,EAAE,UAoB7B,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversor para queries SELECT
|
|
3
|
+
* Convierte SELECT SQL a aggregation pipeline o find query de MongoDB
|
|
4
|
+
*/
|
|
5
|
+
import type { IConverter } from "../../domain/interfaces/Converter.ts";
|
|
6
|
+
export declare const SelectConverter: IConverter;
|
|
7
|
+
//# sourceMappingURL=SelectConverter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelectConverter.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/converters/SelectConverter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAOvE,eAAO,MAAM,eAAe,EAAE,UAyB7B,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversor para queries UPDATE
|
|
3
|
+
* Convierte UPDATE SQL a updateOne/updateMany de MongoDB
|
|
4
|
+
*/
|
|
5
|
+
import type { IConverter } from "../../domain/interfaces/Converter.ts";
|
|
6
|
+
export declare const UpdateConverter: IConverter;
|
|
7
|
+
//# sourceMappingURL=UpdateConverter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UpdateConverter.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/converters/UpdateConverter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAOvE,eAAO,MAAM,eAAe,EAAE,UAwB7B,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser SQL que encapsula node-sql-parser
|
|
3
|
+
* Abstrae la dependencia externa y proporciona una interfaz limpia
|
|
4
|
+
*/
|
|
5
|
+
import sqlParser from "node-sql-parser";
|
|
6
|
+
declare const parser: sqlParser.Parser;
|
|
7
|
+
export type ParsedQuery = ReturnType<typeof parser.parse>;
|
|
8
|
+
export declare const parseSqlQuery: (sql: string) => ParsedQuery | ParsedQuery[];
|
|
9
|
+
export declare const normalizeQuery: (query: string) => string;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=sqlParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlParser.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/parser/sqlParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,SAAS,MAAM,iBAAiB,CAAC;AAExC,QAAA,MAAM,MAAM,kBAAyB,CAAC;AAEtC,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AAE1D,eAAO,MAAM,aAAa,QAAS,MAAM,KAAG,WAAW,GAAG,WAAW,EAEpE,CAAC;AAEF,eAAO,MAAM,cAAc,UAAW,MAAM,KAAG,MAE9C,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const removeUndefinedFields: (obj: Record<string, any>) => Record<string, any>;
|
|
2
|
+
export declare const normalizeFieldName: (field: string) => string;
|
|
3
|
+
export declare const buildProjection: (fields: string[] | "*") => Record<string, 1 | 0>;
|
|
4
|
+
export declare const buildFilter: (condition: any) => Record<string, any>;
|
|
5
|
+
export declare function determineOperation(where: any, operations: {
|
|
6
|
+
one: string;
|
|
7
|
+
many: string;
|
|
8
|
+
}): string;
|
|
9
|
+
//# sourceMappingURL=queryUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryUtils.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/utils/queryUtils.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,qBAAqB,QAC3B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KACvB,MAAM,CAAC,MAAM,EAAE,GAAG,CAEpB,CAAC;AAEF,eAAO,MAAM,kBAAkB,UAAW,MAAM,KAAG,MAElD,CAAC;AAMF,eAAO,MAAM,eAAe,WAClB,MAAM,EAAE,GAAG,GAAG,KACrB,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAGtB,CAAC;AAEF,eAAO,MAAM,WAAW,cAAe,GAAG,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAsC9D,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,GAAG,EACV,UAAU,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACxC,MAAM,CAsBR"}
|
package/examples.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Ejemplos de uso de QueryMongo
|
|
5
|
+
* Demuestra diferentes formas de usar la librería
|
|
6
|
+
*/
|
|
7
|
+
import { mongoConverter } from "./src/application/MongoConverter.ts";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
|
|
10
|
+
interface Example {
|
|
11
|
+
title: string;
|
|
12
|
+
sql: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const examples: Example[] = [
|
|
17
|
+
{
|
|
18
|
+
title: "📌 SELECT Simple",
|
|
19
|
+
sql: "SELECT name, email FROM users",
|
|
20
|
+
description: "Selecciona campos específicos",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
title: "🔍 SELECT con WHERE",
|
|
24
|
+
sql: "SELECT * FROM products WHERE price > 100",
|
|
25
|
+
description: "Filtra documentos con condición",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
title: "📊 SELECT con WHERE y LIMIT",
|
|
29
|
+
sql: "SELECT name, age FROM users WHERE age >= 18 LIMIT 10",
|
|
30
|
+
description: "Proyección, filtro y límite combinados",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
title: "➕ INSERT Simple",
|
|
34
|
+
sql: "INSERT INTO users (name, email, age) VALUES ('John Doe', 'john@example.com', 30)",
|
|
35
|
+
description: "Insertar un documento",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
title: "✏️ UPDATE con WHERE",
|
|
39
|
+
sql: 'UPDATE users SET age = 31 WHERE name = "John Doe"',
|
|
40
|
+
description: "Actualizar documentos que cumplen condición",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
title: "🗑️ DELETE con WHERE",
|
|
44
|
+
sql: "DELETE FROM users WHERE age < 18",
|
|
45
|
+
description: "Eliminar documentos que cumplen condición",
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
console.log(chalk.bold.blue("\n🚀 QueryMongo - Ejemplos de Uso\n"));
|
|
50
|
+
console.log(chalk.gray("Convierte queries SQL a MongoDB format\n"));
|
|
51
|
+
|
|
52
|
+
examples.forEach((example, index) => {
|
|
53
|
+
console.log(chalk.bold.cyan(`\n${index + 1}. ${example.title}`));
|
|
54
|
+
if (example.description) {
|
|
55
|
+
console.log(chalk.gray(` ${example.description}`));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log(chalk.yellow("\n SQL:"));
|
|
59
|
+
console.log(chalk.white(` ${example.sql}`));
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const result = mongoConverter.convert(example.sql);
|
|
63
|
+
|
|
64
|
+
console.log(chalk.green("\n 📋 MongoDB Query:"));
|
|
65
|
+
const resultStr = JSON.stringify(result, null, 4)
|
|
66
|
+
.split("\n")
|
|
67
|
+
.map((line) => ` ${line}`)
|
|
68
|
+
.join("\n");
|
|
69
|
+
console.log(resultStr);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(
|
|
72
|
+
chalk.red(
|
|
73
|
+
` ❌ Error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
74
|
+
),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
console.log(chalk.bold.green("\n✨ Todos los ejemplos completados\n"));
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "querymongo",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"description": "SQL to MongoDB Query Converter CLI",
|
|
6
|
-
"main": "dist/
|
|
7
|
-
"bin":
|
|
8
|
-
"querymongo": "dist/cli/main.js"
|
|
9
|
-
},
|
|
6
|
+
"main": "dist/cli/main.js",
|
|
7
|
+
"bin": "dist/cli/main.js",
|
|
10
8
|
"scripts": {
|
|
9
|
+
"prepare": "npm run build",
|
|
11
10
|
"clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
12
11
|
"build": "npm run clean && tsc",
|
|
13
12
|
"start": "npm run build && node dist/cli/main.js",
|
|
@@ -21,10 +20,6 @@
|
|
|
21
20
|
"converter",
|
|
22
21
|
"query"
|
|
23
22
|
],
|
|
24
|
-
"files": [
|
|
25
|
-
"src",
|
|
26
|
-
"package.json"
|
|
27
|
-
],
|
|
28
23
|
"repository": {
|
|
29
24
|
"url": "https://github.com/rubsuadav/querymongo",
|
|
30
25
|
"type": "git"
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests para verificar las conversiones SQL a MongoDB
|
|
3
|
-
*/
|
|
4
|
-
import { test, describe } from "node:test";
|
|
5
|
-
import { equal, ok, throws } from "node:assert";
|
|
6
|
-
import { mongoConverter } from "../application/MongoConverter.ts";
|
|
7
|
-
|
|
8
|
-
describe("MongoConverter", () => {
|
|
9
|
-
describe("SELECT queries", () => {
|
|
10
|
-
test("should convert simple SELECT query", () => {
|
|
11
|
-
const sql = "SELECT name, email FROM users";
|
|
12
|
-
const result = mongoConverter.convert(sql);
|
|
13
|
-
|
|
14
|
-
equal(result.collection, "users");
|
|
15
|
-
ok(result.pipeline.some((stage: any) => stage.$project));
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
test("should convert SELECT with WHERE clause", () => {
|
|
19
|
-
const sql = "SELECT * FROM users WHERE id = 1";
|
|
20
|
-
const result = mongoConverter.convert(sql);
|
|
21
|
-
|
|
22
|
-
equal(result.collection, "users");
|
|
23
|
-
ok(result.pipeline.some((stage: any) => stage.$match));
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test("should convert SELECT with LIMIT", () => {
|
|
27
|
-
const sql = "SELECT name FROM products LIMIT 10";
|
|
28
|
-
const result = mongoConverter.convert(sql);
|
|
29
|
-
|
|
30
|
-
ok(result.pipeline.some((stage: any) => stage.$limit === 10));
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
describe("INSERT queries", () => {
|
|
35
|
-
test("should convert INSERT query", () => {
|
|
36
|
-
const sql =
|
|
37
|
-
"INSERT INTO users (name, email) VALUES ('John', 'john@example.com')";
|
|
38
|
-
const result = mongoConverter.convert(sql);
|
|
39
|
-
|
|
40
|
-
equal(result.collection, "users");
|
|
41
|
-
equal(result.operation, "insertOne");
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
describe("UPDATE queries", () => {
|
|
46
|
-
test("should convert UPDATE query", () => {
|
|
47
|
-
const sql = 'UPDATE users SET name = "Jane" WHERE id = 1';
|
|
48
|
-
const result = mongoConverter.convert(sql);
|
|
49
|
-
|
|
50
|
-
equal(result.collection, "users");
|
|
51
|
-
equal(result.operation, "updateOne");
|
|
52
|
-
ok(result.update.$set);
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe("DELETE queries", () => {
|
|
57
|
-
test("should convert DELETE query", () => {
|
|
58
|
-
const sql = "DELETE FROM users WHERE id = 1";
|
|
59
|
-
const result = mongoConverter.convert(sql);
|
|
60
|
-
|
|
61
|
-
equal(result.collection, "users");
|
|
62
|
-
equal(result.operation, "deleteOne");
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe("Error handling", () => {
|
|
67
|
-
test("should throw on unsupported query type", () => {
|
|
68
|
-
const sql = "GRANT SELECT ON users TO john";
|
|
69
|
-
|
|
70
|
-
throws(() => mongoConverter.convert(sql), /Unsupported SQL statement/);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Servicio principal de conversión
|
|
3
|
-
* Orquesta el parsing y la conversión a MongoDB
|
|
4
|
-
*/
|
|
5
|
-
import {
|
|
6
|
-
parseSqlQuery,
|
|
7
|
-
normalizeQuery,
|
|
8
|
-
} from "../infrastructure/parser/sqlParser.ts";
|
|
9
|
-
import { getConverter } from "../infrastructure/converters/ConverterFactory.ts";
|
|
10
|
-
|
|
11
|
-
export class MongoConverter {
|
|
12
|
-
convert(sql: string): Record<string, any> {
|
|
13
|
-
const normalized = normalizeQuery(sql);
|
|
14
|
-
const parsed = parseSqlQuery(normalized);
|
|
15
|
-
|
|
16
|
-
const statement = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
17
|
-
const converter = getConverter(normalized);
|
|
18
|
-
|
|
19
|
-
if (!converter) {
|
|
20
|
-
throw new Error(
|
|
21
|
-
`Unsupported SQL statement: ${normalized.substring(0, 50)}...`,
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return converter.convert(statement);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const mongoConverter = new MongoConverter();
|
package/src/cli/main.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* CLI para convertir queries SQL a MongoDB
|
|
5
|
-
* Interfaz de línea de comandos usando Commander
|
|
6
|
-
*/
|
|
7
|
-
import { Command } from "commander";
|
|
8
|
-
import chalk from "chalk";
|
|
9
|
-
import { mongoConverter } from "../application/MongoConverter.ts";
|
|
10
|
-
|
|
11
|
-
const program = new Command();
|
|
12
|
-
|
|
13
|
-
program
|
|
14
|
-
.name("querymongo")
|
|
15
|
-
.description("Convert SQL queries to MongoDB format")
|
|
16
|
-
.version("1.0.0")
|
|
17
|
-
.usage("[command]");
|
|
18
|
-
|
|
19
|
-
program
|
|
20
|
-
.command("interactive")
|
|
21
|
-
.description("Start interactive mode")
|
|
22
|
-
.action(async () => {
|
|
23
|
-
const readline = await import("readline");
|
|
24
|
-
const rl = readline.createInterface({
|
|
25
|
-
input: process.stdin,
|
|
26
|
-
output: process.stdout,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
const question = (prompt: string): Promise<string> => {
|
|
30
|
-
return new Promise((resolve) => rl.question(prompt, resolve));
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
console.log(
|
|
34
|
-
chalk.blue("\n📊 SQL to MongoDB Query Converter - Interactive Mode\n"),
|
|
35
|
-
);
|
|
36
|
-
console.log(chalk.gray('Type "exit" to quit\n'));
|
|
37
|
-
|
|
38
|
-
while (true) {
|
|
39
|
-
const sql = await question(chalk.yellow("SQL Query > "));
|
|
40
|
-
|
|
41
|
-
if (sql.toLowerCase() === "exit") {
|
|
42
|
-
console.log(chalk.blue("Goodbye! 👋\n"));
|
|
43
|
-
rl.close();
|
|
44
|
-
break;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!sql.trim()) continue;
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
const result = mongoConverter.convert(sql);
|
|
51
|
-
console.log(chalk.green("\n✓ MongoDB Query:\n"));
|
|
52
|
-
console.log(chalk.cyan(JSON.stringify(result, null, 2)) + "\n");
|
|
53
|
-
} catch (error) {
|
|
54
|
-
console.error(
|
|
55
|
-
chalk.red(
|
|
56
|
-
`✗ Error: ${error instanceof Error ? error.message : "Unknown error"}\n`,
|
|
57
|
-
),
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
})
|
|
62
|
-
.usage(" ");
|
|
63
|
-
|
|
64
|
-
program.parse(process.argv);
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Factory que orquesta los conversores
|
|
3
|
-
* Implementa Strategy Pattern + Factory Pattern
|
|
4
|
-
* Centraliza la lógica de selección de conversor
|
|
5
|
-
*/
|
|
6
|
-
import type { IConverter } from "../../domain/interfaces/Converter.ts";
|
|
7
|
-
import { SelectConverter } from "./SelectConverter.ts";
|
|
8
|
-
import { CreateConverter } from "./CreateConverter.ts";
|
|
9
|
-
import { UpdateConverter } from "./UpdateConverter.ts";
|
|
10
|
-
import { DeleteConverter } from "./DeleteConverter.ts";
|
|
11
|
-
|
|
12
|
-
const CONVERTERS: IConverter[] = [
|
|
13
|
-
SelectConverter,
|
|
14
|
-
CreateConverter,
|
|
15
|
-
UpdateConverter,
|
|
16
|
-
DeleteConverter,
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
export const getConverter = (statement: string): IConverter | null => {
|
|
20
|
-
return CONVERTERS.find((converter) => converter.can(statement)) || null;
|
|
21
|
-
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Conversor para queries CREATE/INSERT
|
|
3
|
-
* Convierte CREATE/INSERT SQL a insertOne/insertMany de MongoDB
|
|
4
|
-
*/
|
|
5
|
-
import type { IConverter } from "../../domain/interfaces/Converter.ts";
|
|
6
|
-
import { removeUndefinedFields } from "../utils/queryUtils.ts";
|
|
7
|
-
|
|
8
|
-
export const CreateConverter: IConverter = {
|
|
9
|
-
can: (statement: string): boolean => {
|
|
10
|
-
const lower = statement.toLowerCase();
|
|
11
|
-
return lower.startsWith("insert") || lower.startsWith("create");
|
|
12
|
-
},
|
|
13
|
-
|
|
14
|
-
convert: (query: any): Record<string, any> => {
|
|
15
|
-
const { ast } = query;
|
|
16
|
-
const { table, columns, values } = ast;
|
|
17
|
-
|
|
18
|
-
const collectionName = table?.[0]?.table || "collection";
|
|
19
|
-
const valuesList = values.values || [];
|
|
20
|
-
|
|
21
|
-
const docs = valuesList.map((v: any) => {
|
|
22
|
-
const row = v.value || v;
|
|
23
|
-
return Object.fromEntries(
|
|
24
|
-
columns.map((col: string, idx: number) => [
|
|
25
|
-
col,
|
|
26
|
-
row[idx]?.value || row[idx],
|
|
27
|
-
]),
|
|
28
|
-
);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
return removeUndefinedFields({
|
|
32
|
-
collection: collectionName,
|
|
33
|
-
operation: docs.length > 1 ? "insertMany" : "insertOne",
|
|
34
|
-
documents: docs.length === 1 ? docs[0] : docs,
|
|
35
|
-
});
|
|
36
|
-
},
|
|
37
|
-
};
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Conversor para queries DELETE
|
|
3
|
-
* Convierte DELETE SQL a deleteOne/deleteMany de MongoDB
|
|
4
|
-
*/
|
|
5
|
-
import type { IConverter } from "../../domain/interfaces/Converter.ts";
|
|
6
|
-
import {
|
|
7
|
-
buildFilter,
|
|
8
|
-
determineOperation,
|
|
9
|
-
removeUndefinedFields,
|
|
10
|
-
} from "../utils/queryUtils.ts";
|
|
11
|
-
|
|
12
|
-
export const DeleteConverter: IConverter = {
|
|
13
|
-
can: (statement: string): boolean => {
|
|
14
|
-
return statement.toLowerCase().startsWith("delete");
|
|
15
|
-
},
|
|
16
|
-
|
|
17
|
-
convert: (query: any): Record<string, any> => {
|
|
18
|
-
const { ast } = query;
|
|
19
|
-
const { from, where } = ast;
|
|
20
|
-
|
|
21
|
-
const collectionName = from?.[0]?.table || "collection";
|
|
22
|
-
|
|
23
|
-
return removeUndefinedFields({
|
|
24
|
-
collection: collectionName,
|
|
25
|
-
operation: determineOperation(where, {
|
|
26
|
-
one: "deleteOne",
|
|
27
|
-
many: "deleteMany",
|
|
28
|
-
}),
|
|
29
|
-
filter: where ? buildFilter(where) : {},
|
|
30
|
-
});
|
|
31
|
-
},
|
|
32
|
-
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Conversor para queries SELECT
|
|
3
|
-
* Convierte SELECT SQL a aggregation pipeline o find query de MongoDB
|
|
4
|
-
*/
|
|
5
|
-
import type { IConverter } from "../../domain/interfaces/Converter.ts";
|
|
6
|
-
import {
|
|
7
|
-
buildProjection,
|
|
8
|
-
buildFilter,
|
|
9
|
-
removeUndefinedFields,
|
|
10
|
-
} from "../utils/queryUtils.ts";
|
|
11
|
-
|
|
12
|
-
export const SelectConverter: IConverter = {
|
|
13
|
-
can: (statement: string): boolean => {
|
|
14
|
-
return statement.toLowerCase().startsWith("select");
|
|
15
|
-
},
|
|
16
|
-
|
|
17
|
-
convert: (query: any): Record<string, any> => {
|
|
18
|
-
const { ast } = query;
|
|
19
|
-
const { columns, from, where, limit } = ast;
|
|
20
|
-
|
|
21
|
-
const fields =
|
|
22
|
-
columns && columns[0]?.expr?.column !== "*"
|
|
23
|
-
? columns.map((col: any) => col.expr.column)
|
|
24
|
-
: "*";
|
|
25
|
-
|
|
26
|
-
const limitValue = limit?.value?.[0]?.value || limit?.value || limit;
|
|
27
|
-
|
|
28
|
-
return removeUndefinedFields({
|
|
29
|
-
collection: from?.[0]?.table || "collection",
|
|
30
|
-
pipeline: [
|
|
31
|
-
where && { $match: buildFilter(where) },
|
|
32
|
-
fields !== "*" && { $project: buildProjection(fields) },
|
|
33
|
-
limitValue && { $limit: limitValue },
|
|
34
|
-
].filter(Boolean),
|
|
35
|
-
});
|
|
36
|
-
},
|
|
37
|
-
};
|