simple-graph-query 2.1.0 → 2.1.3
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/dist/index.d.ts +1 -0
- package/dist/simple-graph-query.bundle.js +102 -36
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare class SimpleGraphQueryEvaluator {
|
|
|
11
11
|
datum: IDataInstance;
|
|
12
12
|
forgeListener: ForgeListenerImpl;
|
|
13
13
|
walker: ParseTreeWalker;
|
|
14
|
+
private parseTreeCache;
|
|
14
15
|
constructor(datum: IDataInstance);
|
|
15
16
|
getExpressionParseTree(forgeExpr: string): import("./forge-antlr/ForgeParser").ParseExprContext;
|
|
16
17
|
evaluateExpression(forgeExpr: string): EvaluationResult;
|
|
@@ -48542,20 +48542,21 @@ function transitiveClosure(pairs) {
|
|
|
48542
48542
|
}
|
|
48543
48543
|
graph.get(from).add(to);
|
|
48544
48544
|
}
|
|
48545
|
-
//
|
|
48545
|
+
// Use more efficient BFS with index-based queue to avoid O(n) shift() operations
|
|
48546
48546
|
// NOTE: we use Set<string> instead of Set<[SingleValue, SingleValue]> since
|
|
48547
48547
|
// TS would compute equality over the object's reference instead of the value
|
|
48548
48548
|
// when the value is an array
|
|
48549
|
-
const
|
|
48549
|
+
const transitiveClosureSet = new Set();
|
|
48550
48550
|
for (const start of graph.keys()) {
|
|
48551
48551
|
const visited = new Set();
|
|
48552
48552
|
const queue = [...(graph.get(start) ?? [])];
|
|
48553
|
-
|
|
48554
|
-
|
|
48553
|
+
let queueIndex = 0; // Use index instead of shift() for O(1) access
|
|
48554
|
+
while (queueIndex < queue.length) {
|
|
48555
|
+
const current = queue[queueIndex++];
|
|
48555
48556
|
if (visited.has(current))
|
|
48556
48557
|
continue;
|
|
48557
48558
|
visited.add(current);
|
|
48558
|
-
|
|
48559
|
+
transitiveClosureSet.add(JSON.stringify([start, current]));
|
|
48559
48560
|
const neighbors = graph.get(current);
|
|
48560
48561
|
if (neighbors) {
|
|
48561
48562
|
for (const neighbor of neighbors) {
|
|
@@ -48567,7 +48568,7 @@ function transitiveClosure(pairs) {
|
|
|
48567
48568
|
}
|
|
48568
48569
|
}
|
|
48569
48570
|
// convert the result back to a Tuple[] and return
|
|
48570
|
-
return Array.from(
|
|
48571
|
+
return Array.from(transitiveClosureSet).map((pair) => JSON.parse(pair));
|
|
48571
48572
|
}
|
|
48572
48573
|
function dotJoin(left, right) {
|
|
48573
48574
|
const leftExpr = isSingleValue(left) ? [[left]] : left;
|
|
@@ -48685,11 +48686,23 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
|
|
|
48685
48686
|
}
|
|
48686
48687
|
// helper function to get the label as string (used for @: and @str: operators)
|
|
48687
48688
|
getLabelAsString(value) {
|
|
48689
|
+
// Optimization: for primitive types, convert directly
|
|
48690
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
48691
|
+
return String(value);
|
|
48692
|
+
}
|
|
48688
48693
|
const label = this.getLabelForValue(value);
|
|
48689
48694
|
return String(label);
|
|
48690
48695
|
}
|
|
48691
48696
|
// helper function to get the label as boolean (used for @bool: operator)
|
|
48692
48697
|
getLabelAsBoolean(value) {
|
|
48698
|
+
// Optimization: for boolean values, return directly
|
|
48699
|
+
if (typeof value === "boolean") {
|
|
48700
|
+
return value;
|
|
48701
|
+
}
|
|
48702
|
+
// Optimization: for number values, apply rule directly
|
|
48703
|
+
if (typeof value === "number") {
|
|
48704
|
+
return value !== 0;
|
|
48705
|
+
}
|
|
48693
48706
|
const label = this.getLabelForValue(value);
|
|
48694
48707
|
const labelStr = String(label).toLowerCase();
|
|
48695
48708
|
// Convert string representations to boolean
|
|
@@ -48707,6 +48720,10 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
|
|
|
48707
48720
|
}
|
|
48708
48721
|
// helper function to get the label as number (used for @num: operator)
|
|
48709
48722
|
getLabelAsNumber(value) {
|
|
48723
|
+
// Optimization: if value is already a number, return it directly
|
|
48724
|
+
if (typeof value === "number") {
|
|
48725
|
+
return value;
|
|
48726
|
+
}
|
|
48710
48727
|
const label = this.getLabelForValue(value);
|
|
48711
48728
|
const labelNum = Number(label);
|
|
48712
48729
|
if (isNaN(labelNum)) {
|
|
@@ -48725,9 +48742,16 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
|
|
|
48725
48742
|
getIden() {
|
|
48726
48743
|
const instanceTypes = this.instanceData.getTypes();
|
|
48727
48744
|
const result = [];
|
|
48745
|
+
// Track unique atom IDs to avoid duplicates
|
|
48746
|
+
const seenAtomIds = new Set();
|
|
48728
48747
|
for (const t of instanceTypes) {
|
|
48729
48748
|
const typeAtoms = t.atoms;
|
|
48730
48749
|
typeAtoms.forEach((atom) => {
|
|
48750
|
+
// Skip if we've already seen this atom ID
|
|
48751
|
+
if (seenAtomIds.has(atom.id)) {
|
|
48752
|
+
return;
|
|
48753
|
+
}
|
|
48754
|
+
seenAtomIds.add(atom.id);
|
|
48731
48755
|
let value = atom.id;
|
|
48732
48756
|
// do some type conversions so we don't return a string if the value
|
|
48733
48757
|
// is a number or boolean
|
|
@@ -48884,6 +48908,12 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
|
|
|
48884
48908
|
const result = [];
|
|
48885
48909
|
let foundTrue = false;
|
|
48886
48910
|
let foundFalse = false;
|
|
48911
|
+
// Optimize: create environment once and reuse it
|
|
48912
|
+
const quantDeclEnv = {
|
|
48913
|
+
env: {},
|
|
48914
|
+
type: "quantDecl",
|
|
48915
|
+
};
|
|
48916
|
+
this.environmentStack.push(quantDeclEnv);
|
|
48887
48917
|
for (let i = 0; i < product.length; i++) {
|
|
48888
48918
|
const tuple = product[i];
|
|
48889
48919
|
if (isDisjoint) {
|
|
@@ -48901,16 +48931,10 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
|
|
|
48901
48931
|
continue;
|
|
48902
48932
|
}
|
|
48903
48933
|
}
|
|
48904
|
-
|
|
48905
|
-
env: {},
|
|
48906
|
-
type: "quantDecl",
|
|
48907
|
-
};
|
|
48934
|
+
// Update environment values in place
|
|
48908
48935
|
for (let j = 0; j < varNames.length; j++) {
|
|
48909
|
-
|
|
48910
|
-
const varValue = tuple[j];
|
|
48911
|
-
quantDeclEnv.env[varName] = varValue;
|
|
48936
|
+
quantDeclEnv.env[varNames[j]] = tuple[j];
|
|
48912
48937
|
}
|
|
48913
|
-
this.environmentStack.push(quantDeclEnv);
|
|
48914
48938
|
// now, we want to evaluate the barExpr
|
|
48915
48939
|
const barExprValue = this.visit(barExpr);
|
|
48916
48940
|
if (!isBoolean(barExprValue)) {
|
|
@@ -48923,14 +48947,15 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
|
|
|
48923
48947
|
else {
|
|
48924
48948
|
foundFalse = true;
|
|
48925
48949
|
}
|
|
48926
|
-
this.environmentStack.pop();
|
|
48927
48950
|
// short-circuit if possible
|
|
48928
48951
|
if (ctx.quant().ALL_TOK() && foundFalse) {
|
|
48952
|
+
this.environmentStack.pop();
|
|
48929
48953
|
const value = false;
|
|
48930
48954
|
this.cacheResult(ctx, freeVarsKey, value);
|
|
48931
48955
|
return value;
|
|
48932
48956
|
}
|
|
48933
48957
|
if (ctx.quant().NO_TOK() && foundTrue) {
|
|
48958
|
+
this.environmentStack.pop();
|
|
48934
48959
|
const value = false;
|
|
48935
48960
|
this.cacheResult(ctx, freeVarsKey, value);
|
|
48936
48961
|
return value;
|
|
@@ -48938,22 +48963,26 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
|
|
|
48938
48963
|
if (ctx.quant().mult()) {
|
|
48939
48964
|
const multExpr = ctx.quant().mult();
|
|
48940
48965
|
if (multExpr.LONE_TOK() && result.length > 1) {
|
|
48966
|
+
this.environmentStack.pop();
|
|
48941
48967
|
const value = false;
|
|
48942
48968
|
this.cacheResult(ctx, freeVarsKey, value);
|
|
48943
48969
|
return value;
|
|
48944
48970
|
}
|
|
48945
48971
|
if (multExpr.SOME_TOK() && foundTrue) {
|
|
48972
|
+
this.environmentStack.pop();
|
|
48946
48973
|
const value = true;
|
|
48947
48974
|
this.cacheResult(ctx, freeVarsKey, value);
|
|
48948
48975
|
return value;
|
|
48949
48976
|
}
|
|
48950
48977
|
if (multExpr.ONE_TOK() && result.length > 1) {
|
|
48978
|
+
this.environmentStack.pop();
|
|
48951
48979
|
const value = false;
|
|
48952
48980
|
this.cacheResult(ctx, freeVarsKey, value);
|
|
48953
48981
|
return value;
|
|
48954
48982
|
}
|
|
48955
48983
|
}
|
|
48956
48984
|
}
|
|
48985
|
+
this.environmentStack.pop();
|
|
48957
48986
|
if (ctx.quant().ALL_TOK()) {
|
|
48958
48987
|
const value = !foundFalse;
|
|
48959
48988
|
this.cacheResult(ctx, freeVarsKey, value);
|
|
@@ -49747,7 +49776,15 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
|
|
|
49747
49776
|
if (isTupleArray(childrenResults)) {
|
|
49748
49777
|
const transitiveClosureResult = transitiveClosure(childrenResults);
|
|
49749
49778
|
const idenResult = this.getIden();
|
|
49750
|
-
|
|
49779
|
+
// Optimize: Use Set for efficient deduplication instead of deduplicateTuples
|
|
49780
|
+
const resultSet = new Set();
|
|
49781
|
+
for (const tuple of idenResult) {
|
|
49782
|
+
resultSet.add(tupleToKey(tuple));
|
|
49783
|
+
}
|
|
49784
|
+
for (const tuple of transitiveClosureResult) {
|
|
49785
|
+
resultSet.add(tupleToKey(tuple));
|
|
49786
|
+
}
|
|
49787
|
+
return Array.from(resultSet).map(key => JSON.parse(key));
|
|
49751
49788
|
}
|
|
49752
49789
|
}
|
|
49753
49790
|
return childrenResults;
|
|
@@ -49872,18 +49909,18 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
|
|
|
49872
49909
|
}
|
|
49873
49910
|
const product = getCombinations(quantifiedSets);
|
|
49874
49911
|
const result = [];
|
|
49912
|
+
// Optimize: create environment once and reuse it
|
|
49913
|
+
const quantDeclEnv = {
|
|
49914
|
+
env: {},
|
|
49915
|
+
type: "quantDecl",
|
|
49916
|
+
};
|
|
49917
|
+
this.environmentStack.push(quantDeclEnv);
|
|
49875
49918
|
for (let i = 0; i < product.length; i++) {
|
|
49876
49919
|
const tuple = product[i];
|
|
49877
|
-
|
|
49878
|
-
env: {},
|
|
49879
|
-
type: "quantDecl",
|
|
49880
|
-
};
|
|
49920
|
+
// Update environment values in place
|
|
49881
49921
|
for (let j = 0; j < varNames.length; j++) {
|
|
49882
|
-
|
|
49883
|
-
const varValue = tuple[j];
|
|
49884
|
-
quantDeclEnv.env[varName] = varValue;
|
|
49922
|
+
quantDeclEnv.env[varNames[j]] = tuple[j];
|
|
49885
49923
|
}
|
|
49886
|
-
this.environmentStack.push(quantDeclEnv);
|
|
49887
49924
|
// now, we want to evaluate the barExpr
|
|
49888
49925
|
const barExprValue = this.visit(barExpr);
|
|
49889
49926
|
if (!isBoolean(barExprValue)) {
|
|
@@ -49893,9 +49930,10 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
|
|
|
49893
49930
|
// will error if not boolean val, which we want
|
|
49894
49931
|
result.push(tuple);
|
|
49895
49932
|
}
|
|
49896
|
-
this.environmentStack.pop();
|
|
49897
49933
|
}
|
|
49898
|
-
|
|
49934
|
+
this.environmentStack.pop();
|
|
49935
|
+
// Deduplicate results to ensure set semantics
|
|
49936
|
+
return deduplicateTuples(result);
|
|
49899
49937
|
}
|
|
49900
49938
|
if (ctx.LEFT_PAREN_TOK()) {
|
|
49901
49939
|
// NOTE: we just return the result of evaluating the expr that is inside
|
|
@@ -49970,7 +50008,15 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
|
|
|
49970
50008
|
const typeNames = this.instanceData.getTypes().map((t) => t.id);
|
|
49971
50009
|
if (typeNames.includes(identifier)) {
|
|
49972
50010
|
const typeAtoms = this.instanceData.getTypes().find(t => t.id === identifier)?.atoms || [];
|
|
49973
|
-
|
|
50011
|
+
// Deduplicate atoms by ID to handle data sources with duplicate entries
|
|
50012
|
+
const uniqueAtomIds = new Set();
|
|
50013
|
+
const desiredValues = [];
|
|
50014
|
+
for (const atom of typeAtoms) {
|
|
50015
|
+
if (!uniqueAtomIds.has(atom.id)) {
|
|
50016
|
+
uniqueAtomIds.add(atom.id);
|
|
50017
|
+
desiredValues.push(atom.id);
|
|
50018
|
+
}
|
|
50019
|
+
}
|
|
49974
50020
|
result = desiredValues.map((singleValue) => [singleValue]);
|
|
49975
50021
|
}
|
|
49976
50022
|
for (const typeObj of this.instanceData.getTypes()) {
|
|
@@ -50089,7 +50135,15 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
|
|
|
50089
50135
|
if (!intType) {
|
|
50090
50136
|
throw new Error('Type "Int" not found in instance data');
|
|
50091
50137
|
}
|
|
50092
|
-
|
|
50138
|
+
// Deduplicate atoms by ID to handle data sources with duplicate entries
|
|
50139
|
+
const uniqueAtomIds = new Set();
|
|
50140
|
+
const intVals = [];
|
|
50141
|
+
for (const atom of intType.atoms) {
|
|
50142
|
+
if (!uniqueAtomIds.has(atom.id)) {
|
|
50143
|
+
uniqueAtomIds.add(atom.id);
|
|
50144
|
+
intVals.push([Number(atom.id)]);
|
|
50145
|
+
}
|
|
50146
|
+
}
|
|
50093
50147
|
return intVals;
|
|
50094
50148
|
}
|
|
50095
50149
|
return this.visitChildren(ctx);
|
|
@@ -61164,6 +61218,8 @@ class SimpleGraphQueryEvaluator {
|
|
|
61164
61218
|
constructor(datum) {
|
|
61165
61219
|
this.forgeListener = new ForgeListenerImpl_1.ForgeListenerImpl();
|
|
61166
61220
|
this.walker = new ParseTreeWalker_1.ParseTreeWalker();
|
|
61221
|
+
// Cache for parsed expressions to avoid re-parsing the same expression
|
|
61222
|
+
this.parseTreeCache = new Map();
|
|
61167
61223
|
this.datum = datum;
|
|
61168
61224
|
}
|
|
61169
61225
|
getExpressionParseTree(forgeExpr) {
|
|
@@ -61177,18 +61233,28 @@ class SimpleGraphQueryEvaluator {
|
|
|
61177
61233
|
return tree;
|
|
61178
61234
|
}
|
|
61179
61235
|
evaluateExpression(forgeExpr) {
|
|
61180
|
-
|
|
61181
|
-
|
|
61236
|
+
// Check cache first
|
|
61237
|
+
let tree;
|
|
61238
|
+
if (this.parseTreeCache.has(forgeExpr)) {
|
|
61239
|
+
tree = this.parseTreeCache.get(forgeExpr);
|
|
61182
61240
|
}
|
|
61183
|
-
|
|
61184
|
-
//
|
|
61185
|
-
|
|
61186
|
-
|
|
61187
|
-
|
|
61241
|
+
else {
|
|
61242
|
+
try { // now, we can actually evaluate the expression
|
|
61243
|
+
const parsedTree = this.getExpressionParseTree(forgeExpr);
|
|
61244
|
+
tree = parsedTree instanceof ForgeParser_1.ExprContext ? parsedTree : parsedTree.getChild(0);
|
|
61245
|
+
// Cache the parsed tree
|
|
61246
|
+
this.parseTreeCache.set(forgeExpr, tree);
|
|
61247
|
+
}
|
|
61248
|
+
catch (e) {
|
|
61249
|
+
// if we can't parse the expression, we return an error
|
|
61250
|
+
return {
|
|
61251
|
+
error: new Error(`Error parsing expression "${forgeExpr}"`)
|
|
61252
|
+
};
|
|
61253
|
+
}
|
|
61188
61254
|
}
|
|
61189
61255
|
const evaluator = new ForgeExprEvaluator_1.ForgeExprEvaluator(this.datum);
|
|
61190
61256
|
try {
|
|
61191
|
-
let result = evaluator.visit(tree
|
|
61257
|
+
let result = evaluator.visit(tree);
|
|
61192
61258
|
// ensure we're visiting an ExprContext
|
|
61193
61259
|
return result;
|
|
61194
61260
|
}
|
package/package.json
CHANGED