@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +18 -0
- package/dist/cli/commands/export.js +0 -1
- package/dist/cli/commands/export.js.map +1 -1
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +1 -1
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/validate.js +1 -0
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/config.d.ts +1 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +1 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/core/EntityGrouper.d.ts +2 -3
- package/dist/core/EntityGrouper.d.ts.map +1 -1
- package/dist/core/EntityGrouper.js +45 -4
- package/dist/core/EntityGrouper.js.map +1 -1
- package/dist/core/MetadataExporter.d.ts +13 -3
- package/dist/core/MetadataExporter.d.ts.map +1 -1
- package/dist/core/MetadataExporter.js +41 -13
- package/dist/core/MetadataExporter.js.map +1 -1
- package/dist/core/QueryDatabaseWriter.d.ts.map +1 -1
- package/dist/core/QueryDatabaseWriter.js +1 -2
- package/dist/core/QueryDatabaseWriter.js.map +1 -1
- package/dist/core/QueryFixer.d.ts.map +1 -1
- package/dist/core/QueryFixer.js +3 -1
- package/dist/core/QueryFixer.js.map +1 -1
- package/dist/core/QueryRefiner.d.ts.map +1 -1
- package/dist/core/QueryRefiner.js +1 -0
- package/dist/core/QueryRefiner.js.map +1 -1
- package/dist/core/QuestionGenerator.d.ts +4 -5
- package/dist/core/QuestionGenerator.d.ts.map +1 -1
- package/dist/core/QuestionGenerator.js +38 -24
- package/dist/core/QuestionGenerator.js.map +1 -1
- package/dist/data/schema.d.ts +1 -1
- package/dist/data/schema.d.ts.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
- package/src/cli/commands/export.ts +0 -1
- package/src/cli/commands/generate.ts +1 -2
- package/src/cli/commands/validate.ts +1 -0
- package/src/cli/config.ts +3 -0
- package/src/core/EntityGrouper.ts +55 -4
- package/src/core/MetadataExporter.ts +46 -14
- package/src/core/QueryDatabaseWriter.ts +1 -2
- package/src/core/QueryFixer.ts +4 -1
- package/src/core/QueryRefiner.ts +1 -0
- package/src/core/QuestionGenerator.ts +51 -37
- package/src/data/schema.ts +1 -1
- package/src/index.ts +0 -1
- package/dist/utils/query-helpers.d.ts +0 -23
- package/dist/utils/query-helpers.d.ts.map +0 -1
- package/dist/utils/query-helpers.js +0 -34
- package/dist/utils/query-helpers.js.map +0 -1
- 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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
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
|
-
|
|
206
|
+
entityNameLookup: Map<string, string>
|
|
179
207
|
): boolean {
|
|
180
|
-
return question.entities.some((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
|
}
|
package/src/data/schema.ts
CHANGED
|
@@ -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
|
@@ -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
|
-
}
|