Skip to content

Commit 7fedf77

Browse files
test(firestore): cover array pipeline helpers
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 910b625 commit 7fedf77

3 files changed

Lines changed: 275 additions & 94 deletions

File tree

packages/firestore/__tests__/pipelines.test.ts

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ import {
44
arrayFilter,
55
arrayFirst,
66
arrayFirstN,
7+
arrayIndexOf,
78
arrayGet,
9+
arrayLastIndexOf,
10+
arrayMaximum,
11+
arrayMaximumN,
12+
arrayMinimumN,
13+
arrayTransform,
814
and,
915
conditional,
1016
constant,
@@ -154,9 +160,9 @@ describe('Firestore pipelines runtime', function () {
154160
arrayFilter('scores', 'score', greaterThan(variable('score'), constant(15))).as(
155161
'passingScores',
156162
),
157-
field('scores').arrayFilter('score', greaterThan(variable('score'), constant(20))).as(
158-
'topScores',
159-
),
163+
field('scores')
164+
.arrayFilter('score', greaterThan(variable('score'), constant(20)))
165+
.as('topScores'),
160166
)
161167
.serialize();
162168

@@ -207,73 +213,99 @@ describe('Firestore pipelines runtime', function () {
207213
});
208214
});
209215

210-
it('serializes arrayFirst and arrayFirstN as function expression helpers', function () {
216+
it('serializes newer array expression helpers with SDK-compatible arguments', function () {
211217
const db: any = firebase.firestore();
212218
const serialized = db
213219
.pipeline()
214220
.collection('firestore')
215221
.select(
216-
arrayFirst('items').as('firstItem'),
217-
arrayFirstN(field('items'), 2).as('firstTwoItems'),
218-
arrayFirstN('items', field('count')).as('dynamicFirstItems'),
219-
field('items').arrayFirst().as('fluentFirstItem'),
220-
field('items').arrayFirstN(2).as('fluentFirstTwoItems'),
222+
arrayFirst('scores').as('firstScore'),
223+
arrayFirstN('scores', 2).as('firstTwoScores'),
224+
arrayFirstN('scores', field('limit')).as('dynamicFirstScores'),
225+
field('scores').arrayLast().as('lastScore'),
226+
field('scores').arrayLastN(2).as('lastTwoScores'),
227+
field('scores').arraySlice(1, 3).as('middleScores'),
228+
arrayTransform('scores', 'score', variable('score')).as('transformedScores'),
229+
field('scores')
230+
.arrayTransformWithIndex('score', 'index', variable('index'))
231+
.as('indexedScores'),
232+
arrayMaximum('scores').as('maxScore'),
233+
arrayMaximumN(field('scores'), 3).as('topScores'),
234+
field('scores').arrayMinimum().as('minScore'),
235+
arrayMinimumN(field('scores'), 3).as('bottomScores'),
236+
arrayIndexOf('scores', 10).as('firstIndex'),
237+
field('scores').arrayIndexOf(10).as('fluentFirstIndex'),
238+
arrayLastIndexOf(field('scores'), 10).as('lastIndex'),
239+
field('scores').arrayIndexOfAll(10).as('allIndexes'),
221240
)
222241
.serialize();
223242

224243
expect(serialized.stages[0]).toMatchObject({
225244
stage: 'select',
226245
options: {
227246
selections: [
247+
{ alias: 'firstScore', expr: { exprType: 'Function', name: 'arrayFirst' } },
248+
{ alias: 'firstTwoScores', expr: { exprType: 'Function', name: 'arrayFirstN' } },
228249
{
229-
alias: 'firstItem',
230-
expr: {
231-
exprType: 'Function',
232-
name: 'arrayFirst',
233-
args: [{ exprType: 'Field', path: 'items' }],
234-
},
235-
},
236-
{
237-
alias: 'firstTwoItems',
250+
alias: 'dynamicFirstScores',
238251
expr: {
239252
exprType: 'Function',
240253
name: 'arrayFirstN',
241254
args: [
242-
{ exprType: 'Field', path: 'items' },
243-
{ exprType: 'Constant', value: 2 },
255+
{ exprType: 'Field', path: 'scores' },
256+
{ exprType: 'Field', path: 'limit' },
244257
],
245258
},
246259
},
260+
{ alias: 'lastScore', expr: { exprType: 'Function', name: 'arrayLast' } },
261+
{ alias: 'lastTwoScores', expr: { exprType: 'Function', name: 'arrayLastN' } },
262+
{ alias: 'middleScores', expr: { exprType: 'Function', name: 'arraySlice' } },
263+
{ alias: 'transformedScores', expr: { exprType: 'Function', name: 'arrayTransform' } },
264+
{
265+
alias: 'indexedScores',
266+
expr: { exprType: 'Function', name: 'arrayTransformWithIndex' },
267+
},
268+
{ alias: 'maxScore', expr: { exprType: 'Function', name: 'arrayMaximum' } },
269+
{ alias: 'topScores', expr: { exprType: 'Function', name: 'arrayMaximumN' } },
270+
{ alias: 'minScore', expr: { exprType: 'Function', name: 'arrayMinimum' } },
271+
{ alias: 'bottomScores', expr: { exprType: 'Function', name: 'arrayMinimumN' } },
247272
{
248-
alias: 'dynamicFirstItems',
273+
alias: 'firstIndex',
249274
expr: {
250275
exprType: 'Function',
251-
name: 'arrayFirstN',
276+
name: 'arrayIndexOf',
252277
args: [
253-
{ exprType: 'Field', path: 'items' },
254-
{ exprType: 'Field', path: 'count' },
278+
{ exprType: 'Field', path: 'scores' },
279+
{ exprType: 'Constant', value: 10 },
280+
{ exprType: 'Constant', value: 'first' },
255281
],
256282
},
257283
},
258284
{
259-
alias: 'fluentFirstItem',
285+
alias: 'fluentFirstIndex',
260286
expr: {
261287
exprType: 'Function',
262-
name: 'arrayFirst',
263-
args: [{ exprType: 'Field', path: 'items' }],
288+
name: 'arrayIndexOf',
289+
args: [
290+
{ exprType: 'Field', path: 'scores' },
291+
{ exprType: 'Constant', value: 10 },
292+
{ exprType: 'Constant', value: 'first' },
293+
],
264294
},
265295
},
266296
{
267-
alias: 'fluentFirstTwoItems',
297+
alias: 'lastIndex',
268298
expr: {
269299
exprType: 'Function',
270-
name: 'arrayFirstN',
300+
name: 'arrayLastIndexOf',
271301
args: [
272-
{ exprType: 'Field', path: 'items' },
273-
{ exprType: 'Constant', value: 2 },
302+
{ exprType: 'Field', path: 'scores' },
303+
{ exprType: 'Constant', value: 10 },
304+
{ exprType: 'Constant', value: 'last' },
274305
],
275306
},
276307
},
308+
{ alias: 'allIndexes', expr: { exprType: 'Function', name: 'arrayIndexOfAll' } },
277309
],
278310
},
279311
});

packages/firestore/e2e/Pipeline.e2e.js

Lines changed: 97 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,14 +1517,24 @@ describe('FirestorePipeline', function () {
15171517
constant,
15181518
array,
15191519
arrayLength,
1520-
arrayFirst,
1521-
arrayFirstN,
15221520
arrayGet,
15231521
arrayConcat,
15241522
arrayFilter,
1523+
arrayFirst,
1524+
arrayFirstN,
1525+
arrayIndexOf,
1526+
arrayIndexOfAll,
1527+
arrayLastIndexOf,
1528+
arrayMaximum,
1529+
arrayMaximumN,
1530+
arrayMinimumN,
1531+
arraySlice,
1532+
arrayTransform,
1533+
arrayTransformWithIndex,
15251534
arraySum,
15261535
variable,
15271536
and,
1537+
add,
15281538
greaterThan,
15291539
arrayContains,
15301540
arrayContainsAny,
@@ -1540,7 +1550,7 @@ describe('FirestorePipeline', function () {
15401550
permissions: ['read', 'write'],
15411551
primaryTags: ['a', 'b'],
15421552
secondaryTags: ['c', 'd'],
1543-
scores: [10, 20, 30],
1553+
scores: [10, 20, 30, 20],
15441554
items: ['x', 'y', 'z'],
15451555
}),
15461556
setDoc(doc(coll, 'p2'), {
@@ -1573,15 +1583,35 @@ describe('FirestorePipeline', function () {
15731583
arrayFilter(field('scores'), 'score', greaterThan(variable('score'), 15)).as(
15741584
'filteredItems',
15751585
),
1586+
arrayFirst('scores').as('firstScore'),
1587+
arrayFirstN('scores', 2).as('firstTwoScores'),
1588+
field('scores').arrayLast().as('lastScore'),
1589+
field('scores').arrayLastN(2).as('lastTwoScores'),
1590+
arraySlice('scores', 1, 2).as('middleScores'),
1591+
arrayTransform('scores', 'score', add(variable('score'), 1)).as('incrementedScores'),
1592+
arrayTransformWithIndex(
1593+
'scores',
1594+
'score',
1595+
'index',
1596+
add(variable('score'), variable('index')),
1597+
).as('indexedScores'),
1598+
arrayMaximum('scores').as('maxScore'),
1599+
arrayMaximumN('scores', 2).as('topTwoScores'),
1600+
field('scores').arrayMinimum().as('minScore'),
1601+
arrayMinimumN('scores', 2).as('bottomTwoScores'),
1602+
arrayIndexOf('scores', 20).as('firstTwentyIndex'),
1603+
field('scores').arrayIndexOf(20).as('fluentFirstTwentyIndex'),
1604+
arrayLastIndexOf('scores', 20).as('lastTwentyIndex'),
1605+
field('scores').arrayLastIndexOf(20).as('fluentLastTwentyIndex'),
1606+
arrayIndexOfAll('scores', 20).as('allTwentyIndexes'),
15761607
arraySum(field('scores')).as('totalScore'),
15771608
);
15781609

15791610
if (Platform.ios) {
1580-
await expectIOSUnsupportedFunctions(() => execute(pipeline), [
1581-
'arrayFirst',
1582-
'arrayFirstN',
1583-
'arrayGet',
1584-
]);
1611+
await expectIOSUnsupportedFunctions(
1612+
() => execute(pipeline),
1613+
['arrayFirst', 'arrayFirstN', 'arrayGet'],
1614+
);
15851615

15861616
const iosSnapshot = await execute(
15871617
db
@@ -1601,6 +1631,29 @@ describe('FirestorePipeline', function () {
16011631
arrayFilter('scores', 'score', greaterThan(variable('score'), 15)).as(
16021632
'filteredItems',
16031633
),
1634+
arrayFirst('scores').as('firstScore'),
1635+
arrayFirstN('scores', 2).as('firstTwoScores'),
1636+
field('scores').arrayLast().as('lastScore'),
1637+
field('scores').arrayLastN(2).as('lastTwoScores'),
1638+
arraySlice('scores', 1, 2).as('middleScores'),
1639+
arrayTransform('scores', 'score', add(variable('score'), 1)).as(
1640+
'incrementedScores',
1641+
),
1642+
arrayTransformWithIndex(
1643+
'scores',
1644+
'score',
1645+
'index',
1646+
add(variable('score'), variable('index')),
1647+
).as('indexedScores'),
1648+
arrayMaximum('scores').as('maxScore'),
1649+
arrayMaximumN('scores', 2).as('topTwoScores'),
1650+
field('scores').arrayMinimum().as('minScore'),
1651+
arrayMinimumN('scores', 2).as('bottomTwoScores'),
1652+
arrayIndexOf('scores', 20).as('firstTwentyIndex'),
1653+
field('scores').arrayIndexOf(20).as('fluentFirstTwentyIndex'),
1654+
arrayLastIndexOf('scores', 20).as('lastTwentyIndex'),
1655+
field('scores').arrayLastIndexOf(20).as('fluentLastTwentyIndex'),
1656+
arrayIndexOfAll('scores', 20).as('allTwentyIndexes'),
16041657
arraySum(field('scores')).as('totalScore'),
16051658
),
16061659
);
@@ -1610,8 +1663,24 @@ describe('FirestorePipeline', function () {
16101663
iosData.fixedArr.should.eql([1, 2, 3]);
16111664
iosData.tagCount.should.equal(2);
16121665
iosData.allTags.should.eql(['a', 'b', 'c', 'd']);
1613-
iosData.filteredItems.should.eql([20, 30]);
1614-
iosData.totalScore.should.equal(60);
1666+
iosData.filteredItems.should.eql([20, 30, 20]);
1667+
iosData.firstScore.should.equal(10);
1668+
iosData.firstTwoScores.should.eql([10, 20]);
1669+
iosData.lastScore.should.equal(20);
1670+
iosData.lastTwoScores.should.eql([30, 20]);
1671+
iosData.middleScores.should.eql([20, 30]);
1672+
iosData.incrementedScores.should.eql([11, 21, 31, 21]);
1673+
iosData.indexedScores.should.eql([10, 21, 32, 23]);
1674+
iosData.maxScore.should.equal(30);
1675+
[...iosData.topTwoScores].sort((a, b) => a - b).should.eql([20, 30]);
1676+
iosData.minScore.should.equal(10);
1677+
[...iosData.bottomTwoScores].sort((a, b) => a - b).should.eql([10, 20]);
1678+
iosData.firstTwentyIndex.should.equal(1);
1679+
iosData.fluentFirstTwentyIndex.should.equal(1);
1680+
iosData.lastTwentyIndex.should.equal(3);
1681+
iosData.fluentLastTwentyIndex.should.equal(3);
1682+
iosData.allTwentyIndexes.should.eql([1, 3]);
1683+
iosData.totalScore.should.equal(80);
16151684
return;
16161685
}
16171686

@@ -1625,8 +1694,24 @@ describe('FirestorePipeline', function () {
16251694
data.firstTwoItems.should.eql(['x', 'y']);
16261695
data.firstItem.should.equal('x');
16271696
data.allTags.should.eql(['a', 'b', 'c', 'd']);
1628-
data.filteredItems.should.eql([20, 30]);
1629-
data.totalScore.should.equal(60);
1697+
data.filteredItems.should.eql([20, 30, 20]);
1698+
data.firstScore.should.equal(10);
1699+
data.firstTwoScores.should.eql([10, 20]);
1700+
data.lastScore.should.equal(20);
1701+
data.lastTwoScores.should.eql([30, 20]);
1702+
data.middleScores.should.eql([20, 30]);
1703+
data.incrementedScores.should.eql([11, 21, 31, 21]);
1704+
data.indexedScores.should.eql([10, 21, 32, 23]);
1705+
data.maxScore.should.equal(30);
1706+
[...data.topTwoScores].sort((a, b) => a - b).should.eql([20, 30]);
1707+
data.minScore.should.equal(10);
1708+
[...data.bottomTwoScores].sort((a, b) => a - b).should.eql([10, 20]);
1709+
data.firstTwentyIndex.should.equal(1);
1710+
data.fluentFirstTwentyIndex.should.equal(1);
1711+
data.lastTwentyIndex.should.equal(3);
1712+
data.fluentLastTwentyIndex.should.equal(3);
1713+
data.allTwentyIndexes.should.eql([1, 3]);
1714+
data.totalScore.should.equal(80);
16301715
});
16311716
});
16321717

0 commit comments

Comments
 (0)