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 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)) {
@@ -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
- const quantDeclEnv = {
48905
- env: {},
48906
- type: "quantDecl",
48907
- };
48934
+ // Update environment values in place
48908
48935
  for (let j = 0; j < varNames.length; j++) {
48909
- const varName = varNames[j];
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
- return deduplicateTuples([...idenResult, ...transitiveClosureResult]);
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
- const quantDeclEnv = {
49878
- env: {},
49879
- type: "quantDecl",
49880
- };
49920
+ // Update environment values in place
49881
49921
  for (let j = 0; j < varNames.length; j++) {
49882
- const varName = varNames[j];
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
- return result;
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
- const desiredValues = typeAtoms.map((atom) => atom.id);
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
- const intVals = intType.atoms.map((atom) => [Number(atom.id)]);
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
- try { // now, we can actually evaluate the expression
61181
- var tree = this.getExpressionParseTree(forgeExpr);
61236
+ // Check cache first
61237
+ let tree;
61238
+ if (this.parseTreeCache.has(forgeExpr)) {
61239
+ tree = this.parseTreeCache.get(forgeExpr);
61182
61240
  }
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
- };
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 instanceof ForgeParser_1.ExprContext ? tree : tree.getChild(0));
61257
+ let result = evaluator.visit(tree);
61192
61258
  // ensure we're visiting an ExprContext
61193
61259
  return result;
61194
61260
  }
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.3",
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",