Skip to content

Commit 912bb75

Browse files
committed
feat: add new pipeline expressions for regex, string manipulation, and aggregation
1 parent 83cb7b9 commit 912bb75

File tree

1 file changed

+264
-1
lines changed
  • packages/cloud_firestore/cloud_firestore/pipeline_example/lib

1 file changed

+264
-1
lines changed

packages/cloud_firestore/cloud_firestore/pipeline_example/lib/main.dart

Lines changed: 264 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,21 @@ class _PipelineExamplePageState extends State<PipelineExamplePage> {
142142
'b': 2,
143143
'tags': ['p', 'q'],
144144
},
145+
{
146+
'title': 'Regex Email Doc',
147+
'score': 99,
148+
'year': 2024,
149+
'category': 'tech',
150+
'email': 'demo@example.com',
151+
'pi': 3.14159,
152+
},
153+
{
154+
'title': 'Dup Tags',
155+
'score': 8,
156+
'year': 2023,
157+
'category': 'tech',
158+
'tags': ['a', 'b', 'a'],
159+
},
145160
];
146161

147162
for (final item in items) {
@@ -231,6 +246,21 @@ class _PipelineExamplePageState extends State<PipelineExamplePage> {
231246
'b': 2,
232247
'tags': ['p', 'q'],
233248
},
249+
{
250+
'title': 'Regex Email Doc',
251+
'score': 99,
252+
'year': 2024,
253+
'category': 'tech',
254+
'email': 'demo@example.com',
255+
'pi': 3.14159,
256+
},
257+
{
258+
'title': 'Dup Tags',
259+
'score': 8,
260+
'year': 2023,
261+
'category': 'tech',
262+
'tags': ['a', 'b', 'a'],
263+
},
234264
];
235265

