@memberjunction/query-gen 2.126.1 → 2.127.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 (58) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +18 -0
  3. package/dist/cli/commands/export.js +0 -1
  4. package/dist/cli/commands/export.js.map +1 -1
  5. package/dist/cli/commands/generate.d.ts.map +1 -1
  6. package/dist/cli/commands/generate.js +1 -1
  7. package/dist/cli/commands/generate.js.map +1 -1
  8. package/dist/cli/commands/validate.js +1 -0
  9. package/dist/cli/commands/validate.js.map +1 -1
  10. package/dist/cli/config.d.ts +1 -0
  11. package/dist/cli/config.d.ts.map +1 -1
  12. package/dist/cli/config.js +1 -0
  13. package/dist/cli/config.js.map +1 -1
  14. package/dist/core/EntityGrouper.d.ts +2 -3
  15. package/dist/core/EntityGrouper.d.ts.map +1 -1
  16. package/dist/core/EntityGrouper.js +45 -4
  17. package/dist/core/EntityGrouper.js.map +1 -1
  18. package/dist/core/MetadataExporter.d.ts +13 -3
  19. package/dist/core/MetadataExporter.d.ts.map +1 -1
  20. package/dist/core/MetadataExporter.js +41 -13
  21. package/dist/core/MetadataExporter.js.map +1 -1
  22. package/dist/core/QueryDatabaseWriter.d.ts.map +1 -1
  23. package/dist/core/QueryDatabaseWriter.js +1 -2
  24. package/dist/core/QueryDatabaseWriter.js.map +1 -1
  25. package/dist/core/QueryFixer.d.ts.map +1 -1
  26. package/dist/core/QueryFixer.js +3 -1
  27. package/dist/core/QueryFixer.js.map +1 -1
  28. package/dist/core/QueryRefiner.d.ts.map +1 -1
  29. package/dist/core/QueryRefiner.js +1 -0
  30. package/dist/core/QueryRefiner.js.map +1 -1
  31. package/dist/core/QuestionGenerator.d.ts +4 -5
  32. package/dist/core/QuestionGenerator.d.ts.map +1 -1
  33. package/dist/core/QuestionGenerator.js +38 -24
  34. package/dist/core/QuestionGenerator.js.map +1 -1
  35. package/dist/data/schema.d.ts +1 -1
  36. package/dist/data/schema.d.ts.map +1 -1
  37. package/dist/index.d.ts +0 -1
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +1 -3
  40. package/dist/index.js.map +1 -1
  41. package/package.json +10 -10
  42. package/src/cli/commands/export.ts +0 -1
  43. package/src/cli/commands/generate.ts +1 -2
  44. package/src/cli/commands/validate.ts +1 -0
  45. package/src/cli/config.ts +3 -0
  46. package/src/core/EntityGrouper.ts +55 -4
  47. package/src/core/MetadataExporter.ts +46 -14
  48. package/src/core/QueryDatabaseWriter.ts +1 -2
  49. package/src/core/QueryFixer.ts +4 -1
  50. package/src/core/QueryRefiner.ts +1 -0
  51. package/src/core/QuestionGenerator.ts +51 -37
  52. package/src/data/schema.ts +1 -1
  53. package/src/index.ts +0 -1
  54. package/dist/utils/query-helpers.d.ts +0 -23
  55. package/dist/utils/query-helpers.d.ts.map +0 -1
  56. package/dist/utils/query-helpers.js +0 -34
  57. package/dist/utils/query-helpers.js.map +0 -1
  58. package/src/utils/query-helpers.ts +0 -32
