@memberjunction/query-gen 0.0.1 → 2.126.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 (138) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +20 -0
  3. package/COORDINATOR.md +768 -0
  4. package/IMPLEMENTATION_PLAN.md +1753 -0
  5. package/LLM_ENTITY_GROUPING_PLAN.md +977 -0
  6. package/README.md +675 -29
  7. package/dist/cli/commands/export.d.ts +15 -0
  8. package/dist/cli/commands/export.d.ts.map +1 -0
  9. package/dist/cli/commands/export.js +178 -0
  10. package/dist/cli/commands/export.js.map +1 -0
  11. package/dist/cli/commands/generate.d.ts +19 -0
  12. package/dist/cli/commands/generate.d.ts.map +1 -0
  13. package/dist/cli/commands/generate.js +282 -0
  14. package/dist/cli/commands/generate.js.map +1 -0
  15. package/dist/cli/commands/validate.d.ts +17 -0
  16. package/dist/cli/commands/validate.d.ts.map +1 -0
  17. package/dist/cli/commands/validate.js +193 -0
  18. package/dist/cli/commands/validate.js.map +1 -0
  19. package/dist/cli/config.d.ts +51 -0
  20. package/dist/cli/config.d.ts.map +1 -0
  21. package/dist/cli/config.js +142 -0
  22. package/dist/cli/config.js.map +1 -0
  23. package/dist/cli/index.d.ts +13 -0
  24. package/dist/cli/index.d.ts.map +1 -0
  25. package/dist/cli/index.js +57 -0
  26. package/dist/cli/index.js.map +1 -0
  27. package/dist/core/EntityGrouper.d.ts +74 -0
  28. package/dist/core/EntityGrouper.d.ts.map +1 -0
  29. package/dist/core/EntityGrouper.js +246 -0
  30. package/dist/core/EntityGrouper.js.map +1 -0
  31. package/dist/core/MetadataExporter.d.ts +59 -0
  32. package/dist/core/MetadataExporter.d.ts.map +1 -0
  33. package/dist/core/MetadataExporter.js +151 -0
  34. package/dist/core/MetadataExporter.js.map +1 -0
  35. package/dist/core/QueryDatabaseWriter.d.ts +50 -0
  36. package/dist/core/QueryDatabaseWriter.d.ts.map +1 -0
  37. package/dist/core/QueryDatabaseWriter.js +152 -0
  38. package/dist/core/QueryDatabaseWriter.js.map +1 -0
  39. package/dist/core/QueryFixer.d.ts +48 -0
  40. package/dist/core/QueryFixer.d.ts.map +1 -0
  41. package/dist/core/QueryFixer.js +115 -0
  42. package/dist/core/QueryFixer.js.map +1 -0
  43. package/dist/core/QueryRefiner.d.ts +94 -0
  44. package/dist/core/QueryRefiner.d.ts.map +1 -0
  45. package/dist/core/QueryRefiner.js +267 -0
  46. package/dist/core/QueryRefiner.js.map +1 -0
  47. package/dist/core/QueryTester.d.ts +70 -0
  48. package/dist/core/QueryTester.d.ts.map +1 -0
  49. package/dist/core/QueryTester.js +243 -0
  50. package/dist/core/QueryTester.js.map +1 -0
  51. package/dist/core/QueryWriter.d.ts +57 -0
  52. package/dist/core/QueryWriter.d.ts.map +1 -0
  53. package/dist/core/QueryWriter.js +184 -0
  54. package/dist/core/QueryWriter.js.map +1 -0
  55. package/dist/core/QuestionGenerator.d.ts +58 -0
  56. package/dist/core/QuestionGenerator.d.ts.map +1 -0
  57. package/dist/core/QuestionGenerator.js +145 -0
  58. package/dist/core/QuestionGenerator.js.map +1 -0
  59. package/dist/data/schema.d.ts +230 -0
  60. package/dist/data/schema.d.ts.map +1 -0
  61. package/dist/data/schema.js +6 -0
  62. package/dist/data/schema.js.map +1 -0
  63. package/dist/index.d.ts +28 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +77 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/prompts/PromptNames.d.ts +32 -0
  68. package/dist/prompts/PromptNames.d.ts.map +1 -0
  69. package/dist/prompts/PromptNames.js +35 -0
  70. package/dist/prompts/PromptNames.js.map +1 -0
  71. package/dist/utils/category-builder.d.ts +28 -0
  72. package/dist/utils/category-builder.d.ts.map +1 -0
  73. package/dist/utils/category-builder.js +90 -0
  74. package/dist/utils/category-builder.js.map +1 -0
  75. package/dist/utils/entity-helpers.d.ts +49 -0
  76. package/dist/utils/entity-helpers.d.ts.map +1 -0
  77. package/dist/utils/entity-helpers.js +189 -0
  78. package/dist/utils/entity-helpers.js.map +1 -0
  79. package/dist/utils/error-handlers.d.ts +19 -0
  80. package/dist/utils/error-handlers.d.ts.map +1 -0
  81. package/dist/utils/error-handlers.js +41 -0
  82. package/dist/utils/error-handlers.js.map +1 -0
  83. package/dist/utils/graph-helpers.d.ts +51 -0
  84. package/dist/utils/graph-helpers.d.ts.map +1 -0
  85. package/dist/utils/graph-helpers.js +82 -0
  86. package/dist/utils/graph-helpers.js.map +1 -0
  87. package/dist/utils/prompt-helpers.d.ts +25 -0
  88. package/dist/utils/prompt-helpers.d.ts.map +1 -0
  89. package/dist/utils/prompt-helpers.js +66 -0
  90. package/dist/utils/prompt-helpers.js.map +1 -0
  91. package/dist/utils/query-helpers.d.ts +23 -0
  92. package/dist/utils/query-helpers.d.ts.map +1 -0
  93. package/dist/utils/query-helpers.js +34 -0
  94. package/dist/utils/query-helpers.js.map +1 -0
  95. package/dist/utils/user-helpers.d.ts +15 -0
  96. package/dist/utils/user-helpers.d.ts.map +1 -0
  97. package/dist/utils/user-helpers.js +32 -0
  98. package/dist/utils/user-helpers.js.map +1 -0
  99. package/dist/vectors/EmbeddingService.d.ts +58 -0
  100. package/dist/vectors/EmbeddingService.d.ts.map +1 -0
  101. package/dist/vectors/EmbeddingService.js +90 -0
  102. package/dist/vectors/EmbeddingService.js.map +1 -0
  103. package/dist/vectors/SimilaritySearch.d.ts +51 -0
  104. package/dist/vectors/SimilaritySearch.d.ts.map +1 -0
  105. package/dist/vectors/SimilaritySearch.js +85 -0
  106. package/dist/vectors/SimilaritySearch.js.map +1 -0
  107. package/docs/API.md +1040 -0
  108. package/docs/ARCHITECTURE.md +1120 -0
  109. package/examples/advanced-usage.ts +401 -0
  110. package/examples/basic-usage.ts +285 -0
  111. package/package.json +48 -6
  112. package/src/cli/commands/export.ts +173 -0
  113. package/src/cli/commands/generate.ts +330 -0
  114. package/src/cli/commands/validate.ts +185 -0
  115. package/src/cli/config.ts +203 -0
  116. package/src/cli/index.ts +63 -0
  117. package/src/core/EntityGrouper.ts +318 -0
  118. package/src/core/MetadataExporter.ts +148 -0
  119. package/src/core/QueryDatabaseWriter.ts +187 -0
  120. package/src/core/QueryFixer.ts +153 -0
  121. package/src/core/QueryRefiner.ts +382 -0
  122. package/src/core/QueryTester.ts +264 -0
  123. package/src/core/QueryWriter.ts +239 -0
  124. package/src/core/QuestionGenerator.ts +199 -0
  125. package/src/data/golden-queries.json +1371 -0
  126. package/src/data/schema.ts +252 -0
  127. package/src/index.ts +49 -0
  128. package/src/prompts/PromptNames.ts +36 -0
  129. package/src/utils/category-builder.ts +97 -0
  130. package/src/utils/entity-helpers.ts +203 -0
  131. package/src/utils/error-handlers.ts +41 -0
  132. package/src/utils/graph-helpers.ts +99 -0
  133. package/src/utils/prompt-helpers.ts +79 -0
  134. package/src/utils/query-helpers.ts +32 -0
  135. package/src/utils/user-helpers.ts +39 -0
  136. package/src/vectors/EmbeddingService.ts +109 -0
  137. package/src/vectors/SimilaritySearch.ts +108 -0
  138. package/tsconfig.json +39 -0