236266
for (final item in items) {
@@ -965,6 +995,224 @@ class _PipelineExamplePageState extends State<PipelineExamplePage> {
965995
.execute(),
966996
);
967997

998+
// ── New pipeline expressions (regex, map, string, array, agg) ───────────
999+
1000+
Future<void> _runPipeline47() => _runPipeline(
1001+
'Pipeline 47: where(has email) → addFields regexFind(@.+)',
1002+
() => _firestore
1003+
.pipeline()
1004+
.collection(_collectionId)
1005+
.where(Expression.field('email').exists())
1006+
.addFields(Expression.field('email').regexFind('@.+').as('at_domain'))
1007+
.limit(5)
1008+
.execute(),
1009+
);
1010+
1011+
Future<void> _runPipeline48() => _runPipeline(
1012+
'Pipeline 48: where(has email) → addFields regexFindAll([a-z]+)',
1013+
() => _firestore
1014+
.pipeline()
1015+
.collection(_collectionId)
1016+
.where(Expression.field('email').exists())
1017+
.addFields(
1018+
Expression.field('email').regexFindAll('[a-z]+').as('word_chunks'),
1019+
)
1020+
.limit(5)
1021+
.execute(),
1022+
);
1023+
1024+
Future<void> _runPipeline49() => _runPipeline(
1025+
'Pipeline 49: test=expressions + has s → stringReplaceOne, stringIndexOf, stringRepeat',
1026+
() => _firestore
1027+
.pipeline()
1028+
.collection(_collectionId)
1029+
.where(
1030+
Expression.and(
1031+
Expression.field('test').equalValue('expressions'),
1032+
Expression.field('s').exists(),
1033+
),
1034+
)
1035+
.addFields(
1036+
Expression.field('s').stringReplaceOneLiteral('A', 'Z').as('s_replace_one'),
1037+
Expression.field('s').stringIndexOf('y').as('idx_y'),
1038+
Expression.field('s').stringRepeat(2).as('s_twice'),
1039+
)
1040+
.limit(8)
1041+
.execute(),
1042+
);
1043+
1044+
Future<void> _runPipeline50() => _runPipeline(
1045+
'Pipeline 50: title " Padded " → ltrim, rtrim',
1046+
() => _firestore
1047+
.pipeline()
1048+
.collection(_collectionId)
1049+
.where(Expression.field('title').equalValue(' Padded '))
1050+
.addFields(
1051+
Expression.field('title').ltrim().as('lt'),
1052+
Expression.field('title').rtrim().as('rt'),
1053+
)
1054+
.limit(3)
1055+
.execute(),
1056+
);
1057+
1058+
Future<void> _runPipeline51() => _runPipeline(
1059+
'Pipeline 51: where(has items) → mapSet(z), mapEntries()',
1060+
() => _firestore
1061+
.pipeline()
1062+
.collection(_collectionId)
1063+
.where(Expression.field('items').exists())
1064+
.addFields(
1065+
Expression.field('items').mapSet('z', 'added').as('items_plus'),
1066+
Expression.field('items').mapEntries().as('items_entries'),
1067+
)
1068+
.limit(3)
1069+
.execute(),
1070+
);
1071+
1072+
Future<void> _runPipeline52() => _runPipeline(
1073+
'Pipeline 52: addFields type(score)',
1074+
() => _firestore
1075+
.pipeline()
1076+
.collection(_collectionId)
1077+
.addFields(Expression.field('score').type().as('score_type'))
1078+
.limit(6)
1079+
.execute(),
1080+
);
1081+
1082+
Future<void> _runPipeline53() => _runPipeline(
1083+
'Pipeline 53: where(score isType int64) → select title, score',
1084+
() => _firestore
1085+
.pipeline()
1086+
.collection(_collectionId)
1087+
.where(Expression.field('score').isType('int64'))
1088+
.select(Expression.field('title'), Expression.field('score'))
1089+
.limit(8)
1090+
.execute(),
1091+
);
1092+
1093+
Future<void> _runPipeline54() => _runPipeline(
1094+
'Pipeline 54: where(has pi) → trunc(pi), trunc(2dp), rand()',
1095+
() => _firestore
1096+
.pipeline()
1097+
.collection(_collectionId)
1098+
.where(Expression.field('pi').exists())
1099+
.addFields(
1100+
Expression.field('pi').trunc().as('pi_trunc'),
1101+
Expression.field('pi').trunc(Expression.constant(2)).as('pi_2'),
1102+
Expression.rand().as('rnd'),
1103+
)
1104+
.limit(3)
1105+
.execute(),
1106+
);
1107+
1108+
Future<void> _runPipeline55() => _runPipeline(
1109+
'Pipeline 55: title Item G → arrayFirst, arrayLast(tags)',
1110+
() => _firestore
1111+
.pipeline()
1112+
.collection(_collectionId)
1113+
.where(Expression.field('title').equalValue('Item G'))
1114+
.addFields(
1115+
Expression.field('tags').arrayFirst().as('tag_first'),
1116+
Expression.field('tags').arrayLast().as('tag_last'),
1117+
)
1118+
.limit(3)
1119+
.execute(),
1120+
);
1121+
1122+
Future<void> _runPipeline56() => _runPipeline(
1123+
'Pipeline 56: Item G → arrayFirstN(2), arrayLastN(2)(tags)',
1124+
() => _firestore
1125+
.pipeline()
1126+
.collection(_collectionId)
1127+
.where(Expression.field('title').equalValue('Item G'))
1128+
.addFields(
1129+
Expression.field('tags').arrayFirstN(2).as('tags_head'),
1130+
Expression.field('tags').arrayLastN(2).as('tags_tail'),
1131+
)
1132+
.limit(3)
1133+
.execute(),
1134+
);
1135+
1136+
Future<void> _runPipeline57() => _runPipeline(
1137+
'Pipeline 57: where(has scores) → arrayMaximum, arrayMinimum',
1138+
() => _firestore
1139+
.pipeline()
1140+
.collection(_collectionId)
1141+
.where(Expression.field('scores').exists())
1142+
.addFields(
1143+
Expression.field('scores').arrayMaximum().as('smax'),
1144+
Expression.field('scores').arrayMinimum().as('smin'),
1145+
)
1146+
.limit(3)
1147+
.execute(),
1148+
);
1149+
1150+
Future<void> _runPipeline58() => _runPipeline(
1151+
'Pipeline 58: where(has scores) → arrayMaximumN(2), arrayMinimumN(2)',
1152+
() => _firestore
1153+
.pipeline()
1154+
.collection(_collectionId)
1155+
.where(Expression.field('scores').exists())
1156+
.addFields(
1157+
Expression.field('scores').arrayMaximumN(2).as('top2'),
1158+
Expression.field('scores').arrayMinimumN(2).as('bottom2'),
1159+
)
1160+
.limit(3)
1161+
.execute(),
1162+
);
1163+
1164+
Future<void> _runPipeline59() => _runPipeline(
1165+
'Pipeline 59: Dup Tags → arrayIndexOf(a), arrayLastIndexOf(a), arrayIndexOfAll(a)',
1166+
() => _firestore
1167+
.pipeline()
1168+
.collection(_collectionId)
1169+
.where(Expression.field('title').equalValue('Dup Tags'))
1170+
.addFields(
1171+
Expression.field('tags').arrayIndexOf('a').as('idx_first_a'),
1172+
Expression.field('tags').arrayLastIndexOf('a').as('idx_last_a'),
1173+
Expression.field('tags').arrayIndexOfAll('a').as('all_a'),
1174+
)
1175+
.limit(3)
1176+
.execute(),
1177+
);
1178+
1179+
Future<void> _runPipeline60() => _runPipeline(
1180+
'Pipeline 60: aggregate first(score), last(score)',
1181+
() => _firestore
1182+
.pipeline()
1183+
.collection(_collectionId)
1184+
.limit(50)
1185+
.aggregate(
1186+
Expression.field('score').first().as('first_score'),
1187+
Expression.field('score').last().as('last_score'),
1188+
)
1189+
.execute(),
1190+
);
1191+
1192+
Future<void> _runPipeline61() => _runPipeline(
1193+
'Pipeline 61: limit 25 → aggregate array_agg(title)',
1194+
() => _firestore
1195+
.pipeline()
1196+
.collection(_collectionId)
1197+
.limit(25)
1198+
.aggregate(
1199+
Expression.field('title').arrayAgg().as('all_titles'),
1200+
)
1201+
.execute(),
1202+
);
1203+
1204+
Future<void> _runPipeline62() => _runPipeline(
1205+
'Pipeline 62: limit 25 → aggregate array_agg_distinct(category)',
1206+
() => _firestore
1207+
.pipeline()
1208+
.collection(_collectionId)
1209+
.limit(25)
1210+
.aggregate(
1211+
Expression.field('category').arrayAggDistinct().as('cats'),
1212+
)
1213+
.execute(),
1214+
);
1215+
9681216
@override
9691217
Widget build(BuildContext context) {
9701218
return Scaffold(
@@ -1043,7 +1291,6 @@ class _PipelineExamplePageState extends State<PipelineExamplePage> {
10431291
_btn('24: lower/upper', _runPipeline24),
10441292
_btn('25: trim', _runPipeline25),
10451293
_btn('26: substring', _runPipeline26),
1046-
// test comment
10471294
_btn('28: split', _runPipeline28),
10481295
_btn('29: join', _runPipeline29),
10491296
_btn('30: if_absent', _runPipeline30),
@@ -1064,6 +1311,22 @@ class _PipelineExamplePageState extends State<PipelineExamplePage> {
10641311
_btn('44: notEqualAny', _runPipeline44),
10651312
_btn('45: asBoolean', _runPipeline45),
10661313
_btn('46: isError', _runPipeline46),
1314+
_btn('47: regexFind', _runPipeline47),
1315+
_btn('48: regexFindAll', _runPipeline48),
1316+
_btn('49: string idx/repeat', _runPipeline49),
1317+
_btn('50: ltrim/rtrim', _runPipeline50),
1318+
_btn('51: mapSet/entries', _runPipeline51),
1319+
_btn('52: type(score)', _runPipeline52),
1320+
_btn('53: isType int64', _runPipeline53),
1321+
_btn('54: trunc/rand', _runPipeline54),
1322+
_btn('55: array first/last', _runPipeline55),
1323+
_btn('56: arrayFirstN/LastN', _runPipeline56),
1324+
_btn('57: array max/min', _runPipeline57),
1325+
_btn('58: arrayMaxN/MinN', _runPipeline58),
1326+
_btn('59: arrayIndexOf*', _runPipeline59),
1327+
_btn('60: agg first/last', _runPipeline60),
1328+
_btn('61: agg array_agg', _runPipeline61),
1329+
_btn('62: agg array_agg_dist', _runPipeline62),
10671330
],
10681331
),
10691332
),

0 commit comments

Comments
 (0)