@@ -125,35 +125,61 @@ export class QuestionGenerator {
125
125
  /**
126
126
  * Validate and filter business questions
127
127
  * Removes low-quality or unanswerable questions
128
+ * Corrects entity name casing to match exact entity group names
128
129
  *
129
130
  * @param questions - Raw questions from AI
130
131
  * @param entityGroup - Entity group for validation context
131
- * @returns Filtered array of valid questions
132
+ * @returns Filtered array of valid questions with corrected entity names
132
133
  */
133
134
  private validateQuestions(
134
135
  questions: BusinessQuestion[],
135
136
  entityGroup: EntityGroup
136
137
  ): BusinessQuestion[] {
137
- const entityNames = new Set(entityGroup.entities.map((e) => e.Name));
138
-
139
- return questions.filter((q) => {
140
- // Must have required fields
141
- if (!this.hasRequiredFields(q)) {
142
- return false;
143
- }
144
-
145
- // Must reference entities in the group
146
- if (!this.referencesGroupEntities(q, entityNames)) {
147
- return false;
148
- }
149
-
150
- // Must not be overly generic
151
- if (this.isTooGeneric(q)) {
152
- return false;
138
+ // Create case-insensitive lookup map: lowercase -> exact entity name
139
+ // Also map BaseView names to correct entity names (in case LLM returns view names)
140
+ const entityNameLookup = new Map<string, string>();
141
+ for (const entity of entityGroup.entities) {
142
+ // Map entity name (case-insensitive)
143
+ entityNameLookup.set(entity.Name.toLowerCase(), entity.Name);
144
+
145
+ // Map base view name to entity name (case-insensitive) as backup
146
+ if (entity.BaseView) {
147
+ entityNameLookup.set(entity.BaseView.toLowerCase(), entity.Name);
153
148
  }
149
+ }
154
150
 
155
- return true;
156
- });
151
+ return questions
152
+ .filter((q) => {
153
+ // Must have required fields
154
+ if (!this.hasRequiredFields(q)) {
155
+ if (this.config.verbose) {
156
+ LogStatus(` ❌ Filtered: Missing required fields - "${q.userQuestion?.substring(0, 60)}..."`);
157
+ }
158
+ return false;
159
+ }
160
+
161
+ // Must reference entities in the group (by name or base view)
162
+ if (!this.referencesGroupEntities(q, entityNameLookup)) {
163
+ if (this.config.verbose) {
164
+ LogStatus(` ❌ Filtered: Doesn't reference group entities - "${q.userQuestion.substring(0, 60)}..." (references: ${q.entities.join(', ')})`);
165
+ }
166
+ return false;
167
+ }
168
+
169
+ return true;
170
+ })
171
+ .map((q) => {
172
+ // Correct entity name casing to match exact entity group names
173
+ const correctedEntities = q.entities.map((entityName) => {
174
+ const exactName = entityNameLookup.get(entityName.toLowerCase());
175
+ return exactName || entityName; // Use exact name if found, otherwise keep original
176
+ });
177
+
178
+ return {
179
+ ...q,
180
+ entities: correctedEntities
181
+ };
182
+ });
157
183
  }
158
184
 
159
185
  /**
@@ -172,28 +198,16 @@ export class QuestionGenerator {
172
198
 
173
199
  /**
174
200
  * Check if question references entities in the group
201
+ * Uses case-insensitive matching to handle minor casing differences
202
+ * Also accepts base view names as valid references
175
203
  */
176
204
  private referencesGroupEntities(
177
205
  question: BusinessQuestion,
178
- entityNames: Set<string>
206
+ entityNameLookup: Map<string, string>
179
207
  ): boolean {
180
- return question.entities.some((entityName) => entityNames.has(entityName));
181
- }
182
-
183
- /**
184
- * Check if question is too generic to be useful
185
- */
186
- private isTooGeneric(question: BusinessQuestion): boolean {
187
- const genericPatterns = [
188
- /show\s+me\s+all/i,
189
- /list\s+all/i,
190
- /get\s+all/i,
191
- /display\s+all/i,
192
- /^what\s+is/i,
193
- ];
194
-
195
- return genericPatterns.some((pattern) =>
196
- pattern.test(question.userQuestion)
208
+ return question.entities.some((entityName) =>
209
+ entityNameLookup.has(entityName.toLowerCase())
197
210
  );
198
211
  }
212
+
199
213
  }
@@ -81,6 +81,7 @@ export interface BusinessQuestion {
81
81
  * Note: selectClause removed - QueryEntity automatically creates QueryFieldEntity records from SQL
82
82
  */
83
83
  export interface GeneratedQuery {
84
+ queryName: string;
84
85
  sql: string;
85
86
  parameters: QueryParameter[];
86
87
  }
@@ -227,7 +228,6 @@ export interface QueryMetadataRecord {
227
228
  Description: string;
228
229
  TechnicalDescription: string;
229
230
  SQL: string;
230
- OriginalSQL: string;
231
231
  UsesTemplate: boolean;
232
232
  Status: string;
233
233
  };
package/src/index.ts CHANGED
@@ -46,4 +46,3 @@ export {
46
46
  hasRelationships,
47
47
  getRelationshipCount
48
48
  } from './utils/entity-helpers';
49
- export { generateQueryName } from './utils/query-helpers';
@@ -1,23 +0,0 @@
1
- /**
2
- * Query generation utility functions
3
- */
4
- import { BusinessQuestion } from '../data/schema';
5
- /**
6
- * Generate a query name from a business question
7
- *
8
- * Removes question marks, filters short words, capitalizes each word,
9
- * and limits to 5 words for a concise query name.
10
- *
11
- * @param question - The business question to convert
12
- * @returns A formatted query name
13
- *
14
- * @example
15
- * generateQueryName({ userQuestion: "What are the top customers by revenue?" })
16
- * // Returns: "What Are Top Customers Revenue"
17
- *
18
- * @example
19
- * generateQueryName({ userQuestion: "Show me all active users" })
20
- * // Returns: "Show All Active Users"
21
- */
22
- export declare function generateQueryName(question: BusinessQuestion): string;
23
- //# sourceMappingURL=query-helpers.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"query-helpers.d.ts","sourceRoot":"","sources":["../../src/utils/query-helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAQpE"}
@@ -1,34 +0,0 @@
1
- "use strict";
2
- /**
3
- * Query generation utility functions
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.generateQueryName = void 0;
7
- /**
8
- * Generate a query name from a business question
9
- *
10
- * Removes question marks, filters short words, capitalizes each word,
11
- * and limits to 5 words for a concise query name.
12
- *
13
- * @param question - The business question to convert
14
- * @returns A formatted query name
15
- *
16
- * @example
17
- * generateQueryName({ userQuestion: "What are the top customers by revenue?" })
18
- * // Returns: "What Are Top Customers Revenue"
19
- *
20
- * @example
21
- * generateQueryName({ userQuestion: "Show me all active users" })
22
- * // Returns: "Show All Active Users"
23
- */
24
- function generateQueryName(question) {
25
- return question.userQuestion
26
- .replace(/\?/g, '')
27
- .split(' ')
28
- .filter(word => word.length > 2)
29
- .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
30
- .slice(0, 5)
31
- .join(' ');
32
- }
33
- exports.generateQueryName = generateQueryName;
34
- //# sourceMappingURL=query-helpers.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"query-helpers.js","sourceRoot":"","sources":["../../src/utils/query-helpers.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAIH;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,iBAAiB,CAAC,QAA0B;IAC1D,OAAO,QAAQ,CAAC,YAAY;SACzB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;SAClB,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;SAC/B,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;SACvE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AARD,8CAQC"}
@@ -1,32 +0,0 @@
1
- /**
2
- * Query generation utility functions
3
- */
4
-
5
- import { BusinessQuestion } from '../data/schema';
6
-
7
- /**
8
- * Generate a query name from a business question
9
- *
10
- * Removes question marks, filters short words, capitalizes each word,
11
- * and limits to 5 words for a concise query name.
12
- *
13
- * @param question - The business question to convert
14
- * @returns A formatted query name
15
- *
16
- * @example
17
- * generateQueryName({ userQuestion: "What are the top customers by revenue?" })
18
- * // Returns: "What Are Top Customers Revenue"
19
- *
20
- * @example
21
- * generateQueryName({ userQuestion: "Show me all active users" })
22
- * // Returns: "Show All Active Users"
23
- */
24
- export function generateQueryName(question: BusinessQuestion): string {
25
- return question.userQuestion
26
- .replace(/\?/g, '')
27
- .split(' ')
28
- .filter(word => word.length > 2)
29
- .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
30
- .slice(0, 5)
31
- .join(' ');
32
- }