diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestorePlugin.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestorePlugin.java index 35e4697f7128..f96a91c43164 100644 --- a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestorePlugin.java +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestorePlugin.java @@ -859,6 +859,34 @@ public void aggregateQuery( }); } + @Override + public void findNearest(@NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, @NonNull String path, @NonNull Boolean isCollectionGroup, @NonNull GeneratedAndroidFirebaseFirestore.PigeonQueryParameters parameters, @NonNull List queryVector, @NonNull GeneratedAndroidFirebaseFirestore.VectorSource source, @NonNull Long limit, @NonNull GeneratedAndroidFirebaseFirestore.VectorQueryOptions queryOptions, @NonNull GeneratedAndroidFirebaseFirestore.DistanceMeasure distanceMeasure, @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + Query query = PigeonParser.parseQuery(getFirestoreFromPigeon(app), path, isCollectionGroup, parameters); + + if (query == null) { + result.error( + new GeneratedAndroidFirebaseFirestore.FlutterError( + "invalid_query", + "An error occurred while parsing query arguments, see native logs for more information. Please report this issue.", + null)); + return; + } + + cachedThreadPool.execute( + () -> { + try { + QuerySnapshot querySnapshot = Tasks.await(query.findNearest(queryVector, PigeonParser.parseVectorSource(source), limit, PigeonParser.parseVectorQueryOptions(queryOptions), PigeonParser.parseDistanceMeasure(distanceMeasure))); + + result.success( + PigeonParser.toPigeonQuerySnapshot( + querySnapshot, + DocumentSnapshot.ServerTimestampBehavior.NONE)); + } catch (Exception e) { + ExceptionConverter.sendErrorToFlutter(result, e); + } + }); + } + @Override public void writeBatchCommit( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/GeneratedAndroidFirebaseFirestore.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/GeneratedAndroidFirebaseFirestore.java index f7d24bc7c7ec..26732ac221b7 100644 --- a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/GeneratedAndroidFirebaseFirestore.java +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/GeneratedAndroidFirebaseFirestore.java @@ -104,6 +104,38 @@ private Source(final int index) { } } + /** An enumeration of firestore source types. */ + public enum VectorSource { + /** + * Causes Firestore to avoid the cache, generating an error if the server cannot be reached. + * Note that the cache will still be updated if the server request succeeds. Also note that + * latency-compensation still takes effect, so any pending write operations will be visible in + * the returned data (merged into the server-provided data). + */ + SERVER(0); + + final int index; + + private VectorSource(final int index) { + this.index = index; + } + } + + public enum DistanceMeasure { + /** The cosine similarity measure. */ + COSINE(0), + /** The euclidean distance measure. */ + EUCLIDEAN(1), + /** The dot product distance measure. */ + DOT_PRODUCT(2); + + final int index; + + private DistanceMeasure(final int index) { + this.index = index; + } + } + /** * The listener retrieves data and listens to updates from the local Firestore cache only. If the * cache is empty, an empty snapshot will be returned. Snapshot events will be triggered on cache @@ -856,6 +888,79 @@ public ArrayList toList() { } } + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class VectorQueryOptions { + private @NonNull String distanceResultField; + + public @NonNull String getDistanceResultField() { + return distanceResultField; + } + + public void setDistanceResultField(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"distanceResultField\" is null."); + } + this.distanceResultField = setterArg; + } + + private @NonNull Double distanceThreshold; + + public @NonNull Double getDistanceThreshold() { + return distanceThreshold; + } + + public void setDistanceThreshold(@NonNull Double setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"distanceThreshold\" is null."); + } + this.distanceThreshold = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + VectorQueryOptions() {} + + public static final class Builder { + + private @Nullable String distanceResultField; + + public @NonNull Builder setDistanceResultField(@NonNull String setterArg) { + this.distanceResultField = setterArg; + return this; + } + + private @Nullable Double distanceThreshold; + + public @NonNull Builder setDistanceThreshold(@NonNull Double setterArg) { + this.distanceThreshold = setterArg; + return this; + } + + public @NonNull VectorQueryOptions build() { + VectorQueryOptions pigeonReturn = new VectorQueryOptions(); + pigeonReturn.setDistanceResultField(distanceResultField); + pigeonReturn.setDistanceThreshold(distanceThreshold); + return pigeonReturn; + } + } + + @NonNull + public ArrayList toList() { + ArrayList toListResult = new ArrayList(2); + toListResult.add(distanceResultField); + toListResult.add(distanceThreshold); + return toListResult; + } + + static @NonNull VectorQueryOptions fromList(@NonNull ArrayList list) { + VectorQueryOptions pigeonResult = new VectorQueryOptions(); + Object distanceResultField = list.get(0); + pigeonResult.setDistanceResultField((String) distanceResultField); + Object distanceThreshold = list.get(1); + pigeonResult.setDistanceThreshold((Double) distanceThreshold); + return pigeonResult; + } + } + /** Generated class from Pigeon that represents data sent in messages. */ public static final class PigeonGetOptions { private @NonNull Source source; @@ -1667,6 +1772,8 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { return PigeonSnapshotMetadata.fromList((ArrayList) readValue(buffer)); case (byte) 140: return PigeonTransactionCommand.fromList((ArrayList) readValue(buffer)); + case (byte) 141: + return VectorQueryOptions.fromList((ArrayList) readValue(buffer)); default: return super.readValueOfType(type, buffer); } @@ -1713,6 +1820,9 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { } else if (value instanceof PigeonTransactionCommand) { stream.write(140); writeValue(stream, ((PigeonTransactionCommand) value).toList()); + } else if (value instanceof VectorQueryOptions) { + stream.write(141); + writeValue(stream, ((VectorQueryOptions) value).toList()); } else { super.writeValue(stream, value); } @@ -1809,6 +1919,18 @@ void aggregateQuery( @NonNull Boolean isCollectionGroup, @NonNull Result> result); + void findNearest( + @NonNull FirestorePigeonFirebaseApp app, + @NonNull String path, + @NonNull Boolean isCollectionGroup, + @NonNull PigeonQueryParameters parameters, + @NonNull List queryVector, + @NonNull VectorSource source, + @NonNull Long limit, + @NonNull VectorQueryOptions queryOptions, + @NonNull DistanceMeasure distanceMeasure, + @NonNull Result result); + void writeBatchCommit( @NonNull FirestorePigeonFirebaseApp app, @NonNull List writes, @@ -2478,6 +2600,55 @@ public void error(Throwable error) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.findNearest", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); + String pathArg = (String) args.get(1); + Boolean isCollectionGroupArg = (Boolean) args.get(2); + PigeonQueryParameters parametersArg = (PigeonQueryParameters) args.get(3); + List queryVectorArg = (List) args.get(4); + VectorSource sourceArg = VectorSource.values()[(int) args.get(5)]; + Number limitArg = (Number) args.get(6); + VectorQueryOptions queryOptionsArg = (VectorQueryOptions) args.get(7); + DistanceMeasure distanceMeasureArg = DistanceMeasure.values()[(int) args.get(8)]; + Result resultCallback = + new Result() { + public void success(PigeonQuerySnapshot result) { + wrapped.add(0, result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); + } + }; + + api.findNearest( + appArg, + pathArg, + isCollectionGroupArg, + parametersArg, + queryVectorArg, + sourceArg, + (limitArg == null) ? null : limitArg.longValue(), + queryOptionsArg, + distanceMeasureArg, + resultCallback); + }); + } else { + channel.setMessageHandler(null); + } + } { BasicMessageChannel channel = new BasicMessageChannel<>( diff --git a/packages/cloud_firestore/cloud_firestore/ios/Classes/FirestoreMessages.g.m b/packages/cloud_firestore/cloud_firestore/ios/Classes/FirestoreMessages.g.m index 34e717b694a7..cc7e8e2cc99d 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/Classes/FirestoreMessages.g.m +++ b/packages/cloud_firestore/cloud_firestore/ios/Classes/FirestoreMessages.g.m @@ -40,6 +40,27 @@ - (instancetype)initWithValue:(Source)value { } @end +/// An enumeration of firestore source types. +@implementation VectorSourceBox +- (instancetype)initWithValue:(VectorSource)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +@implementation DistanceMeasureBox +- (instancetype)initWithValue:(DistanceMeasure)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + /// The listener retrieves data and listens to updates from the local Firestore cache only. /// If the cache is empty, an empty snapshot will be returned. /// Snapshot events will be triggered on cache updates, like local mutations or load bundles. @@ -168,6 +189,12 @@ + (nullable PigeonQuerySnapshot *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface VectorQueryOptions () ++ (VectorQueryOptions *)fromList:(NSArray *)list; ++ (nullable VectorQueryOptions *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface PigeonGetOptions () + (PigeonGetOptions *)fromList:(NSArray *)list; + (nullable PigeonGetOptions *)nullableFromList:(NSArray *)list; @@ -410,6 +437,33 @@ - (NSArray *)toList { } @end +@implementation VectorQueryOptions ++ (instancetype)makeWithDistanceResultField:(NSString *)distanceResultField + distanceThreshold:(NSNumber *)distanceThreshold { + VectorQueryOptions *pigeonResult = [[VectorQueryOptions alloc] init]; + pigeonResult.distanceResultField = distanceResultField; + pigeonResult.distanceThreshold = distanceThreshold; + return pigeonResult; +} ++ (VectorQueryOptions *)fromList:(NSArray *)list { + VectorQueryOptions *pigeonResult = [[VectorQueryOptions alloc] init]; + pigeonResult.distanceResultField = GetNullableObjectAtIndex(list, 0); + NSAssert(pigeonResult.distanceResultField != nil, @""); + pigeonResult.distanceThreshold = GetNullableObjectAtIndex(list, 1); + NSAssert(pigeonResult.distanceThreshold != nil, @""); + return pigeonResult; +} ++ (nullable VectorQueryOptions *)nullableFromList:(NSArray *)list { + return (list) ? [VectorQueryOptions fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.distanceResultField ?: [NSNull null]), + (self.distanceThreshold ?: [NSNull null]), + ]; +} +@end + @implementation PigeonGetOptions + (instancetype)makeWithSource:(Source)source serverTimestampBehavior:(ServerTimestampBehavior)serverTimestampBehavior { @@ -680,6 +734,8 @@ - (nullable id)readValueOfType:(UInt8)type { return [PigeonSnapshotMetadata fromList:[self readValue]]; case 140: return [PigeonTransactionCommand fromList:[self readValue]]; + case 141: + return [VectorQueryOptions fromList:[self readValue]]; default: return [super readValueOfType:type]; } @@ -729,6 +785,9 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[PigeonTransactionCommand class]]) { [self writeByte:140]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[VectorQueryOptions class]]) { + [self writeByte:141]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -1255,6 +1314,50 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.findNearest" + binaryMessenger:binaryMessenger + codec:FirebaseFirestoreHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (findNearestApp: + path:isCollectionGroup:parameters:queryVector:source:limit + :queryOptions:distanceMeasure:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(findNearestApp:path:isCollectionGroup:parameters:queryVector:source:" + @"limit:queryOptions:distanceMeasure:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_path = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_isCollectionGroup = GetNullableObjectAtIndex(args, 2); + PigeonQueryParameters *arg_parameters = GetNullableObjectAtIndex(args, 3); + NSArray *arg_queryVector = GetNullableObjectAtIndex(args, 4); + VectorSource arg_source = [GetNullableObjectAtIndex(args, 5) integerValue]; + NSNumber *arg_limit = GetNullableObjectAtIndex(args, 6); + VectorQueryOptions *arg_queryOptions = GetNullableObjectAtIndex(args, 7); + DistanceMeasure arg_distanceMeasure = [GetNullableObjectAtIndex(args, 8) integerValue]; + [api findNearestApp:arg_app + path:arg_path + isCollectionGroup:arg_isCollectionGroup + parameters:arg_parameters + queryVector:arg_queryVector + source:arg_source + limit:arg_limit + queryOptions:arg_queryOptions + distanceMeasure:arg_distanceMeasure + completion:^(PigeonQuerySnapshot *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." diff --git a/packages/cloud_firestore/cloud_firestore/ios/Classes/Public/FirestoreMessages.g.h b/packages/cloud_firestore/cloud_firestore/ios/Classes/Public/FirestoreMessages.g.h index 2eabaeaef25f..c4ea951329d5 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/Classes/Public/FirestoreMessages.g.h +++ b/packages/cloud_firestore/cloud_firestore/ios/Classes/Public/FirestoreMessages.g.h @@ -57,6 +57,37 @@ typedef NS_ENUM(NSUInteger, Source) { - (instancetype)initWithValue:(Source)value; @end +/// An enumeration of firestore source types. +typedef NS_ENUM(NSUInteger, VectorSource) { + /// Causes Firestore to avoid the cache, generating an error if the server cannot be reached. Note + /// that the cache will still be updated if the server request succeeds. Also note that + /// latency-compensation still takes effect, so any pending write operations will be visible in + /// the + /// returned data (merged into the server-provided data). + VectorSourceServer = 0, +}; + +/// Wrapper for VectorSource to allow for nullability. +@interface VectorSourceBox : NSObject +@property(nonatomic, assign) VectorSource value; +- (instancetype)initWithValue:(VectorSource)value; +@end + +typedef NS_ENUM(NSUInteger, DistanceMeasure) { + /// The cosine similarity measure. + DistanceMeasureCosine = 0, + /// The euclidean distance measure. + DistanceMeasureEuclidean = 1, + /// The dot product distance measure. + DistanceMeasureDotProduct = 2, +}; + +/// Wrapper for DistanceMeasure to allow for nullability. +@interface DistanceMeasureBox : NSObject +@property(nonatomic, assign) DistanceMeasure value; +- (instancetype)initWithValue:(DistanceMeasure)value; +@end + /// The listener retrieves data and listens to updates from the local Firestore cache only. /// If the cache is empty, an empty snapshot will be returned. /// Snapshot events will be triggered on cache updates, like local mutations or load bundles. @@ -165,6 +196,7 @@ typedef NS_ENUM(NSUInteger, AggregateType) { @class PigeonDocumentSnapshot; @class PigeonDocumentChange; @class PigeonQuerySnapshot; +@class VectorQueryOptions; @class PigeonGetOptions; @class PigeonDocumentOption; @class PigeonTransactionCommand; @@ -243,6 +275,15 @@ typedef NS_ENUM(NSUInteger, AggregateType) { @property(nonatomic, strong) PigeonSnapshotMetadata *metadata; @end +@interface VectorQueryOptions : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithDistanceResultField:(NSString *)distanceResultField + distanceThreshold:(NSNumber *)distanceThreshold; +@property(nonatomic, copy) NSString *distanceResultField; +@property(nonatomic, strong) NSNumber *distanceThreshold; +@end + @interface PigeonGetOptions : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; @@ -396,6 +437,17 @@ NSObject *FirebaseFirestoreHostApiGetCodec(void); isCollectionGroup:(NSNumber *)isCollectionGroup completion:(void (^)(NSArray *_Nullable, FlutterError *_Nullable))completion; +- (void)findNearestApp:(FirestorePigeonFirebaseApp *)app + path:(NSString *)path + isCollectionGroup:(NSNumber *)isCollectionGroup + parameters:(PigeonQueryParameters *)parameters + queryVector:(NSArray *)queryVector + source:(VectorSource)source + limit:(NSNumber *)limit + queryOptions:(VectorQueryOptions *)queryOptions + distanceMeasure:(DistanceMeasure)distanceMeasure + completion: + (void (^)(PigeonQuerySnapshot *_Nullable, FlutterError *_Nullable))completion; - (void)writeBatchCommitApp:(FirestorePigeonFirebaseApp *)app writes:(NSArray *)writes completion:(void (^)(FlutterError *_Nullable))completion; diff --git a/packages/cloud_firestore/cloud_firestore/lib/cloud_firestore.dart b/packages/cloud_firestore/cloud_firestore/lib/cloud_firestore.dart index 60072efbcf3b..a5e47f71bd8b 100755 --- a/packages/cloud_firestore/cloud_firestore/lib/cloud_firestore.dart +++ b/packages/cloud_firestore/cloud_firestore/lib/cloud_firestore.dart @@ -30,6 +30,7 @@ export 'package:cloud_firestore_platform_interface/cloud_firestore_platform_inte ServerTimestampBehavior, SetOptions, ListenSource, + VectorSource, DocumentChangeType, PersistenceSettings, Settings, @@ -59,11 +60,13 @@ part 'src/filters.dart'; part 'src/firestore.dart'; part 'src/load_bundle_task.dart'; part 'src/load_bundle_task_snapshot.dart'; +part 'src/persistent_cache_index_manager.dart'; part 'src/query.dart'; part 'src/query_document_snapshot.dart'; part 'src/query_snapshot.dart'; part 'src/snapshot_metadata.dart'; part 'src/transaction.dart'; part 'src/utils/codec_utility.dart'; +part 'src/vector_query.dart'; +part 'src/vector_query_snapshot.dart'; part 'src/write_batch.dart'; -part 'src/persistent_cache_index_manager.dart'; diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/query.dart b/packages/cloud_firestore/cloud_firestore/lib/src/query.dart index cefa3c75883f..9b723840b24d 100644 --- a/packages/cloud_firestore/cloud_firestore/lib/src/query.dart +++ b/packages/cloud_firestore/cloud_firestore/lib/src/query.dart @@ -226,6 +226,15 @@ abstract class Query { AggregateField? aggregateField29, AggregateField? aggregateField30, ]); + + VectorQuery findNearest( + String field, { + /// List or VectorValue + required Object queryVector, + required int limit, + required DistanceMeasure distanceMeasure, + required VectorQueryOptions options, + }); } /// Represents a [Query] over the data at a particular location. @@ -900,6 +909,28 @@ class _JsonQuery implements Query> { this, ); } + + @override + VectorQuery findNearest( + String field, { + /// List or VectorValue + required Object queryVector, + required int limit, + required DistanceMeasure distanceMeasure, + required VectorQueryOptions options, + }) { + return VectorQuery._( + firestore, + _delegate.findNearest( + field, + queryVector: queryVector, + limit: limit, + distanceMeasure: distanceMeasure, + options: options, + ), + this, + ); + } } class _WithConverterQuery implements Query { @@ -1144,4 +1175,22 @@ class _WithConverterQuery implements Query { aggregateField30, ); } + + @override + VectorQuery findNearest( + String field, { + /// List or VectorValue + required Object queryVector, + required int limit, + required DistanceMeasure distanceMeasure, + required VectorQueryOptions options, + }) { + return _originalQuery.findNearest( + field, + queryVector: queryVector, + limit: limit, + distanceMeasure: distanceMeasure, + options: options, + ); + } } diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/vector_query.dart b/packages/cloud_firestore/cloud_firestore/lib/src/vector_query.dart new file mode 100644 index 000000000000..229b5ec9b9f1 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/lib/src/vector_query.dart @@ -0,0 +1,31 @@ +// Copyright 2022, 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; + +/// [VectorQuery] represents the data at a particular location for retrieving metadata +/// without retrieving the actual documents. +class VectorQuery { + VectorQuery._(this._firestore, this._delegate, this.query) { + VectorQueryPlatform.verify(_delegate); + } + + final FirebaseFirestore _firestore; + + /// [Query] represents the query over the data at a particular location used by the [VectorQuery] to + /// retrieve the metadata. + final Query query; + + final VectorQueryPlatform _delegate; + + /// Returns an [VectorQuerySnapshot] with the count of the documents that match the query. + Future get({ + VectorSource source = VectorSource.server, + }) async { + return VectorQuerySnapshot._( + _firestore, + await _delegate.get(source: source), + ); + } +} diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/vector_query_snapshot.dart b/packages/cloud_firestore/cloud_firestore/lib/src/vector_query_snapshot.dart new file mode 100644 index 000000000000..6e7da44306fd --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/lib/src/vector_query_snapshot.dart @@ -0,0 +1,13 @@ +// Copyright 2022, 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; + +/// [VectorQuerySnapshot] represents a response to an [VectorQuery] request. +class VectorQuerySnapshot extends _JsonQuerySnapshot { + VectorQuerySnapshot._( + super.firestore, + super.delegate, + ) : super(); +} diff --git a/packages/cloud_firestore/cloud_firestore/windows/messages.g.cpp b/packages/cloud_firestore/cloud_firestore/windows/messages.g.cpp index bb9f58d5775d..8c42e72e15cc 100644 --- a/packages/cloud_firestore/cloud_firestore/windows/messages.g.cpp +++ b/packages/cloud_firestore/cloud_firestore/windows/messages.g.cpp @@ -397,6 +397,44 @@ PigeonQuerySnapshot PigeonQuerySnapshot::FromEncodableList( return decoded; } +// VectorQueryOptions + +VectorQueryOptions::VectorQueryOptions(const std::string& distance_result_field, + double distance_threshold) + : distance_result_field_(distance_result_field), + distance_threshold_(distance_threshold) {} + +const std::string& VectorQueryOptions::distance_result_field() const { + return distance_result_field_; +} + +void VectorQueryOptions::set_distance_result_field(std::string_view value_arg) { + distance_result_field_ = value_arg; +} + +double VectorQueryOptions::distance_threshold() const { + return distance_threshold_; +} + +void VectorQueryOptions::set_distance_threshold(double value_arg) { + distance_threshold_ = value_arg; +} + +EncodableList VectorQueryOptions::ToEncodableList() const { + EncodableList list; + list.reserve(2); + list.push_back(EncodableValue(distance_result_field_)); + list.push_back(EncodableValue(distance_threshold_)); + return list; +} + +VectorQueryOptions VectorQueryOptions::FromEncodableList( + const EncodableList& list) { + VectorQueryOptions decoded(std::get(list[0]), + std::get(list[1])); + return decoded; +} + // PigeonGetOptions PigeonGetOptions::PigeonGetOptions( @@ -1045,6 +1083,9 @@ EncodableValue FirebaseFirestoreHostApiCodecSerializer::ReadValueOfType( case 140: return CustomEncodableValue(PigeonTransactionCommand::FromEncodableList( std::get(ReadValue(stream)))); + case 141: + return CustomEncodableValue(VectorQueryOptions::FromEncodableList( + std::get(ReadValue(stream)))); default: return cloud_firestore_windows::FirestoreCodec::ReadValueOfType(type, stream); @@ -1159,6 +1200,13 @@ void FirebaseFirestoreHostApiCodecSerializer::WriteValue( stream); return; } + if (custom_value->type() == typeid(VectorQueryOptions)) { + stream->WriteByte(141); + WriteValue(EncodableValue(std::any_cast(*custom_value) + .ToEncodableList()), + stream); + return; + } } cloud_firestore_windows::FirestoreCodec::WriteValue(value, stream); } @@ -2072,6 +2120,105 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, channel->SetMessageHandler(nullptr); } } + { + auto channel = std::make_unique>( + binary_messenger, + "dev.flutter.pigeon.cloud_firestore_platform_interface." + "FirebaseFirestoreHostApi.findNearest", + &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_arg = args.at(0); + if (encodable_app_arg.IsNull()) { + reply(WrapError("app_arg unexpectedly null.")); + return; + } + const auto& app_arg = + std::any_cast( + std::get(encodable_app_arg)); + const auto& encodable_path_arg = args.at(1); + if (encodable_path_arg.IsNull()) { + reply(WrapError("path_arg unexpectedly null.")); + return; + } + const auto& path_arg = std::get(encodable_path_arg); + const auto& encodable_is_collection_group_arg = args.at(2); + if (encodable_is_collection_group_arg.IsNull()) { + reply(WrapError("is_collection_group_arg unexpectedly null.")); + return; + } + const auto& is_collection_group_arg = + std::get(encodable_is_collection_group_arg); + const auto& encodable_parameters_arg = args.at(3); + if (encodable_parameters_arg.IsNull()) { + reply(WrapError("parameters_arg unexpectedly null.")); + return; + } + const auto& parameters_arg = + std::any_cast( + std::get(encodable_parameters_arg)); + const auto& encodable_query_vector_arg = args.at(4); + if (encodable_query_vector_arg.IsNull()) { + reply(WrapError("query_vector_arg unexpectedly null.")); + return; + } + const auto& query_vector_arg = + std::get(encodable_query_vector_arg); + const auto& encodable_source_arg = args.at(5); + if (encodable_source_arg.IsNull()) { + reply(WrapError("source_arg unexpectedly null.")); + return; + } + const VectorSource& source_arg = + (VectorSource)encodable_source_arg.LongValue(); + const auto& encodable_limit_arg = args.at(6); + if (encodable_limit_arg.IsNull()) { + reply(WrapError("limit_arg unexpectedly null.")); + return; + } + const int64_t limit_arg = encodable_limit_arg.LongValue(); + const auto& encodable_query_options_arg = args.at(7); + if (encodable_query_options_arg.IsNull()) { + reply(WrapError("query_options_arg unexpectedly null.")); + return; + } + const auto& query_options_arg = + std::any_cast( + std::get( + encodable_query_options_arg)); + const auto& encodable_distance_measure_arg = args.at(8); + if (encodable_distance_measure_arg.IsNull()) { + reply(WrapError("distance_measure_arg unexpectedly null.")); + return; + } + const DistanceMeasure& distance_measure_arg = + (DistanceMeasure)encodable_distance_measure_arg.LongValue(); + api->FindNearest( + app_arg, path_arg, is_collection_group_arg, parameters_arg, + query_vector_arg, source_arg, limit_arg, query_options_arg, + distance_measure_arg, + [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + CustomEncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } { auto channel = std::make_unique>( binary_messenger, @@ -2290,8 +2437,8 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, reply(WrapError("request_arg unexpectedly null.")); return; } - const PersistenceCacheIndexManagerRequestEnum& request_arg = - (PersistenceCacheIndexManagerRequestEnum) + const PersistenceCacheIndexManagerRequest& request_arg = + (PersistenceCacheIndexManagerRequest) encodable_request_arg.LongValue(); api->PersistenceCacheIndexManagerRequest( app_arg, request_arg, diff --git a/packages/cloud_firestore/cloud_firestore/windows/messages.g.h b/packages/cloud_firestore/cloud_firestore/windows/messages.g.h index 8aecb887facc..c7ff8a7b2ce7 100644 --- a/packages/cloud_firestore/cloud_firestore/windows/messages.g.h +++ b/packages/cloud_firestore/cloud_firestore/windows/messages.g.h @@ -96,6 +96,27 @@ enum class Source { cache = 2 }; +// An enumeration of firestore source types. +enum class VectorSource { + // Causes Firestore to avoid the cache, generating an error if the server + // cannot be reached. Note + // that the cache will still be updated if the server request succeeds. Also + // note that + // latency-compensation still takes effect, so any pending write operations + // will be visible in the + // returned data (merged into the server-provided data). + server = 0 +}; + +enum class DistanceMeasure { + // The cosine similarity measure. + cosine = 0, + // The euclidean distance measure. + euclidean = 1, + // The dot product distance measure. + dotProduct = 2 +}; + // The listener retrieves data and listens to updates from the local Firestore // cache only. If the cache is empty, an empty snapshot will be returned. // Snapshot events will be triggered on cache updates, like local mutations or @@ -136,7 +157,7 @@ enum class AggregateSource { // [PersistenceCacheIndexManagerRequest] represents the request types for the // persistence cache index manager. -enum class PersistenceCacheIndexManagerRequestEnum { +enum class PersistenceCacheIndexManagerRequest { enableIndexAutoCreation = 0, disableIndexAutoCreation = 1, deleteAllIndexes = 2 @@ -348,6 +369,29 @@ class PigeonQuerySnapshot { PigeonSnapshotMetadata metadata_; }; +// Generated class from Pigeon that represents data sent in messages. +class VectorQueryOptions { + public: + // Constructs an object setting all fields. + explicit VectorQueryOptions(const std::string& distance_result_field, + double distance_threshold); + + const std::string& distance_result_field() const; + void set_distance_result_field(std::string_view value_arg); + + double distance_threshold() const; + void set_distance_threshold(double value_arg); + + private: + static VectorQueryOptions FromEncodableList( + const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class FirebaseFirestoreHostApi; + friend class FirebaseFirestoreHostApiCodecSerializer; + std::string distance_result_field_; + double distance_threshold_; +}; + // Generated class from Pigeon that represents data sent in messages. class PigeonGetOptions { public: @@ -707,6 +751,13 @@ class FirebaseFirestoreHostApi { const PigeonQueryParameters& parameters, const AggregateSource& source, const flutter::EncodableList& queries, bool is_collection_group, std::function reply)> result) = 0; + virtual void FindNearest( + const FirestorePigeonFirebaseApp& app, const std::string& path, + bool is_collection_group, const PigeonQueryParameters& parameters, + const flutter::EncodableList& query_vector, const VectorSource& source, + int64_t limit, const VectorQueryOptions& query_options, + const DistanceMeasure& distance_measure, + std::function reply)> result) = 0; virtual void WriteBatchCommit( const FirestorePigeonFirebaseApp& app, const flutter::EncodableList& writes, @@ -724,7 +775,7 @@ class FirebaseFirestoreHostApi { std::function reply)> result) = 0; virtual void PersistenceCacheIndexManagerRequest( const FirestorePigeonFirebaseApp& app, - const PersistenceCacheIndexManagerRequestEnum& request, + const PersistenceCacheIndexManagerRequest& request, std::function reply)> result) = 0; // The codec used by FirebaseFirestoreHostApi. diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/cloud_firestore_platform_interface.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/cloud_firestore_platform_interface.dart index 01f41179836c..28e07d90b303 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/cloud_firestore_platform_interface.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/cloud_firestore_platform_interface.dart @@ -30,11 +30,12 @@ export 'src/platform_interface/platform_interface_firestore.dart'; export 'src/platform_interface/platform_interface_index_definitions.dart'; export 'src/platform_interface/platform_interface_load_bundle_task.dart'; export 'src/platform_interface/platform_interface_load_bundle_task_snapshot.dart'; +export 'src/platform_interface/platform_interface_persistent_cache_index_manager.dart'; export 'src/platform_interface/platform_interface_query.dart'; export 'src/platform_interface/platform_interface_query_snapshot.dart'; export 'src/platform_interface/platform_interface_transaction.dart'; +export 'src/platform_interface/platform_interface_vector_query.dart'; export 'src/platform_interface/platform_interface_write_batch.dart'; -export 'src/platform_interface/platform_interface_persistent_cache_index_manager.dart'; export 'src/platform_interface/utils/load_bundle_task_state.dart'; export 'src/set_options.dart'; export 'src/settings.dart'; diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_query.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_query.dart index 6ae2ec548688..d66c05284108 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_query.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_query.dart @@ -8,6 +8,7 @@ import 'dart:async'; import 'package:_flutterfire_internals/_flutterfire_internals.dart'; import 'package:cloud_firestore_platform_interface/cloud_firestore_platform_interface.dart'; import 'package:cloud_firestore_platform_interface/src/internal/pointer.dart'; +import 'package:cloud_firestore_platform_interface/src/method_channel/method_channel_vector_query.dart'; import 'package:cloud_firestore_platform_interface/src/platform_interface/platform_interface_query.dart' as query; import 'package:collection/collection.dart'; @@ -417,6 +418,31 @@ class MethodChannelQuery extends QueryPlatform { ); } + @override + VectorQueryPlatform findNearest( + String field, { + /// List or VectorValue + required Object queryVector, + required int limit, + required DistanceMeasure distanceMeasure, + required VectorQueryOptions options, + }) { + return MethodChannelVectorQuery( + firestore, + this, + _pigeonParameters, + _pointer.path, + pigeonApp, + queryVector is List + ? queryVector + : (queryVector as VectorValue).toList(), + limit, + distanceMeasure, + options, + isCollectionGroupQuery, + ); + } + @override bool operator ==(Object other) { return runtimeType == other.runtimeType && diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_vector_query.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_vector_query.dart new file mode 100644 index 000000000000..af02cc5ca8f5 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_vector_query.dart @@ -0,0 +1,60 @@ +// Copyright 2022, 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. + +import 'package:cloud_firestore_platform_interface/src/method_channel/method_channel_query_snapshot.dart'; +import 'package:cloud_firestore_platform_interface/src/method_channel/utils/exception.dart'; + +import '../../cloud_firestore_platform_interface.dart'; +import 'method_channel_firestore.dart'; + +/// An implementation of [VectorQueryPlatform] for the [MethodChannel] +class MethodChannelVectorQuery extends VectorQueryPlatform { + MethodChannelVectorQuery( + this.firestore, + query, + this._pigeonParameters, + this._path, + this._pigeonApp, + this._queryVector, + this._limit, + this._distanceMeasure, + this._options, + this._isCollectionGroupQuery, + ) : super(query); + + final FirebaseFirestorePlatform firestore; + final FirestorePigeonFirebaseApp _pigeonApp; + final String _path; + final PigeonQueryParameters _pigeonParameters; + final bool _isCollectionGroupQuery; + + final int _limit; + final DistanceMeasure _distanceMeasure; + final List _queryVector; + final VectorQueryOptions _options; + + @override + Future get({ + required VectorSource source, + }) async { + try { + final PigeonQuerySnapshot result = + await MethodChannelFirebaseFirestore.pigeonChannel.findNearest( + _pigeonApp, + _path, + _isCollectionGroupQuery, + _pigeonParameters, + _queryVector, + source, + _limit, + _options, + _distanceMeasure, + ); + + return MethodChannelQuerySnapshot(firestore, result); + } catch (e, stack) { + convertPlatformException(e, stack); + } + } +} diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/pigeon/messages.pigeon.dart index 85af6edc5260..acb03098f853 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -46,6 +46,26 @@ enum Source { cache, } +/// An enumeration of firestore source types. +enum VectorSource { + /// Causes Firestore to avoid the cache, generating an error if the server cannot be reached. Note + /// that the cache will still be updated if the server request succeeds. Also note that + /// latency-compensation still takes effect, so any pending write operations will be visible in the + /// returned data (merged into the server-provided data). + server, +} + +enum DistanceMeasure { + /// The cosine similarity measure. + cosine, + + /// The euclidean distance measure. + euclidean, + + /// The dot product distance measure. + dotProduct, +} + /// The listener retrieves data and listens to updates from the local Firestore cache only. /// If the cache is empty, an empty snapshot will be returned. /// Snapshot events will be triggered on cache updates, like local mutations or load bundles. @@ -301,6 +321,32 @@ class PigeonQuerySnapshot { } } +class VectorQueryOptions { + VectorQueryOptions({ + required this.distanceResultField, + required this.distanceThreshold, + }); + + String distanceResultField; + + double distanceThreshold; + + Object encode() { + return [ + distanceResultField, + distanceThreshold, + ]; + } + + static VectorQueryOptions decode(Object result) { + result as List; + return VectorQueryOptions( + distanceResultField: result[0]! as String, + distanceThreshold: result[1]! as double, + ); + } +} + class PigeonGetOptions { PigeonGetOptions({ required this.source, @@ -598,6 +644,9 @@ class _FirebaseFirestoreHostApiCodec extends FirestoreMessageCodec { } else if (value is PigeonTransactionCommand) { buffer.putUint8(140); writeValue(buffer, value.encode()); + } else if (value is VectorQueryOptions) { + buffer.putUint8(141); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -632,6 +681,8 @@ class _FirebaseFirestoreHostApiCodec extends FirestoreMessageCodec { return PigeonSnapshotMetadata.decode(readValue(buffer)!); case 140: return PigeonTransactionCommand.decode(readValue(buffer)!); + case 141: + return VectorQueryOptions.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -1207,6 +1258,54 @@ class FirebaseFirestoreHostApi { } } + Future findNearest( + FirestorePigeonFirebaseApp arg_app, + String arg_path, + bool arg_isCollectionGroup, + PigeonQueryParameters arg_parameters, + List arg_queryVector, + VectorSource arg_source, + int arg_limit, + VectorQueryOptions arg_queryOptions, + DistanceMeasure arg_distanceMeasure, + ) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.findNearest', + codec, + binaryMessenger: _binaryMessenger, + ); + final List? replyList = await channel.send([ + arg_app, + arg_path, + arg_isCollectionGroup, + arg_parameters, + arg_queryVector, + arg_source.index, + arg_limit, + arg_queryOptions, + arg_distanceMeasure.index, + ]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as PigeonQuerySnapshot?)!; + } + } + Future writeBatchCommit( FirestorePigeonFirebaseApp arg_app, List arg_writes, diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_query.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_query.dart index 44cea3026120..08a405d0f4bf 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_query.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_query.dart @@ -275,6 +275,17 @@ abstract class QueryPlatform extends PlatformInterface { throw UnimplementedError('aggregate() is not implemented'); } + VectorQueryPlatform findNearest( + String field, { + /// List or VectorValue + required Object queryVector, + required int limit, + required DistanceMeasure distanceMeasure, + required VectorQueryOptions options, + }) { + throw UnimplementedError('findNearest() is not implemented'); + } + /// Returns an [AggregateQueryPlatform] which uses the [QueryPlatform] to query for /// metadata /// @@ -292,7 +303,7 @@ abstract class QueryPlatform extends PlatformInterface { } } -abstract class AggregateField {} +class AggregateField {} /// Create a CountAggregateField object that can be used to compute /// the count of documents in the result set of a query. diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_vector_query.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_vector_query.dart new file mode 100644 index 000000000000..11ce45c901e0 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_vector_query.dart @@ -0,0 +1,44 @@ +// Copyright 2022, 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. + +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import '../../cloud_firestore_platform_interface.dart'; + +/// [VectorQueryPlatform] represents the data at a particular location for retrieving metadata +/// without retrieving the actual documents. +abstract class VectorQueryPlatform extends PlatformInterface { + VectorQueryPlatform(this.query) : super(token: _token); + + static final Object _token = Object(); + + /// Throws an [AssertionError] if [instance] does not extend + /// [VectorQueryPlatform]. + /// + /// This is used by the app-facing [VectorQuery] to ensure that + /// the object in which it's going to delegate calls has been + /// constructed properly. + static void verify(VectorQueryPlatform instance) { + PlatformInterface.verify(instance, _token); + } + + /// The [QueryPlatform] instance to which this [VectorQueryPlatform] queries against to retrieve the metadata. + final QueryPlatform query; + + /// Returns an [VectorQuerySnapshotPlatform] . + Future get({ + required VectorSource source, + }) async { + throw UnimplementedError('get() is not implemented'); + } +} + +class VectorValue { + final List value; + VectorValue(this.value); + + List toList() { + return value; + } +} diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/messages.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/messages.dart index 45f42dd233c7..0430ad0b6173 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/messages.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/messages.dart @@ -137,6 +137,36 @@ enum Source { cache, } +/// An enumeration of firestore source types. +enum VectorSource { + /// Causes Firestore to avoid the cache, generating an error if the server cannot be reached. Note + /// that the cache will still be updated if the server request succeeds. Also note that + /// latency-compensation still takes effect, so any pending write operations will be visible in the + /// returned data (merged into the server-provided data). + server, +} + +enum DistanceMeasure { + /// The cosine similarity measure. + cosine, + + /// The euclidean distance measure. + euclidean, + + /// The dot product distance measure. + dotProduct, +} + +class VectorQueryOptions { + final String distanceResultField; + final double distanceThreshold; + + VectorQueryOptions({ + required this.distanceResultField, + required this.distanceThreshold, + }); +} + /// The listener retrieves data and listens to updates from the local Firestore cache only. /// If the cache is empty, an empty snapshot will be returned. /// Snapshot events will be triggered on cache updates, like local mutations or load bundles. @@ -411,6 +441,19 @@ abstract class FirebaseFirestoreHostApi { bool isCollectionGroup, ); + @async + PigeonQuerySnapshot findNearest( + FirestorePigeonFirebaseApp app, + String path, + bool isCollectionGroup, + PigeonQueryParameters parameters, + List queryVector, + VectorSource source, + int limit, + VectorQueryOptions queryOptions, + DistanceMeasure distanceMeasure, + ); + @async void writeBatchCommit( FirestorePigeonFirebaseApp app, diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/test/pigeon/test_api.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/test/pigeon/test_api.dart index fda97b2fbce8..d558bb8a0d7f 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/test/pigeon/test_api.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/test/pigeon/test_api.dart @@ -56,6 +56,9 @@ class _TestFirebaseFirestoreHostApiCodec extends StandardMessageCodec { } else if (value is PigeonTransactionCommand) { buffer.putUint8(140); writeValue(buffer, value.encode()); + } else if (value is VectorQueryOptions) { + buffer.putUint8(141); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -90,6 +93,8 @@ class _TestFirebaseFirestoreHostApiCodec extends StandardMessageCodec { return PigeonSnapshotMetadata.decode(readValue(buffer)!); case 140: return PigeonTransactionCommand.decode(readValue(buffer)!); + case 141: + return VectorQueryOptions.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -184,6 +189,18 @@ abstract class TestFirebaseFirestoreHostApi { bool isCollectionGroup, ); + Future findNearest( + FirestorePigeonFirebaseApp app, + String path, + bool isCollectionGroup, + PigeonQueryParameters parameters, + List queryVector, + VectorSource source, + int limit, + VectorQueryOptions queryOptions, + DistanceMeasure distanceMeasure, + ); + Future writeBatchCommit( FirestorePigeonFirebaseApp app, List writes, @@ -910,6 +927,90 @@ abstract class TestFirebaseFirestoreHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.findNearest', + codec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { + assert( + message != null, + 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.findNearest was null.', + ); + final List args = (message as List?)!; + final FirestorePigeonFirebaseApp? arg_app = + (args[0] as FirestorePigeonFirebaseApp?); + assert( + arg_app != null, + 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.findNearest was null, expected non-null FirestorePigeonFirebaseApp.', + ); + final String? arg_path = (args[1] as String?); + assert( + arg_path != null, + 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.findNearest was null, expected non-null String.', + ); + final bool? arg_isCollectionGroup = (args[2] as bool?); + assert( + arg_isCollectionGroup != null, + 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.findNearest was null, expected non-null bool.', + ); + final PigeonQueryParameters? arg_parameters = + (args[3] as PigeonQueryParameters?); + assert( + arg_parameters != null, + 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.findNearest was null, expected non-null PigeonQueryParameters.', + ); + final List? arg_queryVector = + (args[4] as List?)?.cast(); + assert( + arg_queryVector != null, + 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.findNearest was null, expected non-null List.', + ); + final VectorSource? arg_source = + args[5] == null ? null : VectorSource.values[args[5]! as int]; + assert( + arg_source != null, + 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.findNearest was null, expected non-null VectorSource.', + ); + final int? arg_limit = (args[6] as int?); + assert( + arg_limit != null, + 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.findNearest was null, expected non-null int.', + ); + final VectorQueryOptions? arg_queryOptions = + (args[7] as VectorQueryOptions?); + assert( + arg_queryOptions != null, + 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.findNearest was null, expected non-null VectorQueryOptions.', + ); + final DistanceMeasure? arg_distanceMeasure = + args[8] == null ? null : DistanceMeasure.values[args[8]! as int]; + assert( + arg_distanceMeasure != null, + 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.findNearest was null, expected non-null DistanceMeasure.', + ); + final PigeonQuerySnapshot output = await api.findNearest( + arg_app!, + arg_path!, + arg_isCollectionGroup!, + arg_parameters!, + arg_queryVector!, + arg_source!, + arg_limit!, + arg_queryOptions!, + arg_distanceMeasure!, + ); + return [output]; + }); + } + } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit',