simple-graph-query 2.1.0 → 2.1.2

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 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
- // perform BFS from each node to get the transitive closure
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 transitiveClosure = new Set();
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
- while (queue.length > 0) {
48554
- const current = queue.shift();
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
- transitiveClosure.add(JSON.stringify([start, current]));
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(transitiveClosure).map((pair) => JSON.parse(pair));
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)) {
@@ -48884,6 +48901,12 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
48884
48901
  const result = [];
48885
48902
  let foundTrue = false;
48886
48903
  let foundFalse = false;
48904
+ // Optimize: create environment once and reuse it
48905
+ const quantDeclEnv = {
48906
+ env: {},
48907
+ type: "quantDecl",
48908
+ };
48909
+ this.environmentStack.push(quantDeclEnv);
48887
48910
  for (let i = 0; i < product.length; i++) {
48888
48911
  const tuple = product[i];
48889
48912
  if (isDisjoint) {
@@ -48901,16 +48924,10 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
48901
48924
  continue;
48902
48925
  }
48903
48926
  }
48904
- const quantDeclEnv = {
48905
- env: {},
48906
- type: "quantDecl",
48907
- };
48927
+ // Update environment values in place
48908
48928
  for (let j = 0; j < varNames.length; j++) {
48909
- const varName = varNames[j];
48910
- const varValue = tuple[j];
48911
- quantDeclEnv.env[varName] = varValue;
48929
+ quantDeclEnv.env[varNames[j]] = tuple[j];
48912
48930
  }
48913
- this.environmentStack.push(quantDeclEnv);
48914
48931
  // now, we want to evaluate the barExpr
48915
48932
  const barExprValue = this.visit(barExpr);
48916
48933
  if (!isBoolean(barExprValue)) {
@@ -48923,14 +48940,15 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
48923
48940
  else {
48924
48941
  foundFalse = true;
48925
48942
  }
48926
- this.environmentStack.pop();
48927
48943
  // short-circuit if possible
48928
48944
  if (ctx.quant().ALL_TOK() && foundFalse) {
48945
+ this.environmentStack.pop();
48929
48946
  const value = false;
48930
48947
  this.cacheResult(ctx, freeVarsKey, value);
48931
48948
  return value;
48932
48949
  }
48933
48950
  if (ctx.quant().NO_TOK() && foundTrue) {
48951
+ this.environmentStack.pop();
48934
48952
  const value = false;
48935
48953
  this.cacheResult(ctx, freeVarsKey, value);
48936
48954
  return value;
@@ -48938,22 +48956,26 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
48938
48956
  if (ctx.quant().mult()) {
48939
48957
  const multExpr = ctx.quant().mult();
48940
48958
  if (multExpr.LONE_TOK() && result.length > 1) {
48959
+ this.environmentStack.pop();
48941
48960
  const value = false;
48942
48961
  this.cacheResult(ctx, freeVarsKey, value);
48943
48962
  return value;
48944
48963
  }
48945
48964
  if (multExpr.SOME_TOK() && foundTrue) {
48965
+ this.environmentStack.pop();
48946
48966
  const value = true;
48947
48967
  this.cacheResult(ctx, freeVarsKey, value);
48948
48968
  return value;
48949
48969
  }
48950
48970
  if (multExpr.ONE_TOK() && result.length > 1) {
48971
+ this.environmentStack.pop();
48951
48972
  const value = false;
48952
48973
  this.cacheResult(ctx, freeVarsKey, value);
48953
48974
  return value;
48954
48975
  }
48955
48976
  }
48956
48977
  }
48978
+ this.environmentStack.pop();
48957
48979
  if (ctx.quant().ALL_TOK()) {
48958
48980
  const value = !foundFalse;
48959
48981
  this.cacheResult(ctx, freeVarsKey, value);
@@ -49747,7 +49769,15 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
49747
49769
  if (isTupleArray(childrenResults)) {
49748
49770
  const transitiveClosureResult = transitiveClosure(childrenResults);
49749
49771
  const idenResult = this.getIden();
49750
- return deduplicateTuples([...idenResult, ...transitiveClosureResult]);
49772
+ // Optimize: Use Set for efficient deduplication instead of deduplicateTuples
49773
+ const resultSet = new Set();
49774
+ for (const tuple of idenResult) {
49775
+ resultSet.add(tupleToKey(tuple));
49776
+ }
49777
+ for (const tuple of transitiveClosureResult) {
49778
+ resultSet.add(tupleToKey(tuple));
49779
+ }
49780
+ return Array.from(resultSet).map(key => JSON.parse(key));
49751
49781
  }
49752
49782
  }
49753
49783
  return childrenResults;
@@ -49872,18 +49902,18 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
49872
49902
  }
49873
49903
  const product = getCombinations(quantifiedSets);
49874
49904
  const result = [];
49905
+ // Optimize: create environment once and reuse it
49906
+ const quantDeclEnv = {
49907
+ env: {},
49908
+ type: "quantDecl",
49909
+ };
49910
+ this.environmentStack.push(quantDeclEnv);
49875
49911
  for (let i = 0; i < product.length; i++) {
49876
49912
  const tuple = product[i];
49877
- const quantDeclEnv = {
49878
- env: {},
49879
- type: "quantDecl",
49880
- };
49913
+ // Update environment values in place
49881
49914
  for (let j = 0; j < varNames.length; j++) {
49882
- const varName = varNames[j];
49883
- const varValue = tuple[j];
49884
- quantDeclEnv.env[varName] = varValue;
49915
+ quantDeclEnv.env[varNames[j]] = tuple[j];
49885
49916
  }
49886
- this.environmentStack.push(quantDeclEnv);
49887
49917
  // now, we want to evaluate the barExpr
49888
49918
  const barExprValue = this.visit(barExpr);
49889
49919
  if (!isBoolean(barExprValue)) {
@@ -49893,9 +49923,10 @@ class ForgeExprEvaluator extends AbstractParseTreeVisitor_1.AbstractParseTreeVis
49893
49923
  // will error if not boolean val, which we want
49894
49924
  result.push(tuple);
49895
49925
  }
49896
- this.environmentStack.pop();
49897
49926
  }
49898
- return result;
49927
+ this.environmentStack.pop();
49928
+ // Deduplicate results to ensure set semantics
49929
+ return deduplicateTuples(result);
49899
49930
  }
49900
49931
  if (ctx.LEFT_PAREN_TOK()) {
49901
49932
  // NOTE: we just return the result of evaluating the expr that is inside
@@ -61164,6 +61195,8 @@ class SimpleGraphQueryEvaluator {
61164
61195
  constructor(datum) {
61165
61196
  this.forgeListener = new ForgeListenerImpl_1.ForgeListenerImpl();
61166
61197
  this.walker = new ParseTreeWalker_1.ParseTreeWalker();
61198
+ // Cache for parsed expressions to avoid re-parsing the same expression
61199
+ this.parseTreeCache = new Map();
61167
61200
  this.datum = datum;
61168
61201
  }
61169
61202
  getExpressionParseTree(forgeExpr) {
@@ -61177,18 +61210,28 @@ class SimpleGraphQueryEvaluator {
61177
61210
  return tree;
61178
61211
  }
61179
61212
  evaluateExpression(forgeExpr) {
61180
- try { // now, we can actually evaluate the expression
61181
- var tree = this.getExpressionParseTree(forgeExpr);
61213
+ // Check cache first
61214
+ let tree;
61215
+ if (this.parseTreeCache.has(forgeExpr)) {
61216
+ tree = this.parseTreeCache.get(forgeExpr);
61182
61217
  }
61183
- catch (e) {
61184
- // if we can't parse the expression, we return an error
61185
- return {
61186
- error: new Error(`Error parsing expression "${forgeExpr}"`)
61187
- };
61218
+ else {
61219
+ try { // now, we can actually evaluate the expression
61220
+ const parsedTree = this.getExpressionParseTree(forgeExpr);
61221
+ tree = parsedTree instanceof ForgeParser_1.ExprContext ? parsedTree : parsedTree.getChild(0);
61222
+ // Cache the parsed tree
61223
+ this.parseTreeCache.set(forgeExpr, tree);
61224
+ }
61225
+ catch (e) {
61226
+ // if we can't parse the expression, we return an error
61227
+ return {
61228
+ error: new Error(`Error parsing expression "${forgeExpr}"`)
61229
+ };
61230
+ }
61188
61231
  }
61189
61232
  const evaluator = new ForgeExprEvaluator_1.ForgeExprEvaluator(this.datum);
61190
61233
  try {
61191
- let result = evaluator.visit(tree instanceof ForgeParser_1.ExprContext ? tree : tree.getChild(0));
61234
+ let result = evaluator.visit(tree);
61192
61235
  // ensure we're visiting an ExprContext
61193
61236
  return result;
61194
61237
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-graph-query",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "TypeScript evaluator for Forge expressions with browser-compatible UMD bundle",
5
5
  "main": "dist/simple-graph-query.bundle.js",
6
6
  "types": "dist/index.d.ts",