Skip to content

Commit 77a5149

Browse files
authored
Merge 202ac61 into a9add5e
2 parents a9add5e + 202ac61 commit 77a5149

File tree

8 files changed

+388
-82
lines changed

8 files changed

+388
-82
lines changed

.changeset/empty-doors-brush.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@firebase/database': patch
3+
'@firebase/database-compat': patch
4+
---
5+
6+
Use new wire protocol parameters for startAfter, endBefore.

packages/database-compat/test/query.test.ts

+52-10
Original file line numberDiff line numberDiff line change
@@ -375,34 +375,76 @@ describe('Query Tests', () => {
375375
expect(queryId(path)).to.equal('default');
376376

377377
expect(queryId(path.startAt('pri','name'))).to.equal(
378-
'{"sn":"name","sp":"pri"}'
378+
'{"sin":true,"sn":"name","sp":"pri"}'
379379
);
380380
expect(queryId(path.startAfter('pri','name'))).to.equal(
381-
'{"sn":"name-","sp":"pri"}'
381+
'{"sin":false,"sn":"name","sp":"pri"}'
382382
);
383+
expect(queryId(path.endAt('pri','name'))).to.equal(
384+
'{"ein":true,"en":"name","ep":"pri"}'
385+
);
386+
expect(queryId(path.endBefore('pri','name'))).to.equal(
387+
'{"ein":false,"en":"name","ep":"pri"}'
388+
);
389+
383390
expect(queryId(path.startAt('spri').endAt('epri'))).to.equal(
384-
'{"ep":"epri","sp":"spri"}'
391+
'{"ein":true,"ep":"epri","sin":true,"sp":"spri"}'
392+
);
393+
expect(queryId(path.startAt('spri').endBefore('epri'))).to.equal(
394+
'{"ein":false,"en":"[MIN_NAME]","ep":"epri","sin":true,"sp":"spri"}'
385395
);
386396
expect(queryId(path.startAfter('spri').endAt('epri'))).to.equal(
387-
'{"ep":"epri","sn":"[MAX_NAME]","sp":"spri"}'
397+
'{"ein":true,"ep":"epri","sin":false,"sn":"[MAX_NAME]","sp":"spri"}'
398+
);
399+
expect(queryId(path.startAfter('spri').endBefore('epri'))).to.equal(
400+
'{"ein":false,"en":"[MIN_NAME]","ep":"epri","sin":false,"sn":"[MAX_NAME]","sp":"spri"}'
388401
);
402+
389403
expect(
390404
queryId(path.startAt('spri','sname').endAt('epri','ename'))
391-
).to.equal('{"en":"ename","ep":"epri","sn":"sname","sp":"spri"}');
405+
).to.equal(
406+
'{"ein":true,"en":"ename","ep":"epri","sin":true,"sn":"sname","sp":"spri"}'
407+
);
408+
expect(
409+
queryId(path.startAt('spri','sname').endBefore('epri','ename'))
410+
).to.equal(
411+
'{"ein":false,"en":"ename","ep":"epri","sin":true,"sn":"sname","sp":"spri"}'
412+
);
392413
expect(
393414
queryId(path.startAfter('spri','sname').endAt('epri','ename'))
394-
).to.equal('{"en":"ename","ep":"epri","sn":"sname-","sp":"spri"}');
415+
).to.equal(
416+
'{"ein":true,"en":"ename","ep":"epri","sin":false,"sn":"sname","sp":"spri"}'
417+
);
418+
expect(
419+
queryId(path.startAfter('spri','sname').endBefore('epri','ename'))
420+
).to.equal(
421+
'{"ein":false,"en":"ename","ep":"epri","sin":false,"sn":"sname","sp":"spri"}'
422+
);
423+
395424
expect(queryId(path.startAt('pri').limitToFirst(100))).to.equal(
396-
'{"l":100,"sp":"pri","vf":"l"}'
425+
'{"l":100,"sin":true,"sp":"pri","vf":"l"}'
397426
);
398427
expect(queryId(path.startAfter('pri').limitToFirst(100))).to.equal(
399-
'{"l":100,"sn":"[MAX_NAME]","sp":"pri","vf":"l"}'
428+
'{"l":100,"sin":false,"sn":"[MAX_NAME]","sp":"pri","vf":"l"}'
429+
);
430+
expect(queryId(path.endAt('pri').limitToLast(100))).to.equal(
431+
'{"ein":true,"ep":"pri","l":100,"vf":"r"}'
400432
);
433+
expect(queryId(path.endBefore('pri').limitToLast(100))).to.equal(
434+
'{"ein":false,"en":"[MIN_NAME]","ep":"pri","l":100,"vf":"r"}'
435+
);
436+
401437
expect(queryId(path.startAt('bar').orderByChild('foo'))).to.equal(
402-
'{"i":"foo","sp":"bar"}'
438+
'{"i":"foo","sin":true,"sp":"bar"}'
403439
);
404440
expect(queryId(path.startAfter('bar').orderByChild('foo'))).to.equal(
405-
'{"i":"foo","sn":"[MAX_NAME]","sp":"bar"}'
441+
'{"i":"foo","sin":false,"sn":"[MAX_NAME]","sp":"bar"}'
442+
);
443+
expect(queryId(path.endAt('bar').orderByChild('foo'))).to.equal(
444+
'{"ein":true,"ep":"bar","i":"foo"}'
445+
);
446+
expect(queryId(path.endBefore('bar').orderByChild('foo'))).to.equal(
447+
'{"ein":false,"en":"[MIN_NAME]","ep":"bar","i":"foo"}'
406448
);
407449
});
408450

