querymongo 1.1.0 → 1.3.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +19 -45
  3. package/dist/application/MongoConverter.js +1 -1
  4. package/dist/application/MongoConverter.js.map +1 -1
  5. package/dist/application/MongoConverter.spec.js +136 -4
  6. package/dist/application/MongoConverter.spec.js.map +1 -1
  7. package/dist/cli/main.js +2 -11
  8. package/dist/cli/main.js.map +1 -1
  9. package/dist/index.js +1 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/infrastructure/converters/ConverterFactory.js +2 -0
  12. package/dist/infrastructure/converters/ConverterFactory.js.map +1 -1
  13. package/dist/infrastructure/converters/ConverterFactory.spec.js +51 -0
  14. package/dist/infrastructure/converters/ConverterFactory.spec.js.map +1 -0
  15. package/dist/infrastructure/converters/JoinConverter.js +49 -0
  16. package/dist/infrastructure/converters/JoinConverter.js.map +1 -0
  17. package/dist/infrastructure/converters/SelectConverter.js +7 -8
  18. package/dist/infrastructure/converters/SelectConverter.js.map +1 -1
  19. package/dist/infrastructure/utils/queryUtils.js +22 -6
  20. package/dist/infrastructure/utils/queryUtils.js.map +1 -1
  21. package/dist/infrastructure/utils/queryUtils.spec.js +261 -0
  22. package/dist/infrastructure/utils/queryUtils.spec.js.map +1 -0
  23. package/dist/types/application/MongoConverter.d.ts +2 -1
  24. package/dist/types/application/MongoConverter.d.ts.map +1 -1
  25. package/dist/types/index.d.ts +1 -1
  26. package/dist/types/index.d.ts.map +1 -1
  27. package/dist/types/infrastructure/converters/ConverterFactory.d.ts.map +1 -1
  28. package/dist/types/infrastructure/converters/ConverterFactory.spec.d.ts +2 -0
  29. package/dist/types/infrastructure/converters/ConverterFactory.spec.d.ts.map +1 -0
  30. package/dist/types/infrastructure/converters/JoinConverter.d.ts +7 -0
  31. package/dist/types/infrastructure/converters/JoinConverter.d.ts.map +1 -0
  32. package/dist/types/infrastructure/converters/SelectConverter.d.ts.map +1 -1
  33. package/dist/types/infrastructure/utils/queryUtils.d.ts +7 -2
  34. package/dist/types/infrastructure/utils/queryUtils.d.ts.map +1 -1
  35. package/dist/types/infrastructure/utils/queryUtils.spec.d.ts +2 -0
  36. package/dist/types/infrastructure/utils/queryUtils.spec.d.ts.map +1 -0
  37. package/package.json +1 -1
  38. package/.github/workflows/ci.yml +0 -61
  39. package/.vscode/settings.json +0 -3
  40. package/ARCHITECTURE.md +0 -286
  41. package/examples.ts +0 -79
