Skip to content

Commit a20a324

Browse files
committed
Account for bound inclusivity with LimitedFilter
1 parent e6b2bc4 commit a20a324

File tree

2 files changed

+138
-25
lines changed

2 files changed

+138
-25
lines changed

packages/database/src/core/view/filter/LimitedFilter.ts

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,17 @@ export class LimitedFilter implements NodeFilter {
4646

4747
private readonly reverse_: boolean;
4848

49+
private readonly startIsInclusive_: boolean;
50+
51+
private readonly endIsInclusive_: boolean;
52+
4953
constructor(params: QueryParams) {
5054
this.rangedFilter_ = new RangedFilter(params);
5155
this.index_ = params.getIndex();
5256
this.limit_ = params.getLimit();
5357
this.reverse_ = !params.isViewFromLeft();
58+
this.startIsInclusive_ = !params.startAfterSet_;
59+
this.endIsInclusive_ = !params.endBeforeSet_;
5460
}
5561
updateChild(
5662
snap: Node,
@@ -119,19 +125,17 @@ export class LimitedFilter implements NodeFilter {
119125
let count = 0;
120126
while (iterator.hasNext() && count < this.limit_) {
121127
const next = iterator.getNext();
122-
let inRange;
123-
if (this.reverse_) {
124-
inRange =
125-
this.index_.compare(this.rangedFilter_.getStartPost(), next) <= 0;
126-
} else {
127-
inRange =
128-
this.index_.compare(next, this.rangedFilter_.getEndPost()) <= 0;
129-
}
128+
const inRange =
129+
this.withinDirectionalStart(next) &&
130+
this.withinDirectionalEnd(next);
130131
if (inRange) {
131132
filtered = filtered.updateImmediateChild(next.name, next.node);
132133
count++;
134+
} else if (!this.withinDirectionalStart(next)) {
135+
// if we have not reached the start, skip to the next element
136+
continue;
133137
} else {
134-
// if we have reached the end post, we cannot keep adding elemments
138+
// if we have reached the end, stop adding elements
135139
break;
136140
}
137141
}
@@ -142,33 +146,21 @@ export class LimitedFilter implements NodeFilter {
142146
filtered = filtered.updatePriority(
143147
ChildrenNode.EMPTY_NODE
144148
) as ChildrenNode;
145-
let startPost;
146-
let endPost;
147-
let cmp;
149+
148150
let iterator;
149151
if (this.reverse_) {
150152
iterator = filtered.getReverseIterator(this.index_);
151-
startPost = this.rangedFilter_.getEndPost();
152-
endPost = this.rangedFilter_.getStartPost();
153-
const indexCompare = this.index_.getCompare();
154-
cmp = (a: NamedNode, b: NamedNode) => indexCompare(b, a);
155153
} else {
156154
iterator = filtered.getIterator(this.index_);
157-
startPost = this.rangedFilter_.getStartPost();
158-
endPost = this.rangedFilter_.getEndPost();
159-
cmp = this.index_.getCompare();
160155
}
161156

162157
let count = 0;
163-
let foundStartPost = false;
164158
while (iterator.hasNext()) {
165159
const next = iterator.getNext();
166-
if (!foundStartPost && cmp(startPost, next) <= 0) {
167-
// start adding
168-
foundStartPost = true;
169-
}
170160
const inRange =
171-
foundStartPost && count < this.limit_ && cmp(next, endPost) <= 0;
161+
count < this.limit_ &&
162+
this.withinDirectionalStart(next) &&
163+
this.withinDirectionalEnd(next);
172164
if (inRange) {
173165
count++;
174166
} else {
@@ -300,4 +292,26 @@ export class LimitedFilter implements NodeFilter {
300292
return snap;
301293
}
302294
}
295+
296+
private withinDirectionalStart = (node: NamedNode) =>
297+
this.reverse_ ? this.withinEndPost(node) : this.withinStartPost(node);
298+
299+
private withinDirectionalEnd = (node: NamedNode) =>
300+
this.reverse_ ? this.withinStartPost(node) : this.withinEndPost(node);
301+
302+
private withinStartPost = (node: NamedNode) => {
303+
const compareRes = this.index_.getCompare()(
304+
this.rangedFilter_.getStartPost(),
305+
node
306+
);
307+
return this.startIsInclusive_ ? compareRes <= 0 : compareRes < 0;
308+
};
309+
310+
private withinEndPost = (node: NamedNode) => {
311+
const compareRes = this.index_.getCompare()(
312+
node,
313+
this.rangedFilter_.getEndPost()
314+
);
315+
return this.endIsInclusive_ ? compareRes <= 0 : compareRes < 0;
316+
};
303317
}

packages/database/test/helpers/syncPointSpec.json

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4271,6 +4271,105 @@
42714271
}
42724272
]
42734273
},
4274+
{
4275+
"name": "Queries indexed by key with startAfter, endBefore, and limitToFirst work",
4276+
"steps":
4277+
[
4278+
{
4279+
"type": "listen",
4280+
"path": "",
4281+
"params": {
4282+
"tag": 1,
4283+
"orderByKey": true,
4284+
"startAfter": { "index": "a" },
4285+
"endBefore": { "index": "d" },
4286+
"limitToFirst": 1
4287+
},
4288+
"events": []
4289+
},
4290+
{
4291+
".comment": "update from server sends all data",
4292+
"type": "serverUpdate",
4293+
"path": "",
4294+
"data": {
4295+
"a": { ".value": "a" },
4296+
"b": { ".value": "b" },
4297+
"c": { ".value": "c" },
4298+
"d": { ".value": "d" }
4299+
},
4300+
"events": [
4301+
{
4302+
"path": "",
4303+
"type": "child_added",
4304+
"name": "b",
4305+
"prevName": null,
4306+
"data": { ".value": "b" }
4307+
},
4308+
{
4309+
"path": "",
4310+
"type": "value",
4311+
"data": {
4312+
"b": { ".value": "b" }
4313+
}
4314+
}
4315+
]
4316+
}
4317+
]
4318+
},
4319+
{
4320+
"name": "Queries indexed by key with startAfter, endBefore, and limitToLast work",
4321+
"steps":
4322+
[
4323+
{
4324+
"type": "listen",
4325+
"path": "",
4326+
"params": {
4327+
"tag": 1,
4328+
"orderByKey": true,
4329+
"startAfter": { "index": "a" },
4330+
"endBefore": { "index": "e" },
4331+
"limitToLast": 2
4332+
},
4333+
"events": []
4334+
},
4335+
{
4336+
".comment": "update from server sends all data",
4337+
"type": "serverUpdate",
4338+
"path": "",
4339+
"data": {
4340+
"a": { ".value": "a" },
4341+
"b": { ".value": "b" },
4342+
"c": { ".value": "c" },
4343+
"d": { ".value": "d" },
4344+
"e": { ".value": "e" }
4345+
},
4346+
"events": [
4347+
{
4348+
"path": "",
4349+
"type": "child_added",
4350+
"name": "c",
4351+
"prevName": null,
4352+
"data": { ".value": "c" }
4353+
},
4354+
{
4355+
"path": "",
4356+
"type": "child_added",
4357+
"name": "d",
4358+
"prevName": null,
4359+
"data": { ".value": "d" }
4360+
},
4361+
{
4362+
"path": "",
4363+
"type": "value",
4364+
"data": {
4365+
"c": { ".value": "c" },
4366+
"d": { ".value": "d" }
4367+
}
4368+
}
4369+
]
4370+
}
4371+
]
4372+
},
42744373
{
42754374
"name": "Update to single child that moves out of window",
42764375
"steps":

0 commit comments

Comments
 (0)