Skip to content

Commit 0af2bdf

Browse files
authored
Fix startAfter/endBefore for orderByKeys queries (#4363)
1 parent d9b945f commit 0af2bdf

File tree

4 files changed

+63
-25
lines changed

4 files changed

+63
-25
lines changed

.changeset/wet-windows-itch.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
'@firebase/database': patch
3+
---
4+
Fixed an issue with startAfter/endBefore when used in orderByKey queries

packages/database/src/api/Query.ts

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -97,25 +97,19 @@ export class Query {
9797
'Query: When ordering by key, you may only pass one argument to ' +
9898
'startAt(), endAt(), or equalTo().';
9999
const wrongArgTypeError =
100-
'Query: When ordering by key, the argument passed to startAt(), endAt(),' +
101-
'or equalTo() must be a string.';
100+
'Query: When ordering by key, the argument passed to startAt(), startAfter(), ' +
101+
'endAt(), endBefore(), or equalTo() must be a string.';
102102
if (params.hasStart()) {
103103
const startName = params.getIndexStartName();
104-
if (
105-
startName !== MIN_NAME &&
106-
!(params.hasStartAfter() && startName === MAX_NAME)
107-
) {
104+
if (startName !== MIN_NAME) {
108105
throw new Error(tooManyArgsError);
109106
} else if (typeof startNode !== 'string') {
110107
throw new Error(wrongArgTypeError);
111108
}
112109
}
113110
if (params.hasEnd()) {
114111
const endName = params.getIndexEndName();
115-
if (
116-
endName !== MAX_NAME &&
117-
!(params.hasEndBefore() && endName === MIN_NAME)
118-
) {
112+
if (endName !== MAX_NAME) {
119113
throw new Error(tooManyArgsError);
120114
} else if (typeof endNode !== 'string') {
121115
throw new Error(wrongArgTypeError);
@@ -128,7 +122,8 @@ export class Query {
128122
) {
129123
throw new Error(
130124
'Query: When ordering by priority, the first argument passed to startAt(), ' +
131-
'endAt(), or equalTo() must be a valid priority value (null, a number, or a string).'
125+
'startAfter() endAt(), endBefore(), or equalTo() must be a valid priority value ' +
126+
'(null, a number, or a string).'
132127
);
133128
}
134129
} else {
@@ -142,8 +137,8 @@ export class Query {
142137
(endNode != null && typeof endNode === 'object')
143138
) {
144139
throw new Error(
145-
'Query: First argument passed to startAt(), endAt(), or equalTo() cannot be ' +
146-
'an object.'
140+
'Query: First argument passed to startAt(), startAfter(), endAt(), endBefore(), or ' +
141+
'equalTo() cannot be an object.'
147142
);
148143
}
149144
}
@@ -162,7 +157,8 @@ export class Query {
162157
!params.hasAnchoredLimit()
163158
) {
164159
throw new Error(
165-
"Query: Can't combine startAt(), endAt(), and limit(). Use limitToFirst() or limitToLast() instead."
160+
"Query: Can't combine startAt(), startAfter(), endAt(), endBefore(), and limit(). Use " +
161+
'limitToFirst() or limitToLast() instead.'
166162
);
167163
}
168164
}
@@ -617,13 +613,13 @@ export class Query {
617613
validateKey('Query.equalTo', 2, name, true);
618614
if (this.queryParams_.hasStart()) {
619615
throw new Error(
620-
'Query.equalTo: Starting point was already set (by another call to startAt or ' +
616+
'Query.equalTo: Starting point was already set (by another call to startAt/startAfter or ' +
621617
'equalTo).'
622618
);
623619
}
624620
if (this.queryParams_.hasEnd()) {
625621
throw new Error(
626-
'Query.equalTo: Ending point was already set (by another call to endAt or ' +
622+
'Query.equalTo: Ending point was already set (by another call to endAt/endBefore or ' +
627623
'equalTo).'
628624
);
629625
}

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

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -289,13 +289,21 @@ export class QueryParams {
289289
}
290290

291291
startAfter(indexValue: unknown, key?: string | null): QueryParams {
292-
let childKey: string;
293-
if (key == null) {
294-
childKey = MAX_NAME;
292+
let params: QueryParams;
293+
if (this.index_ === KEY_INDEX) {
294+
if (typeof indexValue === 'string') {
295+
indexValue = successor(indexValue as string);
296+
}
297+
params = this.startAt(indexValue, key);
295298
} else {
296-
childKey = successor(key);
299+
let childKey: string;
300+
if (key == null) {
301+
childKey = MAX_NAME;
302+
} else {
303+
childKey = successor(key);
304+
}
305+
params = this.startAt(indexValue, childKey);
297306
}
298-
const params: QueryParams = this.startAt(indexValue, childKey);
299307
params.startAfterSet_ = true;
300308
return params;
301309
}
@@ -324,12 +332,20 @@ export class QueryParams {
324332

325333
endBefore(indexValue: unknown, key?: string | null): QueryParams {
326334
let childKey: string;
327-
if (key == null) {
328-
childKey = MIN_NAME;
335+
let params: QueryParams;
336+
if (this.index_ === KEY_INDEX) {
337+
if (typeof indexValue === 'string') {
338+
indexValue = predecessor(indexValue as string);
339+
}
340+
params = this.endAt(indexValue, key);
329341
} else {
330-
childKey = predecessor(key);
342+
if (key == null) {
343+
childKey = MIN_NAME;
344+
} else {
345+
childKey = predecessor(key);
346+
}
347+
params = this.endAt(indexValue, childKey);
331348
}
332-
const params: QueryParams = this.endAt(indexValue, childKey);
333349
params.endBeforeSet_ = true;
334350
return params;
335351
}

packages/database/test/query.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,6 +1377,28 @@ describe('Query Tests', () => {
13771377
expect(removed).to.equal('b ');
13781378
});
13791379

1380+
it('Ensure startAfter on key index works', async () => {
1381+
const node = getRandomNode() as Reference;
1382+
const childOne = node.push();
1383+
const childTwo = node.push();
1384+
await childOne.set(1);
1385+
await childTwo.set(2);
1386+
const snap = await node.orderByKey().startAfter(childOne.key).get();
1387+
expect(Object.keys(snap.val())).to.deep.equal([childTwo.key]);
1388+
expect(Object.values(snap.val())).to.deep.equal([snap.val()[childTwo.key]]);
1389+
});
1390+
1391+
it('Ensure endBefore on key index works', async () => {
1392+
const node = getRandomNode() as Reference;
1393+
const childOne = node.push();
1394+
const childTwo = node.push();
1395+
await childOne.set(1);
1396+
await childTwo.set(2);
1397+
const snap = await node.orderByKey().endBefore(childTwo.key).get();
1398+
expect(Object.keys(snap.val())).to.deep.equal([childOne.key]);
1399+
expect(Object.values(snap.val())).to.deep.equal([snap.val()[childOne.key]]);
1400+
});
1401+
13801402
it('Ensure startAt / endAt with priority works.', async () => {
13811403
const node = getRandomNode() as Reference;
13821404

0 commit comments

Comments
 (0)