package/ARCHITECTURE.md DELETED
@@ -1,286 +0,0 @@
1
- # 🏗️ Arquitectura y Principios SOLID
2
-
3
- ## Resumen Ejecutivo
4
-
5
- QueryMongo está diseñado siguiendo los 5 principios SOLID, Clean Code y DRY (Don't Repeat Yourself). El código es modular, escalable y fácil de mantener.
6
-
7
- ## 📐 Principios SOLID Aplicados
8
-
9
- ### 1. **S - Single Responsibility Principle**
10
-
11
- Cada clase/función tiene una única responsabilidad:
12
-
13
- ```typescript
14
- // ✅ BIEN: SelectConverter solo convierte SELECT
15
- export const SelectConverter: IConverter = {
16
- can: (statement) => statement.toLowerCase().startsWith("select"),
17
- convert: (query) => {
18
- /* lógica solo para SELECT */
19
- },
20
- };
21
-
22
- // ✅ BIEN: buildProjection solo construye proyecciones
23
- export const buildProjection = (fields: string[]): Record<string, 1 | 0> => {
24
- // lógica única
25
- };
26
- ```
27
-
28
- ### 2. **O - Open/Closed Principle**
29
-
30
- El código está abierto a extensión pero cerrado a modificación:
31
-
32
- ```typescript
33
- // Agregar un nuevo tipo de query (ej: TRUNCATE) es solo:
34
- export const TruncateConverter: IConverter = {
35
- can: (statement) => statement.toLowerCase().startsWith("truncate"),
36
- convert: (query) => {
37
- /* implementación */
38
- },
39
- };
40
-
41
- // Y registrarlo en ConverterFactory sin tocar código existente:
42
- const CONVERTERS: IConverter[] = [
43
- SelectConverter,
44
- CreateConverter,
45
- UpdateConverter,
46
- DeleteConverter,
47
- TruncateConverter, // ← Nueva línea solamente
48
- ];
49
- ```
50
-
51
- ### 3. **L - Liskov Substitution Principle**
52
-
53
- Todos los conversores implementan la interface `IConverter`:
54
-
55
- ```typescript
56
- interface IConverter {
57
- can(statement: string): boolean;
58
- convert(query: any): Record<string, any>;
59
- }
60
-
61
- // SelectConverter, CreateConverter, UpdateConverter, DeleteConverter
62
- // pueden usarse indistintamente donde se espera IConverter
63
- ```
64
-
65
- ### 4. **I - Interface Segregation Principle**
66
-
67
- Las interfaces son mínimas y específicas:
68
-
69
- ```typescript
70
- // Interface pequeña y enfocada
71
- interface IConverter {
72
- can(statement: string): boolean;
73
- convert(query: any): Record<string, any>;
74
- }
75
-
76
- // No hay métodos innecesarios, solo lo que se necesita
77
- ```
78
-
79
- ### 5. **D - Dependency Inversion Principle**
80
-
81
- Dependemos de abstracciones, no de implementaciones concretas:
82
-
83
- ```typescript
84
- // MongoConverter depende de la interface, no de las implementaciones
85
- const converter = getConverter(sql); // Retorna IConverter, no una clase específica
86
-
87
- // Si el parser cambia, solo cambio sqlParser.ts
88
- // Si un conversor cambia, los otros no se ven afectados
89
- ```
90
-
91
- ## 🏛️ Arquitectura en Capas
92
-
93
- ```
94
- ┌─────────────────────────────────────┐
95
- │ CLI Layer (main.ts) │
96
- │ - Commander para argumentos │
97
- │ - Interfaz interactiva │
98
- └─────────────────────────────────────┘
99
-
100
- ┌─────────────────────────────────────┐
101
- │ Application Layer │
102
- │ (MongoConverter.ts) │
103
- │ - Orquestación del flujo │
104
- │ - Error handling │
105
- └─────────────────────────────────────┘
106
-
107
- ┌─────────────────────────────────────┐
108
- │ Infrastructure Layer │
109
- │ - Parsers (sqlParser.ts) │
110
- │ - Conversores (SelectConverter...) │
111
- │ - Utilidades (queryUtils.ts) │
112
- └─────────────────────────────────────┘
113
-
114
- ┌─────────────────────────────────────┐
115
- │ Domain Layer │
116
- │ - Interfaces (IConverter) │
117
- │ - Tipos y contratos │
118
- └─────────────────────────────────────┘
119
-
120
- ┌─────────────────────────────────────┐
121
- │ External Dependencies │
122
- │ - node-sql-parser │
123
- │ - lodash │
124
- │ - chalk, commander │
125
- └─────────────────────────────────────┘
126
- ```
127
-
128
- ## 🎯 Patrones de Diseño
129
-
130
- ### Strategy Pattern
131
-
132
- Cada tipo de SQL es una estrategia diferente:
133
-
134
- ```typescript
135
- // SelectConverter es una estrategia
136
- const SelectConverter: IConverter = {
137
- can: (stmt) => stmt.startsWith("select"),
138
- convert: (query) => {
139
- /* convertir SELECT */
140
- },
141
- };
142
-
143
- // UpdateConverter es una estrategia diferente
144
- const UpdateConverter: IConverter = {
145
- can: (stmt) => stmt.startsWith("update"),
146
- convert: (query) => {
147
- /* convertir UPDATE */
148
- },
149
- };
150
-
151
- // Factory selecciona la estrategia correcta
152
- const converter = CONVERTERS.find((c) => c.can(sql));
153
- ```
154
-
155
- ### Factory Pattern
156
-
157
- `ConverterFactory` determina qué conversor usar:
158
-
159
- ```typescript
160
- export const getConverter = (statement: string): IConverter | null => {
161
- return CONVERTERS.find((converter) => converter.can(statement)) || null;
162
- };
163
- ```
164
-
165
- ### Adapter Pattern
166
-
167
- `sqlParser.ts` adapta `node-sql-parser` a una interfaz limpia:
168
-
169
- ```typescript
170
- // Internamente usa node-sql-parser
171
- const sqlParser = new Parser();
172
-
173
- // Pero expone una interfaz simple
174
- export const parseSqlQuery = (sql: string) => sqlParser.parse(sql);
175
- ```
176
-
177
- ## 🧹 DRY - Don't Repeat Yourself
178
-
179
- Toda lógica compartida está centralizada:
180
-
181
- ```typescript
182
- // queryUtils.ts - Funciones reutilizables
183
- export const normalizeFieldName = (field: string): string => {
184
- return field.trim().replace(/`|"/g, '');
185
- };
186
-
187
- export const buildProjection = (fields: string[]): Record<string, 1 | 0> => {
188
- if (fields === '*') return {};
189
- return Object.fromEntries(normalizeFieldNames(fields).map(f => [f, 1]));
190
- };
191
-
192
- export const buildFilter = (condition: any): Record<string, any> => {
193
- // Lógica única para construir filters
194
- };
195
-
196
- // Los conversores reutilizan estas funciones
197
- SelectConverter usa buildProjection y buildFilter
198
- UpdateConverter usa buildFilter
199
- DeleteConverter usa buildFilter
200
- ```
201
-
202
- ## 🎓 Beneficios de esta Arquitectura
203
-
204
- | Beneficio | Impacto |
205
- | ------------------ | -------------------------------------------- |
206
- | **Mantenibilidad** | Cambios localizados, sin efectos secundarios |
207
- | **Escalabilidad** | Agregar nuevos tipos de query es trivial |
208
- | **Testabilidad** | 100% cobertura fácil de lograr |
209
- | **Reutilización** | Utilidades compartidas evitan duplicación |
210
- | **Legibilidad** | Código claro, intenciones obvias |
211
- | **Flexibilidad** | Fácil refactorizar componentes |
212
-
213
- ## 📊 Flujo de Conversión
214
-
215
- ```
216
- SQL Query
217
-
218
- normalizeQuery()
219
-
220
- parseSqlQuery()
221
-
222
- AST (Abstract Syntax Tree)
223
-
224
- getConverter(statement)
225
-
226
- Estrategia seleccionada
227
-
228
- converter.convert(ast)
229
-
230
- MongoDB Query Object
231
- ```
232
-
233
- ## 🔌 Extensibilidad
234
-
235
- Para agregar soporte para un nuevo tipo de query:
236
-
237
- 1. **Crear nuevo conversor**:
238
-
239
- ```typescript
240
- export const GroupByConverter: IConverter = {
241
- can: (stmt) => stmt.includes("GROUP BY"),
242
- convert: (query) => {
243
- /* implementación */
244
- },
245
- };
246
- ```
247
-
248
- 2. **Registrar en factory**:
249
-
250
- ```typescript
251
- const CONVERTERS: IConverter[] = [
252
- SelectConverter,
253
- CreateConverter,
254
- UpdateConverter,
255
- DeleteConverter,
256
- GroupByConverter, // ← Agregar aquí
257
- ];
258
- ```
259
-
260
- 3. **Escribir tests** - El resto funciona automáticamente gracias a la arquitectura.
261
-
262
- ## 📈 Métricas de Calidad
263
-
264
- - **Test Coverage**: 100%
265
- - **Cyclomatic Complexity**: Bajo (funciones simples y enfocadas)
266
- - **Lines of Code**: Mínimo (código conciso)
267
- - **SOLID Score**: 5/5
268
- - **Code Duplication**: ~0% (todo compartido)
269
-
270
- ## 🚀 Performance
271
-
272
- - **Parsing**: ~1-2ms por query
273
- - **Conversión**: <1ms
274
- - **Total**: <3ms por conversión
275
- - Sin dependencias pesadas, bundle mínimo
276
-
277
- ## 🔐 Error Handling
278
-
279
- ```typescript
280
- try {
281
- const result = mongoConverter.convert(sql);
282
- } catch (error) {
283
- // Error descriptivo
284
- throw new Error(`Unsupported SQL statement: ${sql}...`);
285
- }
286
- ```
package/examples.ts DELETED
@@ -1,79 +0,0 @@
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"));