Skip to content

Commit 07ca9c6

Browse files
Throw on duplicate alias (#9343)
1 parent 0ec6389 commit 07ca9c6

File tree

2 files changed

+70
-4
lines changed

2 files changed

+70
-4
lines changed

packages/firestore/src/util/pipeline_util.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { vector } from '../api';
18+
import { FirestoreError, vector } from '../api';
1919
import {
2020
_constant,
2121
AggregateFunction,
@@ -31,6 +31,7 @@ import {
3131
} from '../lite-api/expressions';
3232
import { VectorValue } from '../lite-api/vector_value';
3333

34+
import { fail } from './assert';
3435
import { isPlainObject } from './input_validation';
3536
import { isFirestoreValue } from './proto';
3637
import { isString } from './types';
@@ -40,13 +41,29 @@ export function selectablesToMap(
4041
): Map<string, Expression> {
4142
const result = new Map<string, Expression>();
4243
for (const selectable of selectables) {
44+
let alias: string;
45+
let expression: Expression;
4346
if (typeof selectable === 'string') {
44-
result.set(selectable as string, field(selectable));
47+
alias = selectable as string;
48+
expression = field(selectable);
4549
} else if (selectable instanceof Field) {
46-
result.set(selectable.alias, selectable.expr);
50+
alias = selectable.alias;
51+
expression = selectable.expr;
4752
} else if (selectable instanceof AliasedExpression) {
48-
result.set(selectable.alias, selectable.expr);
53+
alias = selectable.alias;
54+
expression = selectable.expr;
55+
} else {
56+
fail(0x5319, '`selectable` has an unsupported type', { selectable });
4957
}
58+
59+
if (result.get(alias) !== undefined) {
60+
throw new FirestoreError(
61+
'invalid-argument',
62+
`Duplicate alias or field '${alias}'`
63+
);
64+
}
65+
66+
result.set(alias, expression);
5067
}
5168
return result;
5269
}
@@ -56,6 +73,13 @@ export function aliasedAggregateToMap(
5673
): Map<string, AggregateFunction> {
5774
return aliasedAggregatees.reduce(
5875
(map: Map<string, AggregateFunction>, selectable: AliasedAggregate) => {
76+
if (map.get(selectable.alias) !== undefined) {
77+
throw new FirestoreError(
78+
'invalid-argument',
79+
`Duplicate alias or field '${selectable.alias}'`
80+
);
81+
}
82+
5983
map.set(selectable.alias, selectable.aggregate as AggregateFunction);
6084
return map;
6185
},

packages/firestore/test/integration/api/pipeline.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,27 @@ const timestampDeltaMS = 1000;
879879
});
880880
});
881881

882+
it('throws on Duplicate aliases', async () => {
883+
expect(() =>
884+
firestore
885+
.pipeline()
886+
.collection(randomCol.path)
887+
.aggregate(countAll().as('count'), count('foo').as('count'))
888+
).to.throw("Duplicate alias or field 'count'");
889+
});
890+
891+
it('throws on duplicate group aliases', async () => {
892+
expect(() =>
893+
firestore
894+
.pipeline()
895+
.collection(randomCol.path)
896+
.aggregate({
897+
accumulators: [countAll().as('count')],
898+
groups: ['bax', field('bar').as('bax')]
899+
})
900+
).to.throw("Duplicate alias or field 'bax'");
901+
});
902+
882903
it('supports aggregate options', async () => {
883904
let snapshot = await execute(
884905
firestore
@@ -1081,6 +1102,16 @@ const timestampDeltaMS = 1000;
10811102
);
10821103
});
10831104

1105+
it('throws on Duplicate aliases', async () => {
1106+
expect(() => {
1107+
firestore
1108+
.pipeline()
1109+
.collection(randomCol.path)
1110+
.limit(1)
1111+
.select(constant(1).as('foo'), constant(2).as('foo'));
1112+
}).to.throw("Duplicate alias or field 'foo'");
1113+
});
1114+
10841115
it('supports options', async () => {
10851116
const snapshot = await execute(
10861117
firestore
@@ -1154,6 +1185,17 @@ const timestampDeltaMS = 1000;
11541185
);
11551186
});
11561187

1188+
it('throws on Duplicate aliases', async () => {
1189+
expect(() =>
1190+
firestore
1191+
.pipeline()
1192+
.collection(randomCol.path)
1193+
.select('title', 'author')
1194+
.addFields(constant('bar').as('foo'), constant('baz').as('foo'))
1195+
.sort(field('author').ascending())
1196+
).to.throw("Duplicate alias or field 'foo'");
1197+
});
1198+
11571199
it('supports options', async () => {
11581200
const snapshot = await execute(
11591201
firestore

0 commit comments

Comments
 (0)