packages/database/src/core/view/QueryParams.ts

+26-39
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import { KEY_INDEX } from '../snap/indexes/KeyIndex';
2222
import{PathIndex}from'../snap/indexes/PathIndex';
2323
import{PRIORITY_INDEX,PriorityIndex}from'../snap/indexes/PriorityIndex';
2424
import{VALUE_INDEX}from'../snap/indexes/ValueIndex';
25-
import{predecessor,successor}from'../util/NextPushId';
2625
import{MAX_NAME,MIN_NAME}from'../util/util';
2726

2827
import{IndexedFilter}from'./filter/IndexedFilter';
@@ -36,8 +35,10 @@ import { RangedFilter } from './filter/RangedFilter';
3635
constenumWIRE_PROTOCOL_CONSTANTS{
3736
INDEX_START_VALUE='sp',
3837
INDEX_START_NAME='sn',
38+
INDEX_START_IS_INCLUSIVE='sin',
3939
INDEX_END_VALUE='ep',
4040
INDEX_END_NAME='en',
41+
INDEX_END_IS_INCLUSIVE='ein',
4142
LIMIT='l',
4243
VIEW_FROM='vf',
4344
VIEW_FROM_LEFT='l',
@@ -53,8 +54,10 @@ const enum REST_QUERY_CONSTANTS {
5354
PRIORITY_INDEX='$priority',
5455
VALUE_INDEX='$value',
5556
KEY_INDEX='$key',
57+
START_AFTER='startAfter',
5658
START_AT='startAt',
5759
END_AT='endAt',
60+
END_BEFORE='endBefore',
5861
LIMIT_TO_FIRST='limitToFirst',
5962
LIMIT_TO_LAST='limitToLast'
6063
}
@@ -70,10 +73,10 @@ export class QueryParams {
7073
limitSet_=false;
7174
startSet_=false;
7275
startNameSet_=false;
73-
startAfterSet_=false;
76+
startAfterSet_=false;// can only be true if startSet_ is true
7477
endSet_=false;
7578
endNameSet_=false;
76-
endBeforeSet_=false;
79+
endBeforeSet_=false;// can only be true if endSet_ is true
7780
limit_=0;
7881
viewFrom_='';
7982
indexStartValue_: unknown|null=null;
@@ -86,14 +89,6 @@ export class QueryParams {
8689
returnthis.startSet_;
8790
}
8891

89-
hasStartAfter(): boolean{
90-
returnthis.startAfterSet_;
91-
}
92-
93-
hasEndBefore(): boolean{
94-
returnthis.endBeforeSet_;
95-
}
96-
9792
/**
9893
* @returns True if it would return from left.
9994
*/
@@ -191,10 +186,12 @@ export class QueryParams {
191186
copy.limitSet_=this.limitSet_;
192187
copy.limit_=this.limit_;
193188
copy.startSet_=this.startSet_;
189+
copy.startAfterSet_=this.startAfterSet_;
194190
copy.indexStartValue_=this.indexStartValue_;
195191
copy.startNameSet_=this.startNameSet_;
196192
copy.indexStartName_=this.indexStartName_;
197193
copy.endSet_=this.endSet_;
194+
copy.endBeforeSet_=this.endBeforeSet_;
198195
copy.indexEndValue_=this.indexEndValue_;
199196
copy.endNameSet_=this.endNameSet_;
200197
copy.indexEndName_=this.indexEndName_;
@@ -274,19 +271,10 @@ export function queryParamsStartAfter(
274271
key?: string|null
275272
): QueryParams{
276273
letparams: QueryParams;
277-
if(queryParams.index_===KEY_INDEX){
278-
if(typeofindexValue==='string'){
279-
indexValue=successor(indexValueasstring);
280-
}
274+
if(queryParams.index_===KEY_INDEX||!!key){
281275
params=queryParamsStartAt(queryParams,indexValue,key);
282276
}else{
283-
letchildKey: string;
284-
if(key==null){
285-
childKey=MAX_NAME;
286-
}else{
287-
childKey=successor(key);
288-
}
289-
params=queryParamsStartAt(queryParams,indexValue,childKey);
277+
params=queryParamsStartAt(queryParams,indexValue,MAX_NAME);
290278
}
291279
params.startAfterSet_=true;
292280
returnparams;
@@ -318,20 +306,11 @@ export function queryParamsEndBefore(
318306
indexValue: unknown,
319307
key?: string|null
320308
): QueryParams{
321-
letchildKey: string;
322309
letparams: QueryParams;
323-
if(queryParams.index_===KEY_INDEX){
324-
if(typeofindexValue==='string'){
325-
indexValue=predecessor(indexValueasstring);
326-
}
310+
if(queryParams.index_===KEY_INDEX||!!key){
327311
params=queryParamsEndAt(queryParams,indexValue,key);
328312
}else{
329-
if(key==null){
330-
childKey=MIN_NAME;
331-
}else{
332-
childKey=predecessor(key);
333-
}
334-
params=queryParamsEndAt(queryParams,indexValue,childKey);
313+
params=queryParamsEndAt(queryParams,indexValue,MIN_NAME);
335314
}
336315
params.endBeforeSet_=true;
337316
returnparams;
@@ -374,18 +353,22 @@ export function queryParamsToRestQueryStringParameters(
374353
qs[REST_QUERY_CONSTANTS.ORDER_BY]=stringify(orderBy);
375354

376355
if(queryParams.startSet_){
377-
qs[REST_QUERY_CONSTANTS.START_AT]=stringify(queryParams.indexStartValue_);
356+
conststartParam=queryParams.startAfterSet_
357+
? REST_QUERY_CONSTANTS.START_AFTER
358+
: REST_QUERY_CONSTANTS.START_AT;
359+
qs[startParam]=stringify(queryParams.indexStartValue_);
378360
if(queryParams.startNameSet_){
379-
qs[REST_QUERY_CONSTANTS.START_AT]+=
380-
','+stringify(queryParams.indexStartName_);
361+
qs[startParam]+=','+stringify(queryParams.indexStartName_);
381362
}
382363
}
383364

384365
if(queryParams.endSet_){
385-
qs[REST_QUERY_CONSTANTS.END_AT]=stringify(queryParams.indexEndValue_);
366+
constendParam=queryParams.endBeforeSet_
367+
? REST_QUERY_CONSTANTS.END_BEFORE
368+
: REST_QUERY_CONSTANTS.END_AT;
369+
qs[endParam]=stringify(queryParams.indexEndValue_);
386370
if(queryParams.endNameSet_){
387-
qs[REST_QUERY_CONSTANTS.END_AT]+=
388-
','+stringify(queryParams.indexEndName_);
371+
qs[endParam]+=','+stringify(queryParams.indexEndName_);
389372
}
390373
}
391374

@@ -411,12 +394,16 @@ export function queryParamsGetQueryObject(
411394
obj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_NAME]=
412395
queryParams.indexStartName_;
413396
}
397+
obj[WIRE_PROTOCOL_CONSTANTS.INDEX_START_IS_INCLUSIVE]=
398+
!queryParams.startAfterSet_;
414399
}
415400
if(queryParams.endSet_){
416401
obj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_VALUE]=queryParams.indexEndValue_;
417402
if(queryParams.endNameSet_){
418403
obj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_NAME]=queryParams.indexEndName_;
419404
}
405+
obj[WIRE_PROTOCOL_CONSTANTS.INDEX_END_IS_INCLUSIVE]=
406+
!queryParams.endBeforeSet_;
420407
}
421408
if(queryParams.limitSet_){
422409
obj[WIRE_PROTOCOL_CONSTANTS.LIMIT]=queryParams.limit_;

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

+38-27
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,17 @@ export class LimitedFilter implements NodeFilter {
4646

4747
privatereadonlyreverse_: boolean;
4848

49+
privatereadonlystartIsInclusive_: boolean;
50+
51+
privatereadonlyendIsInclusive_: boolean;
52+
4953
constructor(params: QueryParams){
5054
this.rangedFilter_=newRangedFilter(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,20 +125,15 @@ export class LimitedFilter implements NodeFilter {
119125
letcount=0;
120126
while(iterator.hasNext()&&count<this.limit_){
121127
constnext=iterator.getNext();
122-
letinRange;
123-
if(this.reverse_){
124-
inRange=
125-
this.index_.compare(this.rangedFilter_.getStartPost(),next)<=0;
128+
if(!this.withinDirectionalStart(next)){
129+
// if we have not reached the start, skip to the next element
130+
continue;
131+
}elseif(!this.withinDirectionalEnd(next)){
132+
// if we have reached the end, stop adding elements
133+
break;
126134
}else{
127-
inRange=
128-
this.index_.compare(next,this.rangedFilter_.getEndPost())<=0;
129-
}
130-
if(inRange){
131135
filtered=filtered.updateImmediateChild(next.name,next.node);
132136
count++;
133-
}else{
134-
// if we have reached the end post, we cannot keep adding elemments
135-
break;
136137
}
137138
}
138139
}else{
@@ -142,33 +143,21 @@ export class LimitedFilter implements NodeFilter {
142143
filtered=filtered.updatePriority(
143144
ChildrenNode.EMPTY_NODE
144145
)asChildrenNode;
145-
letstartPost;
146-
letendPost;
147-
letcmp;
146+
148147
letiterator;
149148
if(this.reverse_){
150149
iterator=filtered.getReverseIterator(this.index_);
151-
startPost=this.rangedFilter_.getEndPost();
152-
endPost=this.rangedFilter_.getStartPost();
153-
constindexCompare=this.index_.getCompare();
154-
cmp=(a: NamedNode,b: NamedNode)=>indexCompare(b,a);
155150
}else{
156151
iterator=filtered.getIterator(this.index_);
157-
startPost=this.rangedFilter_.getStartPost();
158-
endPost=this.rangedFilter_.getEndPost();
159-
cmp=this.index_.getCompare();
160152
}
161153

162154
letcount=0;
163-
letfoundStartPost=false;
164155
while(iterator.hasNext()){
165156
constnext=iterator.getNext();
166-
if(!foundStartPost&&cmp(startPost,next)<=0){
167-
// start adding
168-
foundStartPost=true;
169-
}
170157
constinRange=
171-
foundStartPost&&count<this.limit_&&cmp(next,endPost)<=0;
158+
count<this.limit_&&
159+
this.withinDirectionalStart(next)&&
160+
this.withinDirectionalEnd(next);
172161
if(inRange){
173162
count++;
174163
}else{
@@ -300,4 +289,26 @@ export class LimitedFilter implements NodeFilter {
300289
returnsnap;
301290
}
302291
}
292+
293+
privatewithinDirectionalStart=(node: NamedNode)=>
294+
this.reverse_ ? this.withinEndPost(node) : this.withinStartPost(node);
295+
296+
privatewithinDirectionalEnd=(node: NamedNode)=>
297+
this.reverse_ ? this.withinStartPost(node) : this.withinEndPost(node);
298+
299+
privatewithinStartPost=(node: NamedNode)=>{
300+
constcompareRes=this.index_.compare(
301+
this.rangedFilter_.getStartPost(),
302+
node
303+
);
304+
returnthis.startIsInclusive_ ? compareRes<=0 : compareRes<0;
305+
};
306+
307+
privatewithinEndPost=(node: NamedNode)=>{
308+
constcompareRes=this.index_.compare(
309+
node,
310+
this.rangedFilter_.getEndPost()
311+
);
312+
returnthis.endIsInclusive_ ? compareRes<=0 : compareRes<0;
313+
};
303314
}

0 commit comments

Comments
 (0)
close