package/docs/API.md ADDED
@@ -0,0 +1,1040 @@
1
+ # QueryGen API Documentation
2
+
3
+ This document provides comprehensive API documentation for programmatic usage of the QueryGen package.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Core Classes](#core-classes)
9
+ - [Usage Patterns](#usage-patterns)
10
+ - [TypeScript Types](#typescript-types)
11
+ - [Configuration](#configuration)
12
+ - [Examples](#examples)
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @memberjunction/query-gen
18
+ ```
19
+
20
+ ## Core Classes
21
+
22
+ ### EntityGrouper
23
+
24
+ Analyzes entity relationships and creates logical groups for query generation.
25
+
26
+ #### Constructor
27
+
28
+ ```typescript
29
+ new EntityGrouper()
30
+ ```
31
+
32
+ #### Methods
33
+
34
+ ##### `generateEntityGroups()`
35
+
36
+ Generates entity groups from available entities based on foreign key relationships.
37
+
38
+ ```typescript
39
+ async generateEntityGroups(
40
+ entities: EntityInfo[],
41
+ minSize: number,
42
+ maxSize: number
43
+ ): Promise<EntityGroup[]>
44
+ ```
45
+
46
+ **Parameters:**
47
+ - `entities` - Array of EntityInfo objects to analyze
48
+ - `minSize` - Minimum number of entities per group (usually 1)
49
+ - `maxSize` - Maximum number of entities per group (typically 3)
50
+
51
+ **Returns:** Array of EntityGroup objects with relationship metadata
52
+
53
+ **Example:**
54
+ ```typescript
55
+ import { EntityGrouper } from '@memberjunction/query-gen';
56
+ import { Metadata } from '@memberjunction/core';
57
+
58
+ const md = new Metadata();
59
+ const entities = md.Entities.filter(e => e.SchemaName === 'dbo');
60
+
61
+ const grouper = new EntityGrouper();
62
+ const groups = await grouper.generateEntityGroups(entities, 1, 3);
63
+
64
+ console.log(`Generated ${groups.length} entity groups`);
65
+ // Output: Generated 42 entity groups
66
+
67
+ // Single entity group
68
+ console.log(groups[0]);
69
+ // {
70
+ // entities: [CustomersEntity],
71
+ // relationships: [],
72
+ // primaryEntity: CustomersEntity,
73
+ // relationshipType: 'single'
74
+ // }
75
+
76
+ // Multi-entity group
77
+ console.log(groups[1]);
78
+ // {
79
+ // entities: [CustomersEntity, OrdersEntity],
80
+ // relationships: [
81
+ // { from: 'Orders', to: 'Customers', via: 'CustomerID', type: 'many-to-one' }
82
+ // ],
83
+ // primaryEntity: OrdersEntity,
84
+ // relationshipType: 'parent-child'
85
+ // }
86
+ ```
87
+
88
+ ---
89
+
90
+ ### QuestionGenerator
91
+
92
+ Generates domain-specific business questions using AI.
93
+
94
+ #### Constructor
95
+
96
+ ```typescript
97
+ new QuestionGenerator(contextUser: UserInfo)
98
+ ```
99
+
100
+ **Parameters:**
101
+ - `contextUser` - User context for server-side operations
102
+
103
+ #### Methods
104
+
105
+ ##### `generateQuestions()`
106
+
107
+ Generates 1-2 business questions for an entity group using AI.
108
+
109
+ ```typescript
110
+ async generateQuestions(
111
+ entityGroup: EntityGroup
112
+ ): Promise<BusinessQuestion[]>
113
+ ```
114
+
115
+ **Parameters:**
116
+ - `entityGroup` - Entity group to generate questions for
117
+
118
+ **Returns:** Array of BusinessQuestion objects
119
+
120
+ **Example:**
121
+ ```typescript
122
+ import { QuestionGenerator } from '@memberjunction/query-gen';
123
+ import { UserInfo } from '@memberjunction/core';
124
+
125
+ const contextUser = new UserInfo();
126
+ contextUser.Email = 'user@example.com';
127
+ contextUser.Name = 'Test User';
128
+
129
+ const generator = new QuestionGenerator(contextUser);
130
+ const questions = await generator.generateQuestions(entityGroup);
131
+
132
+ console.log(questions);
133
+ // [
134
+ // {
135
+ // userQuestion: "What are the top customers by revenue?",
136
+ // description: "Identify high-value customers based on total order revenue",
137
+ // technicalDescription: "Sum order totals per customer, sort descending, limit to top 10",
138
+ // complexity: "medium",
139
+ // requiresAggregation: true,
140
+ // requiresJoins: true,
141
+ // entities: ["Customers", "Orders"]
142
+ // },
143
+ // {
144
+ // userQuestion: "How many orders does each customer have?",
145
+ // description: "Count number of orders per customer",
146
+ // technicalDescription: "Count orders grouped by customer",
147
+ // complexity: "simple",
148
+ // requiresAggregation: true,
149
+ // requiresJoins: true,
150
+ // entities: ["Customers", "Orders"]
151
+ // }
152
+ // ]
153
+ ```
154
+
155
+ ---
156
+
157
+ ### QueryWriter
158
+
159
+ Generates SQL query templates using AI with few-shot learning.
160
+
161
+ #### Constructor
162
+
163
+ ```typescript
164
+ new QueryWriter(contextUser: UserInfo)
165
+ ```
166
+
167
+ **Parameters:**
168
+ - `contextUser` - User context for server-side operations
169
+
170
+ #### Methods
171
+
172
+ ##### `generateQuery()`
173
+
174
+ Generates a Nunjucks SQL query template from a business question.
175
+
176
+ ```typescript
177
+ async generateQuery(
178
+ businessQuestion: BusinessQuestion,
179
+ entityMetadata: EntityMetadataForPrompt[],
180
+ fewShotExamples: GoldenQuery[]
181
+ ): Promise<GeneratedQuery>
182
+ ```
183
+
184
+ **Parameters:**
185
+ - `businessQuestion` - The business question to answer
186
+ - `entityMetadata` - Formatted metadata for entities involved
187
+ - `fewShotExamples` - Similar golden queries for few-shot learning
188
+
189
+ **Returns:** GeneratedQuery object with SQL, parameters, and output fields
190
+
191
+ **Example:**
192
+ ```typescript
193
+ import { QueryWriter, formatEntityMetadataForPrompt } from '@memberjunction/query-gen';
194
+ import { UserInfo } from '@memberjunction/core';
195
+
196
+ const contextUser = new UserInfo();
197
+ const writer = new QueryWriter(contextUser);
198
+
199
+ // Format entity metadata for prompt
200
+ const entityMetadata = entityGroup.entities.map(e =>
201
+ formatEntityMetadataForPrompt(e, entityGroup.entities)
202
+ );
203
+
204
+ // Find similar golden queries (from vector similarity search)
205
+ const fewShotExamples = []; // Array of GoldenQuery objects
206
+
207
+ // Generate query
208
+ const query = await writer.generateQuery(
209
+ businessQuestion,
210
+ entityMetadata,
211
+ fewShotExamples
212
+ );
213
+
214
+ console.log(query.sql);
215
+ // SELECT
216
+ // c.Name as CustomerName,
217
+ // COUNT(o.ID) as OrderCount,
218
+ // COALESCE(SUM(o.Total), 0) as TotalRevenue
219
+ // FROM [dbo].[vwCustomers] c
220
+ // LEFT JOIN [sales].[vwOrders] o ON o.CustomerID = c.ID
221
+ // WHERE c.Name LIKE {{ searchTerm | sqlString }}
222
+ // GROUP BY c.Name
223
+ // ORDER BY TotalRevenue DESC
224
+
225
+ console.log(query.parameters);
226
+ // [
227
+ // {
228
+ // name: 'searchTerm',
229
+ // type: 'string',
230
+ // isRequired: false,
231
+ // description: 'Customer name search term',
232
+ // usage: ['WHERE clause: c.Name LIKE searchTerm'],
233
+ // defaultValue: null,
234
+ // sampleValue: '%Smith%'
235
+ // }
236
+ // ]
237
+ ```
238
+
239
+ ---
240
+
241
+ ### QueryTester
242
+
243
+ Tests generated queries by executing them against the database.
244
+
245
+ #### Constructor
246
+
247
+ ```typescript
248
+ new QueryTester(
249
+ dataProvider: DatabaseProviderBase,
250
+ entityMetadata: EntityMetadataForPrompt[],
251
+ businessQuestion: BusinessQuestion,
252
+ contextUser: UserInfo
253
+ )
254
+ ```
255
+
256
+ **Parameters:**
257
+ - `dataProvider` - Database provider for SQL execution
258
+ - `entityMetadata` - Entity metadata for error context
259
+ - `businessQuestion` - Business question for error context
260
+ - `contextUser` - User context for server-side operations
261
+
262
+ #### Methods
263
+
264
+ ##### `testQuery()`
265
+
266
+ Tests a query by rendering and executing it. Automatically fixes errors up to maxAttempts.
267
+
268
+ ```typescript
269
+ async testQuery(
270
+ query: GeneratedQuery,
271
+ maxAttempts: number = 5
272
+ ): Promise<QueryTestResult>
273
+ ```
274
+
275
+ **Parameters:**
276
+ - `query` - The query to test
277
+ - `maxAttempts` - Maximum error-fixing attempts (default: 5)
278
+
279
+ **Returns:** QueryTestResult with success status, rendered SQL, and sample rows
280
+
281
+ **Example:**
282
+ ```typescript
283
+ import { QueryTester } from '@memberjunction/query-gen';
284
+ import { Metadata, UserInfo } from '@memberjunction/core';
285
+
286
+ const dataProvider = Metadata.Provider.DatabaseConnection as DatabaseProviderBase;
287
+ const contextUser = new UserInfo();
288
+
289
+ const tester = new QueryTester(
290
+ dataProvider,
291
+ entityMetadata,
292
+ businessQuestion,
293
+ contextUser
294
+ );
295
+
296
+ const result = await tester.testQuery(query, 5);
297
+
298
+ if (result.success) {
299
+ console.log(`Query returned ${result.rowCount} rows in ${result.attempts} attempt(s)`);
300
+ console.log('Sample rows:', result.sampleRows);
301
+ console.log('Rendered SQL:', result.renderedSQL);
302
+ } else {
303
+ console.error(`Query failed after ${result.attempts} attempts:`, result.error);
304
+ }
305
+ ```
306
+
307
+ ---
308
+
309
+ ### QueryFixer
310
+
311
+ Fixes SQL errors using AI.
312
+
313
+ #### Constructor
314
+
315
+ ```typescript
316
+ new QueryFixer(
317
+ entityMetadata: EntityMetadataForPrompt[],
318
+ businessQuestion: BusinessQuestion,
319
+ contextUser: UserInfo
320
+ )
321
+ ```
322
+
323
+ **Parameters:**
324
+ - `entityMetadata` - Entity metadata for error context
325
+ - `businessQuestion` - Business question for error context
326
+ - `contextUser` - User context for server-side operations
327
+
328
+ #### Methods
329
+
330
+ ##### `fixQuery()`
331
+
332
+ Fixes a broken SQL query using AI analysis of the error message.
333
+
334
+ ```typescript
335
+ async fixQuery(
336
+ query: GeneratedQuery,
337
+ errorMessage: string
338
+ ): Promise<GeneratedQuery>
339
+ ```
340
+
341
+ **Parameters:**
342
+ - `query` - The broken query
343
+ - `errorMessage` - SQL error message from database
344
+
345
+ **Returns:** Fixed GeneratedQuery
346
+
347
+ **Example:**
348
+ ```typescript
349
+ import { QueryFixer } from '@memberjunction/query-gen';
350
+ import { UserInfo } from '@memberjunction/core';
351
+
352
+ const contextUser = new UserInfo();
353
+ const fixer = new QueryFixer(entityMetadata, businessQuestion, contextUser);
354
+
355
+ try {
356
+ const result = await dataProvider.ExecuteSQL(renderedSQL);
357
+ } catch (error) {
358
+ // Fix the query
359
+ const errorMsg = extractErrorMessage(error, 'SQL Execution');
360
+ const fixedQuery = await fixer.fixQuery(query, errorMsg);
361
+
362
+ // Try again with fixed query
363
+ const fixedSQL = renderTemplate(fixedQuery);
364
+ const result = await dataProvider.ExecuteSQL(fixedSQL);
365
+ }
366
+ ```
367
+
368
+ ---
369
+
370
+ ### QueryRefiner
371
+
372
+ Evaluates and refines queries iteratively using AI feedback.
373
+
374
+ #### Constructor
375
+
376
+ ```typescript
377
+ new QueryRefiner(
378
+ queryTester: QueryTester,
379
+ contextUser: UserInfo
380
+ )
381
+ ```
382
+
383
+ **Parameters:**
384
+ - `queryTester` - QueryTester instance for executing queries
385
+ - `contextUser` - User context for server-side operations
386
+
387
+ #### Methods
388
+
389
+ ##### `refineQuery()`
390
+
391
+ Evaluates and refines a query through iterative feedback loops.
392
+
393
+ ```typescript
394
+ async refineQuery(
395
+ query: GeneratedQuery,
396
+ businessQuestion: BusinessQuestion,
397
+ entityMetadata: EntityMetadataForPrompt[],
398
+ maxRefinements: number = 3
399
+ ): Promise<RefinedQuery>
400
+ ```
401
+
402
+ **Parameters:**
403
+ - `query` - The query to refine
404
+ - `businessQuestion` - Business question for evaluation context
405
+ - `entityMetadata` - Entity metadata for refinement context
406
+ - `maxRefinements` - Maximum refinement iterations (default: 3)
407
+
408
+ **Returns:** RefinedQuery with final query, test results, and evaluation
409
+
410
+ **Example:**
411
+ ```typescript
412
+ import { QueryRefiner } from '@memberjunction/query-gen';
413
+ import { UserInfo } from '@memberjunction/core';
414
+
415
+ const contextUser = new UserInfo();
416
+ const refiner = new QueryRefiner(queryTester, contextUser);
417
+
418
+ const refined = await refiner.refineQuery(
419
+ query,
420
+ businessQuestion,
421
+ entityMetadata,
422
+ 3 // max 3 refinement cycles
423
+ );
424
+
425
+ console.log(`Refined in ${refined.refinementCount} iteration(s)`);
426
+ console.log('Evaluation:', refined.evaluation);
427
+ // {
428
+ // answersQuestion: true,
429
+ // confidence: 0.95,
430
+ // reasoning: "Query correctly aggregates...",
431
+ // suggestions: [],
432
+ // needsRefinement: false
433
+ // }
434
+
435
+ console.log('Final query:', refined.query.sql);
436
+ console.log('Test result:', refined.testResult);
437
+ ```
438
+
439
+ ---
440
+
441
+ ### MetadataExporter
442
+
443
+ Exports validated queries to MemberJunction metadata format.
444
+
445
+ #### Constructor
446
+
447
+ ```typescript
448
+ new MetadataExporter()
449
+ ```
450
+
451
+ #### Methods
452
+
453
+ ##### `exportQueries()`
454
+
455
+ Exports queries to JSON metadata files.
456
+
457
+ ```typescript
458
+ async exportQueries(
459
+ validatedQueries: ValidatedQuery[],
460
+ outputDirectory: string
461
+ ): Promise<ExportResult>
462
+ ```
463
+
464
+ **Parameters:**
465
+ - `validatedQueries` - Array of validated queries to export
466
+ - `outputDirectory` - Directory path for output files
467
+
468
+ **Returns:** ExportResult with output path and query count
469
+
470
+ **Example:**
471
+ ```typescript
472
+ import { MetadataExporter } from '@memberjunction/query-gen';
473
+
474
+ const exporter = new MetadataExporter();
475
+ const result = await exporter.exportQueries(
476
+ validatedQueries,
477
+ './metadata/queries'
478
+ );
479
+
480
+ console.log(`Exported ${result.queryCount} queries to ${result.outputPath}`);
481
+ // Exported 15 queries to ./metadata/queries/queries-1234567890.json
482
+ ```
483
+
484
+ ---
485
+
486
+ ### QueryDatabaseWriter
487
+
488
+ Writes queries directly to the database.
489
+
490
+ #### Constructor
491
+
492
+ ```typescript
493
+ new QueryDatabaseWriter()
494
+ ```
495
+
496
+ #### Methods
497
+
498
+ ##### `writeQueriesToDatabase()`
499
+
500
+ Inserts queries into Queries, Query Fields, and Query Params tables.
501
+
502
+ ```typescript
503
+ async writeQueriesToDatabase(
504
+ validatedQueries: ValidatedQuery[],
505
+ contextUser: UserInfo
506
+ ): Promise<void>
507
+ ```
508
+
509
+ **Parameters:**
510
+ - `validatedQueries` - Array of validated queries to write
511
+ - `contextUser` - User context for entity operations
512
+
513
+ **Example:**
514
+ ```typescript
515
+ import { QueryDatabaseWriter } from '@memberjunction/query-gen';
516
+ import { UserInfo } from '@memberjunction/core';
517
+
518
+ const contextUser = new UserInfo();
519
+ const writer = new QueryDatabaseWriter();
520
+
521
+ await writer.writeQueriesToDatabase(validatedQueries, contextUser);
522
+ console.log(`Wrote ${validatedQueries.length} queries to database`);
523
+ ```
524
+
525
+ ---
526
+
527
+ ### EmbeddingService
528
+
529
+ Generates vector embeddings for similarity search.
530
+
531
+ #### Constructor
532
+
533
+ ```typescript
534
+ new EmbeddingService(modelName: string)
535
+ ```
536
+
537
+ **Parameters:**
538
+ - `modelName` - Embedding model name (default: 'text-embedding-3-small')
539
+
540
+ #### Methods
541
+
542
+ ##### `embedQuery()`
543
+
544
+ Embeds all fields of a query for similarity search.
545
+
546
+ ```typescript
547
+ async embedQuery(query: {
548
+ name: string;
549
+ userQuestion: string;
550
+ description: string;
551
+ technicalDescription: string;
552
+ }): Promise<{
553
+ name: number[];
554
+ userQuestion: number[];
555
+ description: number[];
556
+ technicalDescription: number[];
557
+ }>
558
+ ```
559
+
560
+ **Parameters:**
561
+ - `query` - Query object with fields to embed
562
+
563
+ **Returns:** Embeddings for each field
564
+
565
+ ##### `embedGoldenQueries()`
566
+
567
+ Embeds all golden queries for similarity comparison.
568
+
569
+ ```typescript
570
+ async embedGoldenQueries(
571
+ goldenQueries: GoldenQuery[]
572
+ ): Promise<Array<{
573
+ query: GoldenQuery;
574
+ embeddings: {
575
+ name: number[];
576
+ userQuestion: number[];
577
+ description: number[];
578
+ technicalDescription: number[];
579
+ };
580
+ }>>
581
+ ```
582
+
583
+ **Parameters:**
584
+ - `goldenQueries` - Array of golden queries to embed
585
+
586
+ **Returns:** Array of golden queries with their embeddings
587
+
588
+ **Example:**
589
+ ```typescript
590
+ import { EmbeddingService } from '@memberjunction/query-gen';
591
+
592
+ const embeddingService = new EmbeddingService('text-embedding-3-small');
593
+
594
+ // Embed a question
595
+ const questionEmbeddings = await embeddingService.embedQuery({
596
+ name: '',
597
+ userQuestion: "What are the top customers by revenue?",
598
+ description: "Identify high-value customers",
599
+ technicalDescription: "Sum order totals per customer"
600
+ });
601
+
602
+ // Embed golden queries
603
+ const goldenQueries = loadGoldenQueries(); // Load from database/files
604
+ const embeddedGolden = await embeddingService.embedGoldenQueries(goldenQueries);
605
+
606
+ console.log(`Embedded ${embeddedGolden.length} golden queries`);
607
+ ```
608
+
609
+ ---
610
+
611
+ ### SimilaritySearch
612
+
613
+ Finds similar queries using weighted cosine similarity.
614
+
615
+ #### Constructor
616
+
617
+ ```typescript
618
+ new SimilaritySearch(weights?: {
619
+ name: number;
620
+ userQuestion: number;
621
+ description: number;
622
+ technicalDescription: number;
623
+ })
624
+ ```
625
+
626
+ **Parameters:**
627
+ - `weights` - Optional custom similarity weights (defaults to: name=0.1, userQuestion=0.2, description=0.35, technicalDescription=0.35)
628
+
629
+ #### Methods
630
+
631
+ ##### `findSimilarQueries()`
632
+
633
+ Finds top-K most similar golden queries using weighted cosine similarity.
634
+
635
+ ```typescript
636
+ async findSimilarQueries(
637
+ queryEmbeddings: {
638
+ name: number[];
639
+ userQuestion: number[];
640
+ description: number[];
641
+ technicalDescription: number[];
642
+ },
643
+ goldenEmbeddings: Array<{
644
+ query: GoldenQuery;
645
+ embeddings: { ... };
646
+ }>,
647
+ topK: number = 5
648
+ ): Promise<SimilarQuery[]>
649
+ ```
650
+
651
+ **Parameters:**
652
+ - `queryEmbeddings` - Embeddings for the target query
653
+ - `goldenEmbeddings` - Array of golden queries with embeddings
654
+ - `topK` - Number of top results to return (default: 5)
655
+
656
+ **Returns:** Array of SimilarQuery objects sorted by similarity descending
657
+
658
+ **Example:**
659
+ ```typescript
660
+ import { SimilaritySearch } from '@memberjunction/query-gen';
661
+
662
+ const search = new SimilaritySearch();
663
+
664
+ const similar = await search.findSimilarQueries(
665
+ questionEmbeddings,
666
+ embeddedGolden,
667
+ 5 // top 5
668
+ );
669
+
670
+ console.log('Top 5 similar golden queries:');
671
+ for (const result of similar) {
672
+ console.log(`- ${result.query.name} (similarity: ${result.similarity.toFixed(2)})`);
673
+ console.log(` Field scores:`, result.fieldScores);
674
+ }
675
+
676
+ // Output:
677
+ // Top 5 similar golden queries:
678
+ // - Top Customers By Order Count (similarity: 0.92)
679
+ // Field scores: { nameSim: 0.85, userQuestionSim: 0.94, descSim: 0.90, techDescSim: 0.95 }
680
+ // - Revenue By Customer Segment (similarity: 0.87)
681
+ // Field scores: { nameSim: 0.72, userQuestionSim: 0.88, descSim: 0.89, techDescSim: 0.91 }
682
+ // ...
683
+ ```
684
+
685
+ ---
686
+
687
+ ## Usage Patterns
688
+
689
+ ### Complete Query Generation Workflow
690
+
691
+ ```typescript
692
+ import {
693
+ EntityGrouper,
694
+ QuestionGenerator,
695
+ QueryWriter,
696
+ QueryTester,
697
+ QueryRefiner,
698
+ MetadataExporter,
699
+ EmbeddingService,
700
+ SimilaritySearch,
701
+ formatEntityMetadataForPrompt
702
+ } from '@memberjunction/query-gen';
703
+ import { Metadata, UserInfo } from '@memberjunction/core';
704
+
705
+ async function generateQueriesForEntity(
706
+ entityName: string,
707
+ contextUser: UserInfo
708
+ ): Promise<void> {
709
+ // 1. Load entity metadata
710
+ const md = new Metadata();
711
+ const entity = md.Entities.find(e => e.Name === entityName);
712
+ if (!entity) {
713
+ throw new Error(`Entity not found: ${entityName}`);
714
+ }
715
+
716
+ // 2. Create entity group
717
+ const grouper = new EntityGrouper();
718
+ const groups = await grouper.generateEntityGroups([entity], 1, 1);
719
+ const entityGroup = groups[0];
720
+
721
+ // 3. Generate business questions
722
+ const questionGen = new QuestionGenerator(contextUser);
723
+ const questions = await questionGen.generateQuestions(entityGroup);
724
+
725
+ // 4. Process each question
726
+ for (const question of questions) {
727
+ console.log(`\nGenerating query for: ${question.userQuestion}`);
728
+
729
+ // 5. Embed question for similarity search
730
+ const embeddingService = new EmbeddingService('text-embedding-3-small');
731
+ const questionEmbeddings = await embeddingService.embedQuery({
732
+ name: '',
733
+ userQuestion: question.userQuestion,
734
+ description: question.description,
735
+ technicalDescription: question.technicalDescription
736
+ });
737
+
738
+ // 6. Find similar golden queries
739
+ const goldenQueries = await loadGoldenQueries(); // Your implementation
740
+ const embeddedGolden = await embeddingService.embedGoldenQueries(goldenQueries);
741
+
742
+ const similaritySearch = new SimilaritySearch();
743
+ const fewShotResults = await similaritySearch.findSimilarQueries(
744
+ questionEmbeddings,
745
+ embeddedGolden,
746
+ 5
747
+ );
748
+ const fewShotExamples = fewShotResults.map(r => r.query);
749
+
750
+ // 7. Generate SQL query
751
+ const entityMetadata = [entity].map(e =>
752
+ formatEntityMetadataForPrompt(e, [entity])
753
+ );
754
+ const queryWriter = new QueryWriter(contextUser);
755
+ const query = await queryWriter.generateQuery(
756
+ question,
757
+ entityMetadata,
758
+ fewShotExamples
759
+ );
760
+
761
+ console.log('Generated SQL:', query.sql.substring(0, 100) + '...');
762
+
763
+ // 8. Test query
764
+ const dataProvider = Metadata.Provider.DatabaseConnection;
765
+ const tester = new QueryTester(
766
+ dataProvider,
767
+ entityMetadata,
768
+ question,
769
+ contextUser
770
+ );
771
+ const testResult = await tester.testQuery(query, 5);
772
+
773
+ if (!testResult.success) {
774
+ console.error('Query test failed:', testResult.error);
775
+ continue;
776
+ }
777
+
778
+ console.log(`Query test passed: ${testResult.rowCount} rows returned`);
779
+
780
+ // 9. Refine query
781
+ const refiner = new QueryRefiner(tester, contextUser);
782
+ const refined = await refiner.refineQuery(query, question, entityMetadata, 3);
783
+
784
+ console.log(`Query refined in ${refined.refinementCount} iteration(s)`);
785
+ console.log('Evaluation confidence:', refined.evaluation.confidence);
786
+
787
+ // 10. Export query
788
+ const exporter = new MetadataExporter();
789
+ await exporter.exportQueries(
790
+ [{
791
+ businessQuestion: question,
792
+ query: refined.query,
793
+ testResult: refined.testResult,
794
+ evaluation: refined.evaluation,
795
+ entityGroup
796
+ }],
797
+ './metadata/queries'
798
+ );
799
+
800
+ console.log('Query exported successfully!');
801
+ }
802
+ }
803
+
804
+ // Usage
805
+ const contextUser = new UserInfo();
806
+ contextUser.Email = 'system@example.com';
807
+ contextUser.Name = 'System';
808
+
809
+ await generateQueriesForEntity('Customers', contextUser);
810
+ ```
811
+
812
+ ### Batch Query Generation
813
+
814
+ ```typescript
815
+ async function generateQueriesForMultipleEntities(
816
+ entityNames: string[],
817
+ contextUser: UserInfo
818
+ ): Promise<ValidatedQuery[]> {
819
+ const allValidatedQueries: ValidatedQuery[] = [];
820
+
821
+ for (const entityName of entityNames) {
822
+ console.log(`\nProcessing ${entityName}...`);
823
+
824
+ // Load entity
825
+ const md = new Metadata();
826
+ const entity = md.Entities.find(e => e.Name === entityName);
827
+ if (!entity) {
828
+ console.error(`Entity not found: ${entityName}`);
829
+ continue;
830
+ }
831
+
832
+ // Generate queries (using workflow from previous example)
833
+ const queries = await generateQueriesForEntity(entityName, contextUser);
834
+ allValidatedQueries.push(...queries);
835
+ }
836
+
837
+ // Export all queries at once
838
+ const exporter = new MetadataExporter();
839
+ await exporter.exportQueries(allValidatedQueries, './metadata/queries');
840
+
841
+ console.log(`\nTotal queries generated: ${allValidatedQueries.length}`);
842
+ return allValidatedQueries;
843
+ }
844
+ ```
845
+
846
+ ### Query Validation
847
+
848
+ ```typescript
849
+ async function validateQueryMetadataFile(
850
+ filePath: string,
851
+ contextUser: UserInfo
852
+ ): Promise<{ passed: number; failed: number; errors: string[] }> {
853
+ const results = { passed: 0, failed: 0, errors: [] };
854
+
855
+ // Load queries from file
856
+ const content = fs.readFileSync(filePath, 'utf-8');
857
+ const data = JSON.parse(content);
858
+ const queries = Array.isArray(data) ? data : [data];
859
+
860
+ // Validate each query
861
+ for (const queryRecord of queries) {
862
+ const query = convertMetadataToGeneratedQuery(queryRecord);
863
+
864
+ // Create dummy context for testing
865
+ const dummyQuestion = {
866
+ userQuestion: queryRecord.fields.UserQuestion || 'Test query',
867
+ description: queryRecord.fields.Description || '',
868
+ technicalDescription: queryRecord.fields.TechnicalDescription || '',
869
+ complexity: 'medium' as const,
870
+ requiresAggregation: false,
871
+ requiresJoins: false,
872
+ entities: []
873
+ };
874
+
875
+ const dataProvider = Metadata.Provider.DatabaseConnection;
876
+ const tester = new QueryTester(dataProvider, [], dummyQuestion, contextUser);
877
+
878
+ try {
879
+ const testResult = await tester.testQuery(query, 1);
880
+
881
+ if (testResult.success) {
882
+ results.passed++;
883
+ console.log(`✓ ${queryRecord.fields.Name}`);
884
+ } else {
885
+ results.failed++;
886
+ results.errors.push(`${queryRecord.fields.Name}: ${testResult.error}`);
887
+ console.error(`✗ ${queryRecord.fields.Name}: ${testResult.error}`);
888
+ }
889
+ } catch (error) {
890
+ results.failed++;
891
+ const errorMsg = extractErrorMessage(error, 'Validation');
892
+ results.errors.push(`${queryRecord.fields.Name}: ${errorMsg}`);
893
+ console.error(`✗ ${queryRecord.fields.Name}: ${errorMsg}`);
894
+ }
895
+ }
896
+
897
+ return results;
898
+ }
899
+ ```
900
+
901
+ ---
902
+
903
+ ## TypeScript Types
904
+
905
+ ### Core Types
906
+
907
+ ```typescript
908
+ import type {
909
+ EntityGroup,
910
+ RelationshipInfo,
911
+ EntityMetadataForPrompt,
912
+ BusinessQuestion,
913
+ GeneratedQuery,
914
+ QueryParameter,
915
+ QueryOutputField,
916
+ GoldenQuery,
917
+ SimilarQuery,
918
+ QueryTestResult,
919
+ QueryEvaluation,
920
+ RefinedQuery,
921
+ ValidatedQuery,
922
+ QueryMetadataRecord
923
+ } from '@memberjunction/query-gen';
924
+ ```
925
+
926
+ ### Type Definitions
927
+
928
+ See [../src/data/schema.ts](../src/data/schema.ts) for complete type definitions.
929
+
930
+ ---
931
+
932
+ ## Configuration
933
+
934
+ ### QueryGenConfig Type
935
+
936
+ ```typescript
937
+ interface QueryGenConfig {
938
+ // Entity Filtering
939
+ includeEntities: string[];
940
+ excludeEntities: string[];
941
+ excludeSchemas: string[];
942
+
943
+ // Entity Grouping
944
+ maxEntitiesPerGroup: number;
945
+ minEntitiesPerGroup: number;
946
+ questionsPerGroup: number;
947
+ entityGroupStrategy: 'breadth' | 'depth';
948
+
949
+ // AI Configuration
950
+ modelOverride?: string;
951
+ vendorOverride?: string;
952
+ embeddingModel: string;
953
+
954
+ // Iteration Limits
955
+ maxRefinementIterations: number;
956
+ maxFixingIterations: number;
957
+
958
+ // Few-Shot Learning
959
+ topSimilarQueries: number;
960
+ similarityThreshold: number;
961
+
962
+ // Similarity Weighting
963
+ similarityWeights: {
964
+ name: number;
965
+ userQuestion: number;
966
+ description: number;
967
+ technicalDescription: number;
968
+ };
969
+
970
+ // Output Configuration
971
+ outputMode: 'metadata' | 'database' | 'both';
972
+ outputDirectory: string;
973
+
974
+ // Performance
975
+ parallelGenerations: number;
976
+ enableCaching: boolean;
977
+
978
+ // Validation
979
+ testWithSampleData: boolean;
980
+ requireMinRows: number;
981
+ maxRefinementRows: number;
982
+
983
+ // Verbose Logging
984
+ verbose: boolean;
985
+ }
986
+ ```
987
+
988
+ ### Loading Configuration
989
+
990
+ ```typescript
991
+ import { loadConfig } from '@memberjunction/query-gen';
992
+
993
+ // Load from mj.config.cjs with CLI overrides
994
+ const config = loadConfig({
995
+ entities: ['Customers', 'Orders'],
996
+ maxEntities: 2,
997
+ verbose: true
998
+ });
999
+
1000
+ // Use configuration
1001
+ console.log(config.maxEntitiesPerGroup); // 2
1002
+ console.log(config.verbose); // true
1003
+ ```
1004
+
1005
+ ---
1006
+
1007
+ ## Examples
1008
+
1009
+ See the [examples](../examples/) directory for complete working examples:
1010
+
1011
+ - [basic-usage.ts](../examples/basic-usage.ts) - Basic query generation workflow
1012
+ - [advanced-usage.ts](../examples/advanced-usage.ts) - Advanced patterns and customization
1013
+
1014
+ ---
1015
+
1016
+ ## Error Handling
1017
+
1018
+ All QueryGen functions use proper error handling:
1019
+
1020
+ ```typescript
1021
+ import { extractErrorMessage } from '@memberjunction/query-gen';
1022
+
1023
+ try {
1024
+ const queries = await generateQueries();
1025
+ } catch (error: unknown) {
1026
+ const errorMsg = extractErrorMessage(error, 'Query Generation');
1027
+ console.error('Failed:', errorMsg);
1028
+ }
1029
+ ```
1030
+
1031
+ Never use `any` for error types - always use `unknown` and extract messages safely.
1032
+
1033
+ ---
1034
+
1035
+ ## Support
1036
+
1037
+ For issues or questions:
1038
+ - GitHub Issues: https://github.com/MemberJunction/MJ/issues
1039
+ - Documentation: https://docs.memberjunction.com
1040
+ - Community: https://community.memberjunction.com