Skip to content

Commit fead2fe

Browse files
authored
Merge pull request #657 from witheve/fix-stratifier
Fix stratification when joining aggregate results with other scans
2 parents 598efa6 + 226a1d0 commit fead2fe

File tree

2 files changed

+153
-18
lines changed

2 files changed

+153
-18
lines changed

src/runtime/builder.ts

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -558,9 +558,20 @@ function buildActions(block, context, actions, scans) {
558558
// Stratifier
559559
//-----------------------------------------------------------
560560

561+
function isAggregate(scan) {
562+
if(scan instanceof Aggregate ||
563+
scan instanceof Sort ||
564+
scan.hasAggregate ||
565+
(scan.strata && scan.strata.length > 1)) {
566+
return true;
567+
}
568+
return false;
569+
}
570+
561571
function stratify(scans) {
562572
if(!scans.length) return [new BlockStratum([], [])];
563573

574+
let aggregates = [];
564575
let variableInfo = {};
565576
let blockLevel = {};
566577

@@ -579,10 +590,10 @@ function stratify(scans) {
579590
let info = variableInfo[variable.id]
580591
let minLevel = level;
581592
for(let provider of info.providers) {
582-
let providerLevel = blockLevel[scan.id] || 0;
593+
let providerLevel = blockLevel[provider.id] || 0;
583594
minLevel = Math.min(minLevel, providerLevel);
584595
}
585-
info.level = level;
596+
info.level = minLevel;
586597
}
587598
}
588599

@@ -592,12 +603,10 @@ function stratify(scans) {
592603
provide(scan.a, scan);
593604
provide(scan.v, scan);
594605
} else if(scan instanceof Aggregate || scan instanceof Sort) {
606+
aggregates.push(scan);
607+
blockLevel[scan.id] = 1;
595608
for(let ret of scan.returns) {
596609
provide(ret, scan);
597-
blockLevel[scan.id] = 1;
598-
if(join.isVariable(ret)) {
599-
variableInfo[ret.id].level = 1;
600-
}
601610
}
602611
} else if(scan instanceof join.Constraint) {
603612
for(let ret of scan.returns) {
@@ -613,6 +622,18 @@ function stratify(scans) {
613622
}
614623
}
615624

625+
// Before we start to stratify, we need to run through all the aggregates
626+
// to determine what level their returns should be at. If the aggregate is
627+
// the only provider for the return, then it should be at the same level as
628+
// the aggregate itself. If, however, there are other scans that provide the
629+
// return, we want to pick up their level instead.
630+
for(let aggregate of aggregates) {
631+
let level = blockLevel[aggregate.id];
632+
for(let variable of aggregate.returns) {
633+
maybeLevelVariable(aggregate, level, variable);
634+
}
635+
}
636+
616637
let round = 0;
617638
let changed = true;
618639
while(changed && round <= scans.length) {
@@ -623,13 +644,7 @@ function stratify(scans) {
623644
// provider or if all the providers are now in a higher level. If so,
624645
// the variable's level is set to the scan's new level.
625646
for(let scan of scans) {
626-
let isAggregate = false;
627-
if(scan instanceof Aggregate ||
628-
scan instanceof Sort ||
629-
scan.hasAggregate ||
630-
(scan.strata && scan.strata.length > 1)) {
631-
isAggregate = true;
632-
}
647+
let isAgg = isAggregate(scan);
633648

634649
let levelMax = 0;
635650
let scanLevel = blockLevel[scan.id] || 0;
@@ -653,8 +668,18 @@ function stratify(scans) {
653668
infoLevel = info.level
654669
}
655670
// if this is an aggregate, we always have to be in the level that is
656-
// one greater than all our dependencies
657-
if(isAggregate) {
671+
// one greater than all our dependencies.
672+
// In the event that one of our dependencies is filtered by an aggregate,
673+
// we stratify ourselves behind it.
674+
if(isAgg) {
675+
for(let provider of info.providers) {
676+
if(isAggregate(provider) && provider !== scan) {
677+
if(blockLevel[provider.id] > infoLevel) {
678+
infoLevel = blockLevel[provider.id];
679+
}
680+
}
681+
}
682+
658683
infoLevel += 1;
659684
}
660685
levelMax = Math.max(levelMax, infoLevel);
@@ -692,7 +717,6 @@ function stratify(scans) {
692717
strata[0].scans.push(scan);
693718
}
694719
}
695-
// console.log(inspect(strata, {colors: true, depth: 10}));
696720

697721
let built = [];
698722
for(let level of strata) {

test/join.ts

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,75 @@ test("aggregate stratification with results", (assert) => {
10631063
assert.end();
10641064
});
10651065

1066+
test("aggregate stratification result joined with an object", (assert) => {
1067+
let expected = {
1068+
insert: [
1069+
["a", "tag", "person"],
1070+
["a", "name", "joe"],
1071+
["b", "tag", "person"],
1072+
["b", "name", "chris"],
1073+
["c", "tag", "expected"],
1074+
["c", "total", 2],
1075+
["d", "tag", "success"],
1076+
],
1077+
remove: [
1078+
]
1079+
};
1080+
evaluate(assert, expected, `
1081+
people
1082+
~~~
1083+
commit
1084+
[#expected total: 2]
1085+
[#person name: "joe"]
1086+
[#person name: "chris"]
1087+
~~~
1088+
1089+
~~~
1090+
search
1091+
expected = [#expected]
1092+
p = [#person]
1093+
expected.total = count[given: p]
1094+
commit
1095+
[#success]
1096+
~~~
1097+
`);
1098+
assert.end();
1099+
});
1100+
1101+
test("aggregate stratification result fails to join with an object", (assert) => {
1102+
let expected = {
1103+
insert: [
1104+
["a", "tag", "person"],
1105+
["a", "name", "joe"],
1106+
["b", "tag", "person"],
1107+
["b", "name", "chris"],
1108+
["c", "tag", "expected"],
1109+
["c", "total", 3],
1110+
],
1111+
remove: [
1112+
]
1113+
};
1114+
evaluate(assert, expected, `
1115+
people
1116+
~~~
1117+
commit
1118+
[#expected total: 3]
1119+
[#person name: "joe"]
1120+
[#person name: "chris"]
1121+
~~~
1122+
1123+
~~~
1124+
search
1125+
expected = [#expected]
1126+
p = [#person]
1127+
expected.total = count[given: p]
1128+
commit
1129+
[#success]
1130+
~~~
1131+
`);
1132+
assert.end();
1133+
});
1134+
10661135
test("aggregate stratification with another aggregate", (assert) => {
10671136
let expected = {
10681137
insert: [
@@ -1118,7 +1187,7 @@ test("unstratifiable aggregate", (assert) => {
11181187
[#person name: "mike" age: 20]
11191188
~~~
11201189
1121-
1190+
11221191
~~~
11231192
search
11241193
p = [#person age]
@@ -2694,4 +2763,46 @@ test("nested if/not expressions correctly get their args set", (assert) => {
26942763
~~~
26952764
`);
26962765
assert.end();
2697-
})
2766+
})
2767+
2768+
test("interdependent aggregates are appropriately stratified", (assert) => {
2769+
let expected = {
2770+
insert: [
2771+
["a", "tag", "foo"],
2772+
["a", "a", 3],
2773+
["a", "a", 5],
2774+
2775+
["b", "tag", "bar"],
2776+
["b", "b", 1],
2777+
["b", "b", 2],
2778+
["b", "b", 3],
2779+
2780+
["c", "tag", "result"],
2781+
["c", "value", 3]
2782+
],
2783+
remove: [],
2784+
};
2785+
evaluate(assert, expected, `
2786+
Add some test data
2787+
~~~ eve
2788+
commit
2789+
[#foo a: (3, 5)]
2790+
[#bar b: (1, 2, 3)]
2791+
~~~
2792+
2793+
2794+
~~~ eve
2795+
search
2796+
[#foo a]
2797+
[#bar b]
2798+
2799+
a = count[given: b]
2800+
final = sum[value: a, given: a]
2801+
2802+
bind
2803+
[#result value: final]
2804+
~~~
2805+
2806+
`);
2807+
assert.end();
2808+
})

0 commit comments

Comments
 (0)