Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
// See also: https://pub.dev/packages/pigeon

#import "FirestoreMessages.g.h"
#import "FLTFirebaseFirestoreReader.h"
#import "FLTFirebaseFirestoreWriter.h"

#if TARGET_OS_OSX
#import <FlutterMacOS/FlutterMacOS.h>
Expand Down Expand Up @@ -168,6 +166,18 @@ + (nullable PigeonQuerySnapshot *)nullableFromList:(NSArray *)list;
- (NSArray *)toList;
@end

@interface PigeonPipelineResult ()
+ (PigeonPipelineResult *)fromList:(NSArray *)list;
+ (nullable PigeonPipelineResult *)nullableFromList:(NSArray *)list;
- (NSArray *)toList;
@end

@interface PigeonPipelineSnapshot ()
+ (PigeonPipelineSnapshot *)fromList:(NSArray *)list;
+ (nullable PigeonPipelineSnapshot *)nullableFromList:(NSArray *)list;
- (NSArray *)toList;
@end

@interface PigeonGetOptions ()
+ (PigeonGetOptions *)fromList:(NSArray *)list;
+ (nullable PigeonGetOptions *)nullableFromList:(NSArray *)list;
Expand Down Expand Up @@ -349,7 +359,7 @@ + (instancetype)makeWithType:(DocumentChangeType)type
pigeonResult.type = type;
pigeonResult.document = document;
pigeonResult.oldIndex = oldIndex;
pigeonResult.index = newIndex;
pigeonResult.newIndex = newIndex;
return pigeonResult;
}
+ (PigeonDocumentChange *)fromList:(NSArray *)list {
Expand All @@ -360,8 +370,8 @@ + (PigeonDocumentChange *)fromList:(NSArray *)list {
NSAssert(pigeonResult.document != nil, @"");
pigeonResult.oldIndex = GetNullableObjectAtIndex(list, 2);
NSAssert(pigeonResult.oldIndex != nil, @"");
pigeonResult.index = GetNullableObjectAtIndex(list, 3);
NSAssert(pigeonResult.index != nil, @"");
pigeonResult.newIndex = GetNullableObjectAtIndex(list, 3);
NSAssert(pigeonResult.newIndex != nil, @"");
return pigeonResult;
}
+ (nullable PigeonDocumentChange *)nullableFromList:(NSArray *)list {
Expand All @@ -372,7 +382,7 @@ - (NSArray *)toList {
@(self.type),
(self.document ? [self.document toList] : [NSNull null]),
(self.oldIndex ?: [NSNull null]),
(self.index ?: [NSNull null]),
(self.newIndex ?: [NSNull null]),
];
}
@end
Expand Down Expand Up @@ -410,6 +420,65 @@ - (NSArray *)toList {
}
@end

@implementation PigeonPipelineResult
+ (instancetype)makeWithDocumentPath:(NSString *)documentPath
createTime:(NSNumber *)createTime
updateTime:(NSNumber *)updateTime {
PigeonPipelineResult *pigeonResult = [[PigeonPipelineResult alloc] init];
pigeonResult.documentPath = documentPath;
pigeonResult.createTime = createTime;
pigeonResult.updateTime = updateTime;
return pigeonResult;
}
+ (PigeonPipelineResult *)fromList:(NSArray *)list {
PigeonPipelineResult *pigeonResult = [[PigeonPipelineResult alloc] init];
pigeonResult.documentPath = GetNullableObjectAtIndex(list, 0);
NSAssert(pigeonResult.documentPath != nil, @"");
pigeonResult.createTime = GetNullableObjectAtIndex(list, 1);
NSAssert(pigeonResult.createTime != nil, @"");
pigeonResult.updateTime = GetNullableObjectAtIndex(list, 2);
NSAssert(pigeonResult.updateTime != nil, @"");
return pigeonResult;
}
+ (nullable PigeonPipelineResult *)nullableFromList:(NSArray *)list {
return (list) ? [PigeonPipelineResult fromList:list] : nil;
}
- (NSArray *)toList {
return @[
(self.documentPath ?: [NSNull null]),
(self.createTime ?: [NSNull null]),
(self.updateTime ?: [NSNull null]),
];
}
@end

@implementation PigeonPipelineSnapshot
+ (instancetype)makeWithResults:(NSArray<PigeonPipelineResult *> *)results
executionTime:(NSNumber *)executionTime {
PigeonPipelineSnapshot *pigeonResult = [[PigeonPipelineSnapshot alloc] init];
pigeonResult.results = results;
pigeonResult.executionTime = executionTime;
return pigeonResult;
}
+ (PigeonPipelineSnapshot *)fromList:(NSArray *)list {
PigeonPipelineSnapshot *pigeonResult = [[PigeonPipelineSnapshot alloc] init];
pigeonResult.results = GetNullableObjectAtIndex(list, 0);
NSAssert(pigeonResult.results != nil, @"");
pigeonResult.executionTime = GetNullableObjectAtIndex(list, 1);
NSAssert(pigeonResult.executionTime != nil, @"");
return pigeonResult;
}
+ (nullable PigeonPipelineSnapshot *)nullableFromList:(NSArray *)list {
return (list) ? [PigeonPipelineSnapshot fromList:list] : nil;
}
- (NSArray *)toList {
return @[
(self.results ?: [NSNull null]),
(self.executionTime ?: [NSNull null]),
];
}
@end

@implementation PigeonGetOptions
+ (instancetype)makeWithSource:(Source)source
serverTimestampBehavior:(ServerTimestampBehavior)serverTimestampBehavior {
Expand Down Expand Up @@ -649,7 +718,7 @@ - (NSArray *)toList {
}
@end

@interface FirebaseFirestoreHostApiCodecReader : FLTFirebaseFirestoreReader
@interface FirebaseFirestoreHostApiCodecReader : FlutterStandardReader
@end
@implementation FirebaseFirestoreHostApiCodecReader
- (nullable id)readValueOfType:(UInt8)type {
Expand All @@ -673,20 +742,24 @@ - (nullable id)readValueOfType:(UInt8)type {
case 136:
return [PigeonGetOptions fromList:[self readValue]];
case 137:
return [PigeonQueryParameters fromList:[self readValue]];
return [PigeonPipelineResult fromList:[self readValue]];
case 138:
return [PigeonQuerySnapshot fromList:[self readValue]];
return [PigeonPipelineSnapshot fromList:[self readValue]];
case 139:
return [PigeonSnapshotMetadata fromList:[self readValue]];
return [PigeonQueryParameters fromList:[self readValue]];
case 140:
return [PigeonQuerySnapshot fromList:[self readValue]];
case 141:
return [PigeonSnapshotMetadata fromList:[self readValue]];
case 142:
return [PigeonTransactionCommand fromList:[self readValue]];
default:
return [super readValueOfType:type];
}
}
@end

@interface FirebaseFirestoreHostApiCodecWriter : FLTFirebaseFirestoreWriter
@interface FirebaseFirestoreHostApiCodecWriter : FlutterStandardWriter
@end
@implementation FirebaseFirestoreHostApiCodecWriter
- (void)writeValue:(id)value {
Expand Down Expand Up @@ -717,18 +790,24 @@ - (void)writeValue:(id)value {
} else if ([value isKindOfClass:[PigeonGetOptions class]]) {
[self writeByte:136];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[PigeonQueryParameters class]]) {
} else if ([value isKindOfClass:[PigeonPipelineResult class]]) {
[self writeByte:137];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[PigeonQuerySnapshot class]]) {
} else if ([value isKindOfClass:[PigeonPipelineSnapshot class]]) {
[self writeByte:138];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[PigeonSnapshotMetadata class]]) {
} else if ([value isKindOfClass:[PigeonQueryParameters class]]) {
[self writeByte:139];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[PigeonTransactionCommand class]]) {
} else if ([value isKindOfClass:[PigeonQuerySnapshot class]]) {
[self writeByte:140];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[PigeonSnapshotMetadata class]]) {
[self writeByte:141];
[self writeValue:[value toList]];
} else if ([value isKindOfClass:[PigeonTransactionCommand class]]) {
[self writeByte:142];
[self writeValue:[value toList]];
} else {
[super writeValue:value];
}
Expand Down Expand Up @@ -1379,4 +1458,32 @@ void FirebaseFirestoreHostApiSetup(id<FlutterBinaryMessenger> binaryMessenger,
[channel setMessageHandler:nil];
}
}
{
FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface."
@"FirebaseFirestoreHostApi.executePipeline"
binaryMessenger:binaryMessenger
codec:FirebaseFirestoreHostApiGetCodec()];
if (api) {
NSCAssert([api respondsToSelector:@selector(executePipelineApp:stages:options:completion:)],
@"FirebaseFirestoreHostApi api (%@) doesn't respond to "
@"@selector(executePipelineApp:stages:options:completion:)",
api);
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
NSArray *args = message;
FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0);
NSArray<NSDictionary<NSString *, id> *> *arg_stages = GetNullableObjectAtIndex(args, 1);
NSDictionary<NSString *, id> *arg_options = GetNullableObjectAtIndex(args, 2);
[api executePipelineApp:arg_app
stages:arg_stages
options:arg_options
completion:^(PigeonPipelineSnapshot *_Nullable output,
FlutterError *_Nullable error) {
callback(wrapResult(output, error));
}];
}];
} else {
[channel setMessageHandler:nil];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ typedef NS_ENUM(NSUInteger, AggregateType) {
@class PigeonDocumentSnapshot;
@class PigeonDocumentChange;
@class PigeonQuerySnapshot;
@class PigeonPipelineResult;
@class PigeonPipelineSnapshot;
@class PigeonGetOptions;
@class PigeonDocumentOption;
@class PigeonTransactionCommand;
Expand Down Expand Up @@ -229,7 +231,7 @@ typedef NS_ENUM(NSUInteger, AggregateType) {
@property(nonatomic, assign) DocumentChangeType type;
@property(nonatomic, strong) PigeonDocumentSnapshot *document;
@property(nonatomic, strong) NSNumber *oldIndex;
@property(nonatomic, strong) NSNumber *index;
@property(nonatomic, strong) NSNumber *newIndex;
@end

@interface PigeonQuerySnapshot : NSObject
Expand All @@ -243,6 +245,26 @@ typedef NS_ENUM(NSUInteger, AggregateType) {
@property(nonatomic, strong) PigeonSnapshotMetadata *metadata;
@end

@interface PigeonPipelineResult : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)makeWithDocumentPath:(NSString *)documentPath
createTime:(NSNumber *)createTime
updateTime:(NSNumber *)updateTime;
@property(nonatomic, copy) NSString *documentPath;
@property(nonatomic, strong) NSNumber *createTime;
@property(nonatomic, strong) NSNumber *updateTime;
@end

@interface PigeonPipelineSnapshot : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)makeWithResults:(NSArray<PigeonPipelineResult *> *)results
executionTime:(NSNumber *)executionTime;
@property(nonatomic, strong) NSArray<PigeonPipelineResult *> *results;
@property(nonatomic, strong) NSNumber *executionTime;
@end

@interface PigeonGetOptions : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
Expand Down Expand Up @@ -416,6 +438,11 @@ NSObject<FlutterMessageCodec> *FirebaseFirestoreHostApiGetCodec(void);
- (void)persistenceCacheIndexManagerRequestApp:(FirestorePigeonFirebaseApp *)app
request:(PersistenceCacheIndexManagerRequest)request
completion:(void (^)(FlutterError *_Nullable))completion;
- (void)executePipelineApp:(FirestorePigeonFirebaseApp *)app
stages:(NSArray<NSDictionary<NSString *, id> *> *)stages
options:(nullable NSDictionary<NSString *, id> *)options
completion:(void (^)(PigeonPipelineSnapshot *_Nullable,
FlutterError *_Nullable))completion;
@end

extern void FirebaseFirestoreHostApiSetup(id<FlutterBinaryMessenger> binaryMessenger,
Expand Down
10 changes: 10 additions & 0 deletions packages/cloud_firestore/cloud_firestore/lib/cloud_firestore.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,17 @@ part 'src/filters.dart';
part 'src/firestore.dart';
part 'src/load_bundle_task.dart';
part 'src/load_bundle_task_snapshot.dart';
part 'pipeline_snapshot.dart';
part 'src/persistent_cache_index_manager.dart';
part 'src/pipeline.dart';
part 'src/pipeline_aggregate.dart';
part 'src/pipeline_distance.dart';
part 'src/pipeline_execute_options.dart';
part 'src/pipeline_expression.dart';
part 'src/pipeline_ordering.dart';
part 'src/pipeline_sample.dart';
part 'src/pipeline_source.dart';
part 'src/pipeline_stage.dart';
part 'src/query.dart';
part 'src/query_document_snapshot.dart';
part 'src/query_snapshot.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2026, the Chromium project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

part of 'cloud_firestore.dart';

/// Result of executing a pipeline
class PipelineResult {
final DocumentReference<Map<String, dynamic>> document;
final DateTime createTime;
final DateTime updateTime;

PipelineResult({
required this.document,
required this.createTime,
required this.updateTime,
});
}

/// Snapshot containing pipeline execution results
class PipelineSnapshot {
final List<PipelineResult> result;
final DateTime executionTime;

PipelineSnapshot._(this.result, this.executionTime);
}
20 changes: 20 additions & 0 deletions packages/cloud_firestore/cloud_firestore/lib/src/firestore.dart
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,26 @@ class FirebaseFirestore extends FirebasePluginPlatform {
return null;
}

/// Returns a [PipelineSource] for creating and executing pipelines.
///
/// Pipelines allow you to perform complex queries and transformations on
/// Firestore data using a fluent API.
///
/// Example:
/// ```dart
/// final snapshot = await FirebaseFirestore.instance
/// .pipeline()
/// .collection('users')
/// .where(PipelineFilter(Field('age'), isGreaterThan: 18))
/// .sort(Field('name').ascending())
/// .limit(10)
/// .execute();
/// ```
// ignore: use_to_and_as_if_applicable
PipelineSource pipeline() {
return PipelineSource._(this);
}

/// Configures indexing for local query execution. Any previous index configuration is overridden.
///
/// The index entries themselves are created asynchronously. You can continue to use queries that
Expand Down
Loading
Loading