flowquery 1.0.28 → 1.0.29
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/flowquery.min.js +1 -1
- package/dist/parsing/expressions/operator.d.ts +2 -1
- package/dist/parsing/expressions/operator.d.ts.map +1 -1
- package/dist/parsing/expressions/operator.js +35 -2
- package/dist/parsing/expressions/operator.js.map +1 -1
- package/dist/parsing/functions/count.d.ts +21 -0
- package/dist/parsing/functions/count.d.ts.map +1 -0
- package/dist/parsing/functions/count.js +70 -0
- package/dist/parsing/functions/count.js.map +1 -0
- package/dist/parsing/functions/function_factory.d.ts +1 -0
- package/dist/parsing/functions/function_factory.d.ts.map +1 -1
- package/dist/parsing/functions/function_factory.js +1 -0
- package/dist/parsing/functions/function_factory.js.map +1 -1
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +14 -2
- package/dist/parsing/parser.js.map +1 -1
- package/docs/flowquery.min.js +1 -1
- package/flowquery-py/pyproject.toml +1 -1
- package/flowquery-py/src/parsing/functions/__init__.py +2 -0
- package/flowquery-py/src/parsing/functions/count.py +79 -0
- package/flowquery-py/src/parsing/parser.py +12 -2
- package/flowquery-py/tests/compute/test_runner.py +138 -0
- package/flowquery-py/tests/parsing/test_expression.py +79 -0
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/package.json +1 -1
- package/src/parsing/expressions/operator.ts +26 -2
- package/src/parsing/functions/count.ts +54 -0
- package/src/parsing/functions/function_factory.ts +1 -0
- package/src/parsing/parser.ts +14 -2
- package/tests/compute/runner.test.ts +129 -0
- package/tests/parsing/expression.test.ts +129 -0
package/package.json
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
import ASTNode from "../ast_node";
|
|
2
2
|
|
|
3
|
+
function deepEquals(a: any, b: any): boolean {
|
|
4
|
+
if (a === b) return true;
|
|
5
|
+
if (a === null || b === null || a === undefined || b === undefined) return false;
|
|
6
|
+
if (typeof a !== typeof b) return false;
|
|
7
|
+
if (typeof a !== "object") return false;
|
|
8
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
9
|
+
if (Array.isArray(a)) {
|
|
10
|
+
if (a.length !== b.length) return false;
|
|
11
|
+
for (let i = 0; i < a.length; i++) {
|
|
12
|
+
if (!deepEquals(a[i], b[i])) return false;
|
|
13
|
+
}
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
const keysA = Object.keys(a);
|
|
17
|
+
const keysB = Object.keys(b);
|
|
18
|
+
if (keysA.length !== keysB.length) return false;
|
|
19
|
+
for (const key of keysA) {
|
|
20
|
+
if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
|
|
21
|
+
if (!deepEquals(a[key], b[key])) return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
3
26
|
abstract class Operator extends ASTNode {
|
|
4
27
|
private _precedence: number = 0;
|
|
5
28
|
private _leftAssociative: boolean = true;
|
|
@@ -88,7 +111,7 @@ class Equals extends Operator {
|
|
|
88
111
|
super(0, true);
|
|
89
112
|
}
|
|
90
113
|
public value(): number {
|
|
91
|
-
return this.lhs.value()
|
|
114
|
+
return deepEquals(this.lhs.value(), this.rhs.value()) ? 1 : 0;
|
|
92
115
|
}
|
|
93
116
|
}
|
|
94
117
|
|
|
@@ -97,7 +120,7 @@ class NotEquals extends Operator {
|
|
|
97
120
|
super(0, true);
|
|
98
121
|
}
|
|
99
122
|
public value(): number {
|
|
100
|
-
return this.lhs.value()
|
|
123
|
+
return !deepEquals(this.lhs.value(), this.rhs.value()) ? 1 : 0;
|
|
101
124
|
}
|
|
102
125
|
}
|
|
103
126
|
|
|
@@ -296,6 +319,7 @@ class NotEndsWith extends Operator {
|
|
|
296
319
|
}
|
|
297
320
|
|
|
298
321
|
export {
|
|
322
|
+
deepEquals,
|
|
299
323
|
Operator,
|
|
300
324
|
Add,
|
|
301
325
|
Subtract,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import AggregateFunction from "./aggregate_function";
|
|
2
|
+
import { FunctionDef } from "./function_metadata";
|
|
3
|
+
import ReducerElement from "./reducer_element";
|
|
4
|
+
|
|
5
|
+
class CountReducerElement extends ReducerElement {
|
|
6
|
+
private _value: number = 0;
|
|
7
|
+
public get value(): any {
|
|
8
|
+
return this._value;
|
|
9
|
+
}
|
|
10
|
+
public set value(value: any) {
|
|
11
|
+
this._value += 1;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class DistinctCountReducerElement extends ReducerElement {
|
|
16
|
+
private _seen: Set<string> = new Set();
|
|
17
|
+
public get value(): any {
|
|
18
|
+
return this._seen.size;
|
|
19
|
+
}
|
|
20
|
+
public set value(value: any) {
|
|
21
|
+
const key: string = JSON.stringify(value);
|
|
22
|
+
this._seen.add(key);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@FunctionDef({
|
|
27
|
+
description: "Counts the number of values across grouped rows",
|
|
28
|
+
category: "aggregate",
|
|
29
|
+
parameters: [{ name: "value", description: "Value to count", type: "any" }],
|
|
30
|
+
output: { description: "Number of values", type: "number", example: 3 },
|
|
31
|
+
examples: [
|
|
32
|
+
"WITH [1, 2, 3] AS nums UNWIND nums AS n RETURN count(n)",
|
|
33
|
+
"WITH [1, 2, 2, 3] AS nums UNWIND nums AS n RETURN count(distinct n)",
|
|
34
|
+
],
|
|
35
|
+
})
|
|
36
|
+
class Count extends AggregateFunction {
|
|
37
|
+
private _distinct: boolean = false;
|
|
38
|
+
constructor() {
|
|
39
|
+
super("count");
|
|
40
|
+
this._expectedParameterCount = 1;
|
|
41
|
+
this._supports_distinct = true;
|
|
42
|
+
}
|
|
43
|
+
public reduce(element: CountReducerElement | DistinctCountReducerElement): void {
|
|
44
|
+
element.value = this.firstChild().value();
|
|
45
|
+
}
|
|
46
|
+
public element(): CountReducerElement | DistinctCountReducerElement {
|
|
47
|
+
return this._distinct ? new DistinctCountReducerElement() : new CountReducerElement();
|
|
48
|
+
}
|
|
49
|
+
public set distinct(distinct: boolean) {
|
|
50
|
+
this._distinct = distinct;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default Count;
|
package/src/parsing/parser.ts
CHANGED
|
@@ -171,11 +171,17 @@ class Parser extends BaseParser {
|
|
|
171
171
|
}
|
|
172
172
|
this.setNextToken();
|
|
173
173
|
this.expectAndSkipWhitespaceAndComments();
|
|
174
|
+
let distinct = false;
|
|
175
|
+
if (this.token.isDistinct()) {
|
|
176
|
+
distinct = true;
|
|
177
|
+
this.setNextToken();
|
|
178
|
+
this.expectAndSkipWhitespaceAndComments();
|
|
179
|
+
}
|
|
174
180
|
const expressions = Array.from(this.parseExpressions(AliasOption.REQUIRED));
|
|
175
181
|
if (expressions.length === 0) {
|
|
176
182
|
throw new Error("Expected expression");
|
|
177
183
|
}
|
|
178
|
-
if (expressions.some((expression: Expression) => expression.has_reducers())) {
|
|
184
|
+
if (distinct || expressions.some((expression: Expression) => expression.has_reducers())) {
|
|
179
185
|
return new AggregatedWith(expressions);
|
|
180
186
|
}
|
|
181
187
|
return new With(expressions);
|
|
@@ -220,11 +226,17 @@ class Parser extends BaseParser {
|
|
|
220
226
|
}
|
|
221
227
|
this.setNextToken();
|
|
222
228
|
this.expectAndSkipWhitespaceAndComments();
|
|
229
|
+
let distinct = false;
|
|
230
|
+
if (this.token.isDistinct()) {
|
|
231
|
+
distinct = true;
|
|
232
|
+
this.setNextToken();
|
|
233
|
+
this.expectAndSkipWhitespaceAndComments();
|
|
234
|
+
}
|
|
223
235
|
const expressions = Array.from(this.parseExpressions(AliasOption.OPTIONAL));
|
|
224
236
|
if (expressions.length === 0) {
|
|
225
237
|
throw new Error("Expected expression");
|
|
226
238
|
}
|
|
227
|
-
if (expressions.some((expression: Expression) => expression.has_reducers())) {
|
|
239
|
+
if (distinct || expressions.some((expression: Expression) => expression.has_reducers())) {
|
|
228
240
|
return new AggregatedReturn(expressions);
|
|
229
241
|
}
|
|
230
242
|
this._returns++;
|
|
@@ -140,6 +140,58 @@ test("Test aggregated return with multiple aggregates", async () => {
|
|
|
140
140
|
expect(results[1]).toEqual({ i: 2, sum: 20, avg: 2.5 });
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
+
test("Test count", async () => {
|
|
144
|
+
const runner = new Runner(
|
|
145
|
+
"unwind [1, 1, 2, 2] as i unwind [1, 2, 3, 4] as j return i, count(j) as cnt"
|
|
146
|
+
);
|
|
147
|
+
await runner.run();
|
|
148
|
+
const results = runner.results;
|
|
149
|
+
expect(results.length).toBe(2);
|
|
150
|
+
expect(results[0]).toEqual({ i: 1, cnt: 8 });
|
|
151
|
+
expect(results[1]).toEqual({ i: 2, cnt: 8 });
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("Test count distinct", async () => {
|
|
155
|
+
const runner = new Runner(
|
|
156
|
+
`
|
|
157
|
+
unwind [1, 1, 2, 2] as i
|
|
158
|
+
unwind [1, 2, 1, 2] as j
|
|
159
|
+
return i, count(distinct j) as cnt
|
|
160
|
+
`
|
|
161
|
+
);
|
|
162
|
+
await runner.run();
|
|
163
|
+
const results = runner.results;
|
|
164
|
+
expect(results.length).toBe(2);
|
|
165
|
+
expect(results[0]).toEqual({ i: 1, cnt: 2 });
|
|
166
|
+
expect(results[1]).toEqual({ i: 2, cnt: 2 });
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("Test count with strings", async () => {
|
|
170
|
+
const runner = new Runner(
|
|
171
|
+
`
|
|
172
|
+
unwind ["a", "b", "a", "c"] as s
|
|
173
|
+
return count(s) as cnt
|
|
174
|
+
`
|
|
175
|
+
);
|
|
176
|
+
await runner.run();
|
|
177
|
+
const results = runner.results;
|
|
178
|
+
expect(results.length).toBe(1);
|
|
179
|
+
expect(results[0]).toEqual({ cnt: 4 });
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("Test count distinct with strings", async () => {
|
|
183
|
+
const runner = new Runner(
|
|
184
|
+
`
|
|
185
|
+
unwind ["a", "b", "a", "c"] as s
|
|
186
|
+
return count(distinct s) as cnt
|
|
187
|
+
`
|
|
188
|
+
);
|
|
189
|
+
await runner.run();
|
|
190
|
+
const results = runner.results;
|
|
191
|
+
expect(results.length).toBe(1);
|
|
192
|
+
expect(results[0]).toEqual({ cnt: 3 });
|
|
193
|
+
});
|
|
194
|
+
|
|
143
195
|
test("Test avg with null", async () => {
|
|
144
196
|
const runner = new Runner("return avg(null) as avg");
|
|
145
197
|
await runner.run();
|
|
@@ -330,6 +382,83 @@ test("Test collect distinct with associative array", async () => {
|
|
|
330
382
|
expect(results[1]).toEqual({ i: 2, collected: [{ j: 1 }, { j: 2 }, { j: 3 }] });
|
|
331
383
|
});
|
|
332
384
|
|
|
385
|
+
test("Test return distinct", async () => {
|
|
386
|
+
const runner = new Runner(
|
|
387
|
+
`
|
|
388
|
+
unwind [1, 1, 2, 2, 3, 3] as i
|
|
389
|
+
return distinct i
|
|
390
|
+
`
|
|
391
|
+
);
|
|
392
|
+
await runner.run();
|
|
393
|
+
const results = runner.results;
|
|
394
|
+
expect(results.length).toBe(3);
|
|
395
|
+
expect(results[0]).toEqual({ i: 1 });
|
|
396
|
+
expect(results[1]).toEqual({ i: 2 });
|
|
397
|
+
expect(results[2]).toEqual({ i: 3 });
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
test("Test return distinct with multiple expressions", async () => {
|
|
401
|
+
const runner = new Runner(
|
|
402
|
+
`
|
|
403
|
+
unwind [1, 1, 2, 2] as i
|
|
404
|
+
unwind [10, 10, 20, 20] as j
|
|
405
|
+
return distinct i, j
|
|
406
|
+
`
|
|
407
|
+
);
|
|
408
|
+
await runner.run();
|
|
409
|
+
const results = runner.results;
|
|
410
|
+
expect(results.length).toBe(4);
|
|
411
|
+
expect(results[0]).toEqual({ i: 1, j: 10 });
|
|
412
|
+
expect(results[1]).toEqual({ i: 1, j: 20 });
|
|
413
|
+
expect(results[2]).toEqual({ i: 2, j: 10 });
|
|
414
|
+
expect(results[3]).toEqual({ i: 2, j: 20 });
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
test("Test with distinct", async () => {
|
|
418
|
+
const runner = new Runner(
|
|
419
|
+
`
|
|
420
|
+
unwind [1, 1, 2, 2, 3, 3] as i
|
|
421
|
+
with distinct i as i
|
|
422
|
+
return i
|
|
423
|
+
`
|
|
424
|
+
);
|
|
425
|
+
await runner.run();
|
|
426
|
+
const results = runner.results;
|
|
427
|
+
expect(results.length).toBe(3);
|
|
428
|
+
expect(results[0]).toEqual({ i: 1 });
|
|
429
|
+
expect(results[1]).toEqual({ i: 2 });
|
|
430
|
+
expect(results[2]).toEqual({ i: 3 });
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test("Test with distinct and aggregation", async () => {
|
|
434
|
+
const runner = new Runner(
|
|
435
|
+
`
|
|
436
|
+
unwind [1, 1, 2, 2] as i
|
|
437
|
+
with distinct i as i
|
|
438
|
+
return sum(i) as total
|
|
439
|
+
`
|
|
440
|
+
);
|
|
441
|
+
await runner.run();
|
|
442
|
+
const results = runner.results;
|
|
443
|
+
expect(results.length).toBe(1);
|
|
444
|
+
expect(results[0]).toEqual({ total: 3 });
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
test("Test return distinct with strings", async () => {
|
|
448
|
+
const runner = new Runner(
|
|
449
|
+
`
|
|
450
|
+
unwind ["a", "b", "a", "c", "b"] as x
|
|
451
|
+
return distinct x
|
|
452
|
+
`
|
|
453
|
+
);
|
|
454
|
+
await runner.run();
|
|
455
|
+
const results = runner.results;
|
|
456
|
+
expect(results.length).toBe(3);
|
|
457
|
+
expect(results[0]).toEqual({ x: "a" });
|
|
458
|
+
expect(results[1]).toEqual({ x: "b" });
|
|
459
|
+
expect(results[2]).toEqual({ x: "c" });
|
|
460
|
+
});
|
|
461
|
+
|
|
333
462
|
test("Test join function", async () => {
|
|
334
463
|
const runner = new Runner('RETURN join(["a", "b", "c"], ",") as join');
|
|
335
464
|
await runner.run();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import ASTNode from "../../src/parsing/ast_node";
|
|
1
2
|
import Null from "../../src/parsing/components/null";
|
|
2
3
|
import Expression from "../../src/parsing/expressions/expression";
|
|
3
4
|
import Number from "../../src/parsing/expressions/number";
|
|
@@ -6,19 +7,33 @@ import {
|
|
|
6
7
|
And,
|
|
7
8
|
Contains,
|
|
8
9
|
EndsWith,
|
|
10
|
+
Equals,
|
|
9
11
|
GreaterThan,
|
|
10
12
|
Is,
|
|
11
13
|
IsNot,
|
|
12
14
|
Multiply,
|
|
13
15
|
NotContains,
|
|
14
16
|
NotEndsWith,
|
|
17
|
+
NotEquals,
|
|
15
18
|
NotStartsWith,
|
|
16
19
|
Power,
|
|
17
20
|
StartsWith,
|
|
18
21
|
Subtract,
|
|
22
|
+
deepEquals,
|
|
19
23
|
} from "../../src/parsing/expressions/operator";
|
|
20
24
|
import String from "../../src/parsing/expressions/string";
|
|
21
25
|
|
|
26
|
+
class ObjectValue extends ASTNode {
|
|
27
|
+
private _value: any;
|
|
28
|
+
constructor(value: any) {
|
|
29
|
+
super();
|
|
30
|
+
this._value = value;
|
|
31
|
+
}
|
|
32
|
+
public value(): any {
|
|
33
|
+
return this._value;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
22
37
|
test("Test Expression Shunting Yard algorithm", () => {
|
|
23
38
|
const expression = new Expression();
|
|
24
39
|
expression.addNode(new Number("2"));
|
|
@@ -172,3 +187,117 @@ test("Test NOT ENDS WITH", () => {
|
|
|
172
187
|
expression.finish();
|
|
173
188
|
expect(expression.value()).toBe(1);
|
|
174
189
|
});
|
|
190
|
+
|
|
191
|
+
// deepEquals unit tests
|
|
192
|
+
|
|
193
|
+
test("deepEquals with identical primitives", () => {
|
|
194
|
+
expect(deepEquals(1, 1)).toBe(true);
|
|
195
|
+
expect(deepEquals("abc", "abc")).toBe(true);
|
|
196
|
+
expect(deepEquals(null, null)).toBe(true);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("deepEquals with different primitives", () => {
|
|
200
|
+
expect(deepEquals(1, 2)).toBe(false);
|
|
201
|
+
expect(deepEquals("abc", "def")).toBe(false);
|
|
202
|
+
expect(deepEquals(null, 1)).toBe(false);
|
|
203
|
+
expect(deepEquals(1, "1")).toBe(false);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("deepEquals with identical objects", () => {
|
|
207
|
+
const obj = { id: "1", name: "Alice" };
|
|
208
|
+
expect(deepEquals(obj, obj)).toBe(true);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("deepEquals with structurally equal objects", () => {
|
|
212
|
+
expect(deepEquals({ id: "1", name: "Alice" }, { id: "1", name: "Alice" })).toBe(true);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("deepEquals with different objects", () => {
|
|
216
|
+
expect(deepEquals({ id: "1", name: "Alice" }, { id: "2", name: "Bob" })).toBe(false);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("deepEquals with nested objects", () => {
|
|
220
|
+
expect(
|
|
221
|
+
deepEquals({ id: "1", data: { x: 1, y: [2, 3] } }, { id: "1", data: { x: 1, y: [2, 3] } })
|
|
222
|
+
).toBe(true);
|
|
223
|
+
expect(
|
|
224
|
+
deepEquals({ id: "1", data: { x: 1, y: [2, 3] } }, { id: "1", data: { x: 1, y: [2, 4] } })
|
|
225
|
+
).toBe(false);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("deepEquals with arrays", () => {
|
|
229
|
+
expect(deepEquals([1, 2, 3], [1, 2, 3])).toBe(true);
|
|
230
|
+
expect(deepEquals([1, 2, 3], [1, 2])).toBe(false);
|
|
231
|
+
expect(deepEquals([1, 2], [1, 2, 3])).toBe(false);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("deepEquals with objects having different keys", () => {
|
|
235
|
+
expect(deepEquals({ a: 1 }, { b: 1 })).toBe(false);
|
|
236
|
+
expect(deepEquals({ a: 1 }, { a: 1, b: 2 })).toBe(false);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Equals operator with objects
|
|
240
|
+
|
|
241
|
+
test("Test Equals with equal numbers", () => {
|
|
242
|
+
const expression = new Expression();
|
|
243
|
+
expression.addNode(new Number("42"));
|
|
244
|
+
expression.addNode(new Equals());
|
|
245
|
+
expression.addNode(new Number("42"));
|
|
246
|
+
expression.finish();
|
|
247
|
+
expect(expression.value()).toBe(1);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("Test Equals with different numbers", () => {
|
|
251
|
+
const expression = new Expression();
|
|
252
|
+
expression.addNode(new Number("42"));
|
|
253
|
+
expression.addNode(new Equals());
|
|
254
|
+
expression.addNode(new Number("99"));
|
|
255
|
+
expression.finish();
|
|
256
|
+
expect(expression.value()).toBe(0);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("Test Equals with structurally equal objects", () => {
|
|
260
|
+
const expression = new Expression();
|
|
261
|
+
expression.addNode(new ObjectValue({ id: "1", name: "Alice" }));
|
|
262
|
+
expression.addNode(new Equals());
|
|
263
|
+
expression.addNode(new ObjectValue({ id: "1", name: "Alice" }));
|
|
264
|
+
expression.finish();
|
|
265
|
+
expect(expression.value()).toBe(1);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("Test Equals with different objects", () => {
|
|
269
|
+
const expression = new Expression();
|
|
270
|
+
expression.addNode(new ObjectValue({ id: "1", name: "Alice" }));
|
|
271
|
+
expression.addNode(new Equals());
|
|
272
|
+
expression.addNode(new ObjectValue({ id: "2", name: "Bob" }));
|
|
273
|
+
expression.finish();
|
|
274
|
+
expect(expression.value()).toBe(0);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("Test NotEquals with structurally equal objects", () => {
|
|
278
|
+
const expression = new Expression();
|
|
279
|
+
expression.addNode(new ObjectValue({ id: "1", name: "Alice" }));
|
|
280
|
+
expression.addNode(new NotEquals());
|
|
281
|
+
expression.addNode(new ObjectValue({ id: "1", name: "Alice" }));
|
|
282
|
+
expression.finish();
|
|
283
|
+
expect(expression.value()).toBe(0);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
test("Test NotEquals with different objects", () => {
|
|
287
|
+
const expression = new Expression();
|
|
288
|
+
expression.addNode(new ObjectValue({ id: "1", name: "Alice" }));
|
|
289
|
+
expression.addNode(new NotEquals());
|
|
290
|
+
expression.addNode(new ObjectValue({ id: "2", name: "Bob" }));
|
|
291
|
+
expression.finish();
|
|
292
|
+
expect(expression.value()).toBe(1);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("Test Equals with same reference object", () => {
|
|
296
|
+
const obj = { id: "1", name: "Alice" };
|
|
297
|
+
const expression = new Expression();
|
|
298
|
+
expression.addNode(new ObjectValue(obj));
|
|
299
|
+
expression.addNode(new Equals());
|
|
300
|
+
expression.addNode(new ObjectValue(obj));
|
|
301
|
+
expression.finish();
|
|
302
|
+
expect(expression.value()).toBe(1);
|
|
303
|
+
});
|