@react-native-firebase/firestore 23.8.6 → 24.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/RNFBFirestore.podspec +2 -1
- package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreCollectionModule.java +17 -4
- package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreDocumentModule.java +2 -2
- package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestorePipelineExecutor.java +1243 -0
- package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestorePipelineNodeBuilder.java +3919 -0
- package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestorePipelineParser.java +1735 -0
- package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreSerialize.java +1 -1
- package/dist/module/FieldPath.js +59 -0
- package/dist/module/FieldPath.js.map +1 -0
- package/dist/module/FieldValue.js +82 -0
- package/dist/module/FieldValue.js.map +1 -0
- package/{lib → dist/module}/FirestoreAggregate.js +31 -43
- package/dist/module/FirestoreAggregate.js.map +1 -0
- package/dist/module/FirestoreBlob.js +56 -0
- package/dist/module/FirestoreBlob.js.map +1 -0
- package/dist/module/FirestoreCollectionReference.js +70 -0
- package/dist/module/FirestoreCollectionReference.js.map +1 -0
- package/{lib → dist/module}/FirestoreDocumentChange.js +12 -15
- package/dist/module/FirestoreDocumentChange.js.map +1 -0
- package/dist/module/FirestoreDocumentReference.js +170 -0
- package/dist/module/FirestoreDocumentReference.js.map +1 -0
- package/dist/module/FirestoreDocumentSnapshot.js +88 -0
- package/dist/module/FirestoreDocumentSnapshot.js.map +1 -0
- package/dist/module/FirestoreFilter.js +146 -0
- package/dist/module/FirestoreFilter.js.map +1 -0
- package/dist/module/FirestoreGeoPoint.js +80 -0
- package/dist/module/FirestoreGeoPoint.js.map +1 -0
- package/{lib → dist/module}/FirestorePath.js +5 -12
- package/dist/module/FirestorePath.js.map +1 -0
- package/{lib → dist/module}/FirestorePersistentCacheIndexManager.js +11 -4
- package/dist/module/FirestorePersistentCacheIndexManager.js.map +1 -0
- package/dist/module/FirestoreQuery.js +298 -0
- package/dist/module/FirestoreQuery.js.map +1 -0
- package/{lib → dist/module}/FirestoreQueryModifiers.js +25 -136
- package/dist/module/FirestoreQueryModifiers.js.map +1 -0
- package/dist/module/FirestoreQuerySnapshot.js +98 -0
- package/dist/module/FirestoreQuerySnapshot.js.map +1 -0
- package/dist/module/FirestoreSnapshotMetadata.js +38 -0
- package/dist/module/FirestoreSnapshotMetadata.js.map +1 -0
- package/dist/module/FirestoreStatics.js +50 -0
- package/dist/module/FirestoreStatics.js.map +1 -0
- package/{lib → dist/module}/FirestoreTimestamp.js +39 -39
- package/dist/module/FirestoreTimestamp.js.map +1 -0
- package/dist/module/FirestoreTransaction.js +113 -0
- package/dist/module/FirestoreTransaction.js.map +1 -0
- package/dist/module/FirestoreTransactionHandler.js +137 -0
- package/dist/module/FirestoreTransactionHandler.js.map +1 -0
- package/dist/module/FirestoreVectorValue.js +75 -0
- package/dist/module/FirestoreVectorValue.js.map +1 -0
- package/dist/module/FirestoreWriteBatch.js +113 -0
- package/dist/module/FirestoreWriteBatch.js.map +1 -0
- package/dist/module/LoadBundleTask.js +70 -0
- package/dist/module/LoadBundleTask.js.map +1 -0
- package/dist/module/index.js +31 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/modular/Bytes.js +67 -0
- package/dist/module/modular/Bytes.js.map +1 -0
- package/dist/module/modular/FieldPath.js +25 -0
- package/dist/module/modular/FieldPath.js.map +1 -0
- package/dist/module/modular/FieldValue.js +37 -0
- package/dist/module/modular/FieldValue.js.map +1 -0
- package/dist/module/modular/GeoPoint.js +22 -0
- package/dist/module/modular/GeoPoint.js.map +1 -0
- package/dist/module/modular/Timestamp.js +22 -0
- package/dist/module/modular/Timestamp.js.map +1 -0
- package/dist/module/modular/VectorValue.js +25 -0
- package/dist/module/modular/VectorValue.js.map +1 -0
- package/dist/module/modular/query.js +222 -0
- package/dist/module/modular/query.js.map +1 -0
- package/dist/module/modular/snapshot.js +32 -0
- package/dist/module/modular/snapshot.js.map +1 -0
- package/dist/module/modular.js +229 -0
- package/dist/module/modular.js.map +1 -0
- package/dist/module/namespaced.js +298 -0
- package/dist/module/namespaced.js.map +1 -0
- package/dist/module/package.json +1 -0
- package/dist/module/pipelines/expressions.js +1273 -0
- package/dist/module/pipelines/expressions.js.map +1 -0
- package/dist/module/pipelines/index.js +32 -0
- package/dist/module/pipelines/index.js.map +1 -0
- package/dist/module/pipelines/pipeline-result.js +58 -0
- package/dist/module/pipelines/pipeline-result.js.map +1 -0
- package/dist/module/pipelines/pipeline-source.js +4 -0
- package/dist/module/pipelines/pipeline-source.js.map +1 -0
- package/dist/module/pipelines/pipeline.js +4 -0
- package/dist/module/pipelines/pipeline.js.map +1 -0
- package/dist/module/pipelines/pipeline_impl.js +42 -0
- package/dist/module/pipelines/pipeline_impl.js.map +1 -0
- package/dist/module/pipelines/pipeline_options.js +4 -0
- package/dist/module/pipelines/pipeline_options.js.map +1 -0
- package/dist/module/pipelines/pipeline_runtime.js +526 -0
- package/dist/module/pipelines/pipeline_runtime.js.map +1 -0
- package/dist/module/pipelines/pipeline_support.js +71 -0
- package/dist/module/pipelines/pipeline_support.js.map +1 -0
- package/dist/module/pipelines/pipeline_validate.js +183 -0
- package/dist/module/pipelines/pipeline_validate.js.map +1 -0
- package/dist/module/pipelines/stage_options.js +4 -0
- package/dist/module/pipelines/stage_options.js.map +1 -0
- package/dist/module/pipelines/types.js +2 -0
- package/dist/module/pipelines/types.js.map +1 -0
- package/dist/module/types/firestore.js +4 -0
- package/dist/module/types/firestore.js.map +1 -0
- package/dist/module/types/internal.js +4 -0
- package/dist/module/types/internal.js.map +1 -0
- package/dist/module/types/namespaced.js +338 -0
- package/dist/module/types/namespaced.js.map +1 -0
- package/{lib → dist/module}/utils/index.js +59 -114
- package/dist/module/utils/index.js.map +1 -0
- package/{lib → dist/module}/utils/serialize.js +58 -116
- package/dist/module/utils/serialize.js.map +1 -0
- package/{lib → dist/module}/utils/typemap.js +6 -20
- package/dist/module/utils/typemap.js.map +1 -0
- package/dist/module/version.js +5 -0
- package/dist/module/version.js.map +1 -0
- package/dist/module/web/RNFBFirestoreModule.android.js +5 -0
- package/dist/module/web/RNFBFirestoreModule.android.js.map +1 -0
- package/dist/module/web/RNFBFirestoreModule.ios.js +5 -0
- package/dist/module/web/RNFBFirestoreModule.ios.js.map +1 -0
- package/dist/module/web/RNFBFirestoreModule.js +387 -0
- package/dist/module/web/RNFBFirestoreModule.js.map +1 -0
- package/{lib → dist/module}/web/convert.js +60 -94
- package/dist/module/web/convert.js.map +1 -0
- package/dist/module/web/pipelines/pipeline.js +34 -0
- package/dist/module/web/pipelines/pipeline.js.map +1 -0
- package/dist/module/web/pipelines/pipeline_bridge_factory.js +217 -0
- package/dist/module/web/pipelines/pipeline_bridge_factory.js.map +1 -0
- package/dist/module/web/pipelines/pipeline_node_builder.js +294 -0
- package/dist/module/web/pipelines/pipeline_node_builder.js.map +1 -0
- package/dist/module/web/pipelines/pipeline_parser.js +21 -0
- package/dist/module/web/pipelines/pipeline_parser.js.map +1 -0
- package/dist/module/web/pipelines/pipeline_snapshot_serializer.js +89 -0
- package/dist/module/web/pipelines/pipeline_snapshot_serializer.js.map +1 -0
- package/dist/module/web/query.js +95 -0
- package/dist/module/web/query.js.map +1 -0
- package/dist/typescript/lib/FieldPath.d.ts +10 -0
- package/dist/typescript/lib/FieldPath.d.ts.map +1 -0
- package/dist/typescript/lib/FieldValue.d.ts +17 -0
- package/dist/typescript/lib/FieldValue.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreAggregate.d.ts +40 -0
- package/dist/typescript/lib/FirestoreAggregate.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreBlob.d.ts +11 -0
- package/dist/typescript/lib/FirestoreBlob.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreCollectionReference.d.ts +15 -0
- package/dist/typescript/lib/FirestoreCollectionReference.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreDocumentChange.d.ts +27 -0
- package/dist/typescript/lib/FirestoreDocumentChange.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreDocumentReference.d.ts +30 -0
- package/dist/typescript/lib/FirestoreDocumentReference.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreDocumentSnapshot.d.ts +30 -0
- package/dist/typescript/lib/FirestoreDocumentSnapshot.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreFilter.d.ts +52 -0
- package/dist/typescript/lib/FirestoreFilter.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreGeoPoint.d.ts +22 -0
- package/dist/typescript/lib/FirestoreGeoPoint.d.ts.map +1 -0
- package/dist/typescript/lib/FirestorePath.d.ts +12 -0
- package/dist/typescript/lib/FirestorePath.d.ts.map +1 -0
- package/dist/typescript/lib/FirestorePersistentCacheIndexManager.d.ts +16 -0
- package/dist/typescript/lib/FirestorePersistentCacheIndexManager.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreQuery.d.ts +39 -0
- package/dist/typescript/lib/FirestoreQuery.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreQueryModifiers.d.ts +59 -0
- package/dist/typescript/lib/FirestoreQueryModifiers.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreQuerySnapshot.d.ts +49 -0
- package/dist/typescript/lib/FirestoreQuerySnapshot.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreSnapshotMetadata.d.ts +8 -0
- package/dist/typescript/lib/FirestoreSnapshotMetadata.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreStatics.d.ts +23 -0
- package/dist/typescript/lib/FirestoreStatics.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreTimestamp.d.ts +33 -0
- package/dist/typescript/lib/FirestoreTimestamp.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreTransaction.d.ts +42 -0
- package/dist/typescript/lib/FirestoreTransaction.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreTransactionHandler.d.ts +26 -0
- package/dist/typescript/lib/FirestoreTransactionHandler.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreVectorValue.d.ts +17 -0
- package/dist/typescript/lib/FirestoreVectorValue.d.ts.map +1 -0
- package/dist/typescript/lib/FirestoreWriteBatch.d.ts +21 -0
- package/dist/typescript/lib/FirestoreWriteBatch.d.ts.map +1 -0
- package/dist/typescript/lib/LoadBundleTask.d.ts +16 -0
- package/dist/typescript/lib/LoadBundleTask.d.ts.map +1 -0
- package/dist/typescript/lib/index.d.ts +6 -0
- package/dist/typescript/lib/index.d.ts.map +1 -0
- package/dist/typescript/lib/modular/Bytes.d.ts +22 -0
- package/dist/typescript/lib/modular/Bytes.d.ts.map +1 -0
- package/dist/typescript/lib/modular/FieldPath.d.ts +4 -0
- package/dist/typescript/lib/modular/FieldPath.d.ts.map +1 -0
- package/dist/typescript/lib/modular/FieldValue.d.ts +8 -0
- package/dist/typescript/lib/modular/FieldValue.d.ts.map +1 -0
- package/dist/typescript/lib/modular/GeoPoint.d.ts +3 -0
- package/dist/typescript/lib/modular/GeoPoint.d.ts.map +1 -0
- package/dist/typescript/lib/modular/Timestamp.d.ts +3 -0
- package/dist/typescript/lib/modular/Timestamp.d.ts.map +1 -0
- package/dist/typescript/lib/modular/VectorValue.d.ts +4 -0
- package/dist/typescript/lib/modular/VectorValue.d.ts.map +1 -0
- package/dist/typescript/lib/modular/query.d.ts +93 -0
- package/dist/typescript/lib/modular/query.d.ts.map +1 -0
- package/dist/typescript/lib/modular/snapshot.d.ts +30 -0
- package/dist/typescript/lib/modular/snapshot.d.ts.map +1 -0
- package/dist/typescript/lib/modular.d.ts +69 -0
- package/dist/typescript/lib/modular.d.ts.map +1 -0
- package/dist/typescript/lib/namespaced.d.ts +13 -0
- package/dist/typescript/lib/namespaced.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/expressions.d.ts +723 -0
- package/dist/typescript/lib/pipelines/expressions.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/index.d.ts +31 -0
- package/dist/typescript/lib/pipelines/index.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline-result.d.ts +30 -0
- package/dist/typescript/lib/pipelines/pipeline-result.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline-source.d.ts +64 -0
- package/dist/typescript/lib/pipelines/pipeline-source.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline.d.ts +61 -0
- package/dist/typescript/lib/pipelines/pipeline.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline_impl.d.ts +21 -0
- package/dist/typescript/lib/pipelines/pipeline_impl.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline_options.d.ts +17 -0
- package/dist/typescript/lib/pipelines/pipeline_options.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline_runtime.d.ts +10 -0
- package/dist/typescript/lib/pipelines/pipeline_runtime.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline_support.d.ts +7 -0
- package/dist/typescript/lib/pipelines/pipeline_support.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/pipeline_validate.d.ts +9 -0
- package/dist/typescript/lib/pipelines/pipeline_validate.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/stage_options.d.ts +326 -0
- package/dist/typescript/lib/pipelines/stage_options.d.ts.map +1 -0
- package/dist/typescript/lib/pipelines/types.d.ts +10 -0
- package/dist/typescript/lib/pipelines/types.d.ts.map +1 -0
- package/dist/typescript/lib/types/firestore.d.ts +263 -0
- package/dist/typescript/lib/types/firestore.d.ts.map +1 -0
- package/dist/typescript/lib/types/internal.d.ts +483 -0
- package/dist/typescript/lib/types/internal.d.ts.map +1 -0
- package/dist/typescript/lib/types/namespaced.d.ts +2285 -0
- package/dist/typescript/lib/types/namespaced.d.ts.map +1 -0
- package/dist/typescript/lib/utils/index.d.ts +15 -0
- package/dist/typescript/lib/utils/index.d.ts.map +1 -0
- package/dist/typescript/lib/utils/serialize.d.ts +17 -0
- package/dist/typescript/lib/utils/serialize.d.ts.map +1 -0
- package/dist/typescript/lib/utils/typemap.d.ts +3 -0
- package/dist/typescript/lib/utils/typemap.d.ts.map +1 -0
- package/dist/typescript/lib/version.d.ts +2 -0
- package/dist/typescript/lib/version.d.ts.map +1 -0
- package/dist/typescript/lib/web/RNFBFirestoreModule.android.d.ts +3 -0
- package/dist/typescript/lib/web/RNFBFirestoreModule.android.d.ts.map +1 -0
- package/dist/typescript/lib/web/RNFBFirestoreModule.d.ts +75 -0
- package/dist/typescript/lib/web/RNFBFirestoreModule.d.ts.map +1 -0
- package/dist/typescript/lib/web/RNFBFirestoreModule.ios.d.ts +3 -0
- package/dist/typescript/lib/web/RNFBFirestoreModule.ios.d.ts.map +1 -0
- package/dist/typescript/lib/web/convert.d.ts +14 -0
- package/dist/typescript/lib/web/convert.d.ts.map +1 -0
- package/dist/typescript/lib/web/pipelines/pipeline.d.ts +4 -0
- package/dist/typescript/lib/web/pipelines/pipeline.d.ts.map +1 -0
- package/dist/typescript/lib/web/pipelines/pipeline_bridge_factory.d.ts +5 -0
- package/dist/typescript/lib/web/pipelines/pipeline_bridge_factory.d.ts.map +1 -0
- package/dist/typescript/lib/web/pipelines/pipeline_node_builder.d.ts +5 -0
- package/dist/typescript/lib/web/pipelines/pipeline_node_builder.d.ts.map +1 -0
- package/dist/typescript/lib/web/pipelines/pipeline_parser.d.ts +3 -0
- package/dist/typescript/lib/web/pipelines/pipeline_parser.d.ts.map +1 -0
- package/dist/typescript/lib/web/pipelines/pipeline_snapshot_serializer.d.ts +4 -0
- package/dist/typescript/lib/web/pipelines/pipeline_snapshot_serializer.d.ts.map +1 -0
- package/dist/typescript/lib/web/query.d.ts +23 -0
- package/dist/typescript/lib/web/query.d.ts.map +1 -0
- package/dist/typescript/package.json +1 -0
- package/ios/RNFBFirestore/RNFBFirestoreCollectionModule.m +52 -2
- package/ios/RNFBFirestore/RNFBFirestorePipelineBridgeFactory.swift +384 -0
- package/ios/RNFBFirestore/RNFBFirestorePipelineCallHandler.swift +86 -0
- package/ios/RNFBFirestore/RNFBFirestorePipelineNodeBuilder.swift +1500 -0
- package/ios/RNFBFirestore/RNFBFirestorePipelineParser.swift +1352 -0
- package/ios/RNFBFirestore/RNFBFirestorePipelineSnapshotSerializer.swift +98 -0
- package/lib/{FirestoreFieldPath.js → FieldPath.ts} +10 -12
- package/lib/{FirestoreFieldValue.js → FieldValue.ts} +22 -19
- package/lib/FirestoreAggregate.ts +124 -0
- package/lib/FirestoreBlob.ts +73 -0
- package/lib/FirestoreCollectionReference.ts +99 -0
- package/lib/FirestoreDocumentChange.ts +71 -0
- package/lib/FirestoreDocumentReference.ts +310 -0
- package/lib/FirestoreDocumentSnapshot.ts +149 -0
- package/lib/FirestoreFilter.ts +232 -0
- package/lib/{FirestoreGeoPoint.js → FirestoreGeoPoint.ts} +48 -8
- package/lib/FirestorePath.ts +54 -0
- package/lib/FirestorePersistentCacheIndexManager.ts +46 -0
- package/lib/{FirestoreQuery.js → FirestoreQuery.ts} +208 -100
- package/lib/FirestoreQueryModifiers.ts +411 -0
- package/lib/{FirestoreQuerySnapshot.js → FirestoreQuerySnapshot.ts} +61 -32
- package/lib/{FirestoreSnapshotMetadata.js → FirestoreSnapshotMetadata.ts} +8 -6
- package/lib/{FirestoreStatics.js → FirestoreStatics.ts} +18 -11
- package/lib/FirestoreTimestamp.ts +161 -0
- package/lib/{FirestoreTransaction.js → FirestoreTransaction.ts} +64 -27
- package/lib/{FirestoreTransactionHandler.js → FirestoreTransactionHandler.ts} +54 -75
- package/lib/{FirestoreVectorValue.js → FirestoreVectorValue.ts} +36 -15
- package/lib/{FirestoreWriteBatch.js → FirestoreWriteBatch.ts} +45 -21
- package/lib/LoadBundleTask.ts +85 -0
- package/lib/index.ts +71 -0
- package/lib/modular/Bytes.ts +81 -0
- package/lib/modular/FieldPath.ts +24 -0
- package/lib/modular/FieldValue.ts +40 -0
- package/lib/modular/GeoPoint.ts +20 -0
- package/lib/modular/Timestamp.ts +20 -0
- package/lib/modular/VectorValue.ts +24 -0
- package/lib/modular/query.ts +368 -0
- package/lib/modular/snapshot.ts +137 -0
- package/lib/modular.ts +552 -0
- package/lib/{index.js → namespaced.ts} +170 -80
- package/lib/pipelines/expressions.ts +2321 -0
- package/lib/pipelines/index.ts +203 -0
- package/lib/pipelines/pipeline-result.ts +78 -0
- package/lib/pipelines/pipeline-source.ts +83 -0
- package/lib/pipelines/pipeline.ts +99 -0
- package/lib/pipelines/pipeline_impl.ts +46 -0
- package/lib/pipelines/pipeline_options.ts +32 -0
- package/lib/pipelines/pipeline_runtime.ts +863 -0
- package/lib/pipelines/pipeline_support.ts +134 -0
- package/lib/pipelines/pipeline_validate.ts +242 -0
- package/lib/pipelines/stage_options.ts +376 -0
- package/lib/pipelines/types.ts +26 -0
- package/lib/types/firestore.ts +477 -0
- package/lib/types/internal.ts +747 -0
- package/lib/{index.d.ts → types/namespaced.ts} +280 -79
- package/lib/utils/index.ts +244 -0
- package/lib/utils/serialize.ts +314 -0
- package/lib/utils/typemap.ts +65 -0
- package/lib/version.ts +2 -0
- package/lib/web/{RNFBFirestoreModule.js → RNFBFirestoreModule.ts} +222 -234
- package/lib/web/convert.ts +287 -0
- package/lib/web/pipelines/pipeline.ts +47 -0
- package/lib/web/pipelines/pipeline_bridge_factory.ts +377 -0
- package/lib/web/pipelines/pipeline_node_builder.ts +413 -0
- package/lib/web/pipelines/pipeline_parser.ts +23 -0
- package/lib/web/pipelines/pipeline_snapshot_serializer.ts +133 -0
- package/lib/web/query.ts +150 -0
- package/package.json +46 -7
- package/tsconfig.json +35 -0
- package/lib/FirestoreBlob.js +0 -107
- package/lib/FirestoreCollectionReference.js +0 -70
- package/lib/FirestoreDocumentReference.js +0 -222
- package/lib/FirestoreDocumentSnapshot.js +0 -132
- package/lib/FirestoreFilter.js +0 -156
- package/lib/modular/Bytes.d.ts +0 -11
- package/lib/modular/Bytes.js +0 -62
- package/lib/modular/FieldPath.d.ts +0 -20
- package/lib/modular/FieldPath.js +0 -7
- package/lib/modular/FieldValue.d.ts +0 -67
- package/lib/modular/FieldValue.js +0 -41
- package/lib/modular/GeoPoint.d.ts +0 -17
- package/lib/modular/GeoPoint.js +0 -3
- package/lib/modular/Timestamp.d.ts +0 -85
- package/lib/modular/Timestamp.js +0 -3
- package/lib/modular/VectorValue.d.ts +0 -30
- package/lib/modular/VectorValue.js +0 -11
- package/lib/modular/index.d.ts +0 -788
- package/lib/modular/index.js +0 -410
- package/lib/modular/query.d.ts +0 -370
- package/lib/modular/query.js +0 -233
- package/lib/modular/snapshot.d.ts +0 -256
- package/lib/modular/snapshot.js +0 -33
- package/lib/modular/utils/observer.js +0 -16
- package/lib/version.js +0 -2
- package/lib/web/query.js +0 -112
- /package/lib/web/{RNFBFirestoreModule.android.js → RNFBFirestoreModule.android.ts} +0 -0
- /package/lib/web/{RNFBFirestoreModule.ios.js → RNFBFirestoreModule.ios.ts} +0 -0
|
@@ -0,0 +1,1352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import Foundation
|
|
18
|
+
|
|
19
|
+
struct RNFBFirestoreParsedPipelineRequest {
|
|
20
|
+
let source: RNFBFirestoreParsedPipelineSource
|
|
21
|
+
let stages: [RNFBFirestoreParsedPipelineStage]
|
|
22
|
+
let options: RNFBFirestoreParsedPipelineExecuteOptions
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
struct RNFBFirestoreParsedPipelineSource {
|
|
26
|
+
let sourceType: String
|
|
27
|
+
let path: String?
|
|
28
|
+
let collectionId: String?
|
|
29
|
+
let documents: [String]
|
|
30
|
+
let queryType: String?
|
|
31
|
+
let query: RNFBFirestoreParsedQuerySource?
|
|
32
|
+
let rawOptions: [String: Any]?
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
struct RNFBFirestoreParsedQuerySource {
|
|
36
|
+
let filters: [RNFBFirestoreParsedValueNode]
|
|
37
|
+
let orders: [RNFBFirestoreParsedValueNode]
|
|
38
|
+
let options: [String: RNFBFirestoreParsedValueNode]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
indirect enum RNFBFirestoreParsedExpressionNode {
|
|
42
|
+
case field(path: String)
|
|
43
|
+
case constant(RNFBFirestoreParsedValueNode)
|
|
44
|
+
case function(name: String, args: [RNFBFirestoreParsedValueNode])
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
indirect enum RNFBFirestoreParsedValueNode {
|
|
48
|
+
case primitive(Any)
|
|
49
|
+
case list([RNFBFirestoreParsedValueNode])
|
|
50
|
+
case map([String: RNFBFirestoreParsedValueNode])
|
|
51
|
+
case expression(RNFBFirestoreParsedExpressionNode)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
struct RNFBFirestoreParsedSelectableNode {
|
|
55
|
+
let expression: RNFBFirestoreParsedExpressionNode
|
|
56
|
+
let alias: String?
|
|
57
|
+
let isFlatFieldAlias: Bool
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
struct RNFBFirestoreParsedOrderingNode {
|
|
61
|
+
let expression: RNFBFirestoreParsedExpressionNode
|
|
62
|
+
let descending: Bool
|
|
63
|
+
let fieldShortcut: Bool
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
struct RNFBFirestoreParsedAggregateNode {
|
|
67
|
+
let kind: String
|
|
68
|
+
let alias: String
|
|
69
|
+
let primaryValue: RNFBFirestoreParsedValueNode?
|
|
70
|
+
let args: [RNFBFirestoreParsedValueNode]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
enum RNFBFirestoreParsedPipelineStage {
|
|
74
|
+
case whereStage(RNFBFirestoreParsedWhereStage)
|
|
75
|
+
case selectStage(RNFBFirestoreParsedSelectStage)
|
|
76
|
+
case addFieldsStage(RNFBFirestoreParsedAddFieldsStage)
|
|
77
|
+
case removeFieldsStage(RNFBFirestoreParsedRemoveFieldsStage)
|
|
78
|
+
case sortStage(RNFBFirestoreParsedSortStage)
|
|
79
|
+
case limitStage(RNFBFirestoreParsedLimitStage)
|
|
80
|
+
case offsetStage(RNFBFirestoreParsedOffsetStage)
|
|
81
|
+
case aggregateStage(RNFBFirestoreParsedAggregateStage)
|
|
82
|
+
case distinctStage(RNFBFirestoreParsedDistinctStage)
|
|
83
|
+
case findNearestStage(RNFBFirestoreParsedFindNearestStage)
|
|
84
|
+
case replaceWithStage(RNFBFirestoreParsedReplaceWithStage)
|
|
85
|
+
case sampleStage(RNFBFirestoreParsedSampleStage)
|
|
86
|
+
case unionStage(RNFBFirestoreParsedUnionStage)
|
|
87
|
+
case unnestStage(RNFBFirestoreParsedUnnestStage)
|
|
88
|
+
case rawStage(RNFBFirestoreParsedRawStage)
|
|
89
|
+
|
|
90
|
+
var stageName: String {
|
|
91
|
+
switch self {
|
|
92
|
+
case .whereStage: return "where"
|
|
93
|
+
case .selectStage: return "select"
|
|
94
|
+
case .addFieldsStage: return "addFields"
|
|
95
|
+
case .removeFieldsStage: return "removeFields"
|
|
96
|
+
case .sortStage: return "sort"
|
|
97
|
+
case .limitStage: return "limit"
|
|
98
|
+
case .offsetStage: return "offset"
|
|
99
|
+
case .aggregateStage: return "aggregate"
|
|
100
|
+
case .distinctStage: return "distinct"
|
|
101
|
+
case .findNearestStage: return "findNearest"
|
|
102
|
+
case .replaceWithStage: return "replaceWith"
|
|
103
|
+
case .sampleStage: return "sample"
|
|
104
|
+
case .unionStage: return "union"
|
|
105
|
+
case .unnestStage: return "unnest"
|
|
106
|
+
case .rawStage: return "rawStage"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
struct RNFBFirestoreParsedPipelineExecuteOptions {
|
|
112
|
+
let indexMode: String?
|
|
113
|
+
let rawOptions: [String: Any]?
|
|
114
|
+
|
|
115
|
+
var isEmpty: Bool {
|
|
116
|
+
indexMode == nil && rawOptions == nil
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
struct RNFBFirestoreParsedWhereStage {
|
|
121
|
+
let condition: RNFBFirestoreParsedExpressionNode
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
struct RNFBFirestoreParsedSelectStage {
|
|
125
|
+
let selections: [RNFBFirestoreParsedSelectableNode]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
struct RNFBFirestoreParsedAddFieldsStage {
|
|
129
|
+
let fields: [RNFBFirestoreParsedSelectableNode]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
struct RNFBFirestoreParsedRemoveFieldsStage {
|
|
133
|
+
let fields: [String]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
struct RNFBFirestoreParsedSortStage {
|
|
137
|
+
let orderings: [RNFBFirestoreParsedOrderingNode]
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
struct RNFBFirestoreParsedLimitStage {
|
|
141
|
+
let limit: NSNumber
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
struct RNFBFirestoreParsedOffsetStage {
|
|
145
|
+
let offset: NSNumber
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
struct RNFBFirestoreParsedAggregateStage {
|
|
149
|
+
let accumulators: [RNFBFirestoreParsedAggregateNode]
|
|
150
|
+
let groups: [RNFBFirestoreParsedSelectableNode]
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
struct RNFBFirestoreParsedDistinctStage {
|
|
154
|
+
let groups: [RNFBFirestoreParsedSelectableNode]
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
struct RNFBFirestoreParsedFindNearestStage {
|
|
158
|
+
let field: RNFBFirestoreParsedExpressionNode
|
|
159
|
+
let vectorValue: RNFBFirestoreParsedValueNode
|
|
160
|
+
let distanceMeasure: String
|
|
161
|
+
let limit: NSNumber?
|
|
162
|
+
let distanceField: RNFBFirestoreParsedExpressionNode?
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
struct RNFBFirestoreParsedReplaceWithStage {
|
|
166
|
+
let value: RNFBFirestoreParsedExpressionNode
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
struct RNFBFirestoreParsedSampleStage {
|
|
170
|
+
let documents: NSNumber?
|
|
171
|
+
let percentage: NSNumber?
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
struct RNFBFirestoreParsedUnionStage {
|
|
175
|
+
let other: RNFBFirestoreParsedPipelineRequest
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
struct RNFBFirestoreParsedUnnestStage {
|
|
179
|
+
let selectable: RNFBFirestoreParsedSelectableNode
|
|
180
|
+
let indexField: RNFBFirestoreParsedExpressionNode?
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
struct RNFBFirestoreParsedRawStage {
|
|
184
|
+
let name: String
|
|
185
|
+
let params: Any?
|
|
186
|
+
let options: [String: Any]?
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
enum RNFBFirestorePipelineParser {
|
|
190
|
+
private static let sourceTypes: Set<String> = [
|
|
191
|
+
"collection", "collectionGroup", "database", "documents", "query",
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
private static let knownStages: Set<String> = [
|
|
195
|
+
"where", "select", "addFields", "removeFields", "sort", "limit", "offset",
|
|
196
|
+
"aggregate", "distinct", "findNearest", "replaceWith", "sample", "union", "unnest", "rawStage",
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
private final class ParsedPipelineRequestBox {
|
|
200
|
+
var value: RNFBFirestoreParsedPipelineRequest?
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private final class ParsedValueNodeBox {
|
|
204
|
+
var value: RNFBFirestoreParsedValueNode?
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private final class ParsedExpressionNodeBox {
|
|
208
|
+
var value: RNFBFirestoreParsedExpressionNode?
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private enum PendingParsedStage {
|
|
212
|
+
case ready(RNFBFirestoreParsedPipelineStage)
|
|
213
|
+
case union(ParsedPipelineRequestBox)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private enum PipelineParseFrame {
|
|
217
|
+
case enter(
|
|
218
|
+
pipeline: [String: Any],
|
|
219
|
+
options: [String: Any]?,
|
|
220
|
+
box: ParsedPipelineRequestBox
|
|
221
|
+
)
|
|
222
|
+
case exit(
|
|
223
|
+
source: RNFBFirestoreParsedPipelineSource,
|
|
224
|
+
stages: [PendingParsedStage],
|
|
225
|
+
options: RNFBFirestoreParsedPipelineExecuteOptions,
|
|
226
|
+
box: ParsedPipelineRequestBox
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private enum QuerySourceValueParseFrame {
|
|
231
|
+
case enter(
|
|
232
|
+
Any,
|
|
233
|
+
ParsedValueNodeBox
|
|
234
|
+
)
|
|
235
|
+
case exitList(
|
|
236
|
+
ParsedValueNodeBox,
|
|
237
|
+
[ParsedValueNodeBox]
|
|
238
|
+
)
|
|
239
|
+
case exitMap(
|
|
240
|
+
ParsedValueNodeBox,
|
|
241
|
+
[(String, ParsedValueNodeBox)]
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private enum ExpressionValueParseFrame {
|
|
246
|
+
case expressionEnter(
|
|
247
|
+
Any,
|
|
248
|
+
ParsedExpressionNodeBox,
|
|
249
|
+
String
|
|
250
|
+
)
|
|
251
|
+
case expressionValueExit(
|
|
252
|
+
ParsedValueNodeBox,
|
|
253
|
+
ParsedExpressionNodeBox,
|
|
254
|
+
String
|
|
255
|
+
)
|
|
256
|
+
case expressionConstantExit(
|
|
257
|
+
ParsedExpressionNodeBox,
|
|
258
|
+
ParsedValueNodeBox,
|
|
259
|
+
String
|
|
260
|
+
)
|
|
261
|
+
case expressionFunctionExit(
|
|
262
|
+
ParsedExpressionNodeBox,
|
|
263
|
+
String,
|
|
264
|
+
[ParsedValueNodeBox],
|
|
265
|
+
String
|
|
266
|
+
)
|
|
267
|
+
case expressionOperatorLogicalExit(
|
|
268
|
+
ParsedExpressionNodeBox,
|
|
269
|
+
String,
|
|
270
|
+
[ParsedExpressionNodeBox],
|
|
271
|
+
String
|
|
272
|
+
)
|
|
273
|
+
case expressionOperatorBinaryExit(
|
|
274
|
+
ParsedExpressionNodeBox,
|
|
275
|
+
String,
|
|
276
|
+
ParsedExpressionNodeBox,
|
|
277
|
+
ParsedValueNodeBox,
|
|
278
|
+
String
|
|
279
|
+
)
|
|
280
|
+
case valueEnter(
|
|
281
|
+
Any,
|
|
282
|
+
ParsedValueNodeBox,
|
|
283
|
+
String
|
|
284
|
+
)
|
|
285
|
+
case valueListExit(
|
|
286
|
+
ParsedValueNodeBox,
|
|
287
|
+
[ParsedValueNodeBox],
|
|
288
|
+
String
|
|
289
|
+
)
|
|
290
|
+
case valueMapExit(
|
|
291
|
+
ParsedValueNodeBox,
|
|
292
|
+
[(String, ParsedValueNodeBox)],
|
|
293
|
+
String
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private struct StageDescriptor {
|
|
298
|
+
let name: String
|
|
299
|
+
let options: [String: Any]
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
static func parse(
|
|
303
|
+
pipeline: NSDictionary?,
|
|
304
|
+
options: NSDictionary?
|
|
305
|
+
) throws -> RNFBFirestoreParsedPipelineRequest {
|
|
306
|
+
guard let pipeline = pipeline as? [String: Any] else {
|
|
307
|
+
throw PipelineValidationError("pipelineExecute() expected a pipeline object.")
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return try parsePipelineMap(pipeline, options: options as? [String: Any])
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private static func parsePipelineMap(
|
|
314
|
+
_ pipeline: [String: Any],
|
|
315
|
+
options: [String: Any]?
|
|
316
|
+
) throws -> RNFBFirestoreParsedPipelineRequest {
|
|
317
|
+
let rootBox = ParsedPipelineRequestBox()
|
|
318
|
+
var stack: [PipelineParseFrame] = [
|
|
319
|
+
.enter(pipeline: pipeline, options: options, box: rootBox),
|
|
320
|
+
]
|
|
321
|
+
|
|
322
|
+
while let frame = stack.popLast() {
|
|
323
|
+
switch frame {
|
|
324
|
+
case let .enter(pipeline, options, box):
|
|
325
|
+
let source = try parseSource(requireMap(pipeline, key: "source", fieldName: "pipeline.source"))
|
|
326
|
+
let stageMaps = try requireStageArray(pipeline, key: "stages", fieldName: "pipeline.stages")
|
|
327
|
+
let executeOptions = try parseOptions(options)
|
|
328
|
+
var stages: [PendingParsedStage] = []
|
|
329
|
+
var nestedPipelines: [([String: Any], ParsedPipelineRequestBox)] = []
|
|
330
|
+
stages.reserveCapacity(stageMaps.count)
|
|
331
|
+
|
|
332
|
+
for (index, stage) in stageMaps.enumerated() {
|
|
333
|
+
let fieldName = "pipeline.stages[\(index)]"
|
|
334
|
+
let descriptor = try parseStageDescriptor(stage, fieldName: fieldName)
|
|
335
|
+
if descriptor.name == "union" {
|
|
336
|
+
let childBox = ParsedPipelineRequestBox()
|
|
337
|
+
stages.append(.union(childBox))
|
|
338
|
+
nestedPipelines.append((
|
|
339
|
+
try requireMap(descriptor.options, key: "other", fieldName: "\(fieldName).options.other"),
|
|
340
|
+
childBox
|
|
341
|
+
))
|
|
342
|
+
} else {
|
|
343
|
+
stages.append(.ready(try parseStage(
|
|
344
|
+
stageName: descriptor.name,
|
|
345
|
+
options: descriptor.options,
|
|
346
|
+
fieldName: fieldName
|
|
347
|
+
)))
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
stack.append(.exit(
|
|
352
|
+
source: source,
|
|
353
|
+
stages: stages,
|
|
354
|
+
options: executeOptions,
|
|
355
|
+
box: box
|
|
356
|
+
))
|
|
357
|
+
|
|
358
|
+
for (nestedPipeline, childBox) in nestedPipelines.reversed() {
|
|
359
|
+
stack.append(.enter(pipeline: nestedPipeline, options: nil, box: childBox))
|
|
360
|
+
}
|
|
361
|
+
case let .exit(source, pendingStages, options, box):
|
|
362
|
+
var stages: [RNFBFirestoreParsedPipelineStage] = []
|
|
363
|
+
stages.reserveCapacity(pendingStages.count)
|
|
364
|
+
|
|
365
|
+
for pendingStage in pendingStages {
|
|
366
|
+
switch pendingStage {
|
|
367
|
+
case let .ready(stage):
|
|
368
|
+
stages.append(stage)
|
|
369
|
+
case let .union(childBox):
|
|
370
|
+
guard let other = childBox.value else {
|
|
371
|
+
throw PipelineValidationError("pipelineExecute() failed to parse nested union pipeline.")
|
|
372
|
+
}
|
|
373
|
+
stages.append(.unionStage(RNFBFirestoreParsedUnionStage(other: other)))
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
box.value = RNFBFirestoreParsedPipelineRequest(source: source, stages: stages, options: options)
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
guard let request = rootBox.value else {
|
|
382
|
+
throw PipelineValidationError("pipelineExecute() expected a pipeline object.")
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return request
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
private static func parseSource(_ source: [String: Any]) throws -> RNFBFirestoreParsedPipelineSource {
|
|
389
|
+
let sourceType = try requireNonEmptyString(source, key: "source", fieldName: "pipeline.source.source")
|
|
390
|
+
|
|
391
|
+
guard sourceTypes.contains(sourceType) else {
|
|
392
|
+
throw PipelineValidationError("pipelineExecute() received an unknown source type.")
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
switch sourceType {
|
|
396
|
+
case "collection":
|
|
397
|
+
return RNFBFirestoreParsedPipelineSource(
|
|
398
|
+
sourceType: sourceType,
|
|
399
|
+
path: try requireNonEmptyString(source, key: "path", fieldName: "pipeline.source.path"),
|
|
400
|
+
collectionId: nil,
|
|
401
|
+
documents: [],
|
|
402
|
+
queryType: nil,
|
|
403
|
+
query: nil,
|
|
404
|
+
rawOptions: try parseUnsupportedSourceRawOptions(source, sourceType: sourceType)
|
|
405
|
+
)
|
|
406
|
+
case "collectionGroup":
|
|
407
|
+
return RNFBFirestoreParsedPipelineSource(
|
|
408
|
+
sourceType: sourceType,
|
|
409
|
+
path: nil,
|
|
410
|
+
collectionId: try requireNonEmptyString(source, key: "collectionId", fieldName: "pipeline.source.collectionId"),
|
|
411
|
+
documents: [],
|
|
412
|
+
queryType: nil,
|
|
413
|
+
query: nil,
|
|
414
|
+
rawOptions: try parseUnsupportedSourceRawOptions(source, sourceType: sourceType)
|
|
415
|
+
)
|
|
416
|
+
case "database":
|
|
417
|
+
return RNFBFirestoreParsedPipelineSource(
|
|
418
|
+
sourceType: sourceType,
|
|
419
|
+
path: nil,
|
|
420
|
+
collectionId: nil,
|
|
421
|
+
documents: [],
|
|
422
|
+
queryType: nil,
|
|
423
|
+
query: nil,
|
|
424
|
+
rawOptions: try parseUnsupportedSourceRawOptions(source, sourceType: sourceType)
|
|
425
|
+
)
|
|
426
|
+
case "documents":
|
|
427
|
+
let documents = try requireArray(source, key: "documents", fieldName: "pipeline.source.documents")
|
|
428
|
+
guard !documents.isEmpty else {
|
|
429
|
+
throw PipelineValidationError(
|
|
430
|
+
"pipelineExecute() expected pipeline.source.documents to contain at least one document path.")
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
let parsedDocuments = try documents.enumerated().map { index, value in
|
|
434
|
+
guard let documentPath = value as? String else {
|
|
435
|
+
throw PipelineValidationError(
|
|
436
|
+
"pipelineExecute() expected pipeline.source.documents entries to be strings.")
|
|
437
|
+
}
|
|
438
|
+
return documentPath
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return RNFBFirestoreParsedPipelineSource(
|
|
442
|
+
sourceType: sourceType,
|
|
443
|
+
path: nil,
|
|
444
|
+
collectionId: nil,
|
|
445
|
+
documents: parsedDocuments,
|
|
446
|
+
queryType: nil,
|
|
447
|
+
query: nil,
|
|
448
|
+
rawOptions: nil
|
|
449
|
+
)
|
|
450
|
+
case "query":
|
|
451
|
+
return RNFBFirestoreParsedPipelineSource(
|
|
452
|
+
sourceType: sourceType,
|
|
453
|
+
path: try requireNonEmptyString(source, key: "path", fieldName: "pipeline.source.path"),
|
|
454
|
+
collectionId: nil,
|
|
455
|
+
documents: [],
|
|
456
|
+
queryType: try requireNonEmptyString(source, key: "queryType", fieldName: "pipeline.source.queryType"),
|
|
457
|
+
query: try parseQuerySource(source),
|
|
458
|
+
rawOptions: nil
|
|
459
|
+
)
|
|
460
|
+
default:
|
|
461
|
+
throw PipelineValidationError("pipelineExecute() received an unknown source type.")
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
private static func parseQuerySource(_ source: [String: Any]) throws -> RNFBFirestoreParsedQuerySource {
|
|
466
|
+
let filters = try requireArray(source, key: "filters", fieldName: "pipeline.source.filters")
|
|
467
|
+
let orders = try requireArray(source, key: "orders", fieldName: "pipeline.source.orders")
|
|
468
|
+
let options = try requireMap(source, key: "options", fieldName: "pipeline.source.options")
|
|
469
|
+
|
|
470
|
+
return RNFBFirestoreParsedQuerySource(
|
|
471
|
+
filters: try filters.enumerated().map { index, value in
|
|
472
|
+
try parseQuerySourceValueNode(value, fieldName: "pipeline.source.filters[\(index)]")
|
|
473
|
+
},
|
|
474
|
+
orders: try orders.enumerated().map { index, value in
|
|
475
|
+
try parseQuerySourceValueNode(value, fieldName: "pipeline.source.orders[\(index)]")
|
|
476
|
+
},
|
|
477
|
+
options: try options.reduce(into: [String: RNFBFirestoreParsedValueNode]()) { result, entry in
|
|
478
|
+
result[entry.key] = try parseQuerySourceValueNode(
|
|
479
|
+
entry.value,
|
|
480
|
+
fieldName: "pipeline.source.options.\(entry.key)"
|
|
481
|
+
)
|
|
482
|
+
}
|
|
483
|
+
)
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
private static func parseStageDescriptor(
|
|
487
|
+
_ stage: [String: Any],
|
|
488
|
+
fieldName: String
|
|
489
|
+
) throws -> StageDescriptor {
|
|
490
|
+
guard let stageName = stage["stage"] as? String else {
|
|
491
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).stage to be a string.")
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
guard !stageName.isEmpty else {
|
|
495
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).stage to be a non-empty string.")
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
guard knownStages.contains(stageName) else {
|
|
499
|
+
throw PipelineValidationError("pipelineExecute() received an unknown stage: \(stageName).")
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
let options = try requireMap(stage, key: "options", fieldName: "\(fieldName).options")
|
|
503
|
+
return StageDescriptor(name: stageName, options: options)
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
private static func parseStage(
|
|
507
|
+
stageName: String,
|
|
508
|
+
options: [String: Any],
|
|
509
|
+
fieldName: String
|
|
510
|
+
) throws -> RNFBFirestoreParsedPipelineStage {
|
|
511
|
+
switch stageName {
|
|
512
|
+
case "where":
|
|
513
|
+
return .whereStage(RNFBFirestoreParsedWhereStage(
|
|
514
|
+
condition: try parseExpressionNode(
|
|
515
|
+
requireValue(options, key: "condition", fieldName: "\(fieldName).options.condition"),
|
|
516
|
+
fieldName: "\(fieldName).options.condition"
|
|
517
|
+
)
|
|
518
|
+
))
|
|
519
|
+
case "select":
|
|
520
|
+
return .selectStage(RNFBFirestoreParsedSelectStage(
|
|
521
|
+
selections: try parseSelectableNodes(
|
|
522
|
+
requireArray(options, key: "selections", fieldName: "\(fieldName).options.selections"),
|
|
523
|
+
fieldName: "\(fieldName).options.selections"
|
|
524
|
+
)
|
|
525
|
+
))
|
|
526
|
+
case "addFields":
|
|
527
|
+
return .addFieldsStage(RNFBFirestoreParsedAddFieldsStage(
|
|
528
|
+
fields: try parseSelectableNodes(
|
|
529
|
+
requireArray(options, key: "fields", fieldName: "\(fieldName).options.fields"),
|
|
530
|
+
fieldName: "\(fieldName).options.fields"
|
|
531
|
+
)
|
|
532
|
+
))
|
|
533
|
+
case "removeFields":
|
|
534
|
+
return .removeFieldsStage(RNFBFirestoreParsedRemoveFieldsStage(
|
|
535
|
+
fields: try requireStringArray(options, key: "fields", fieldName: "\(fieldName).options.fields")
|
|
536
|
+
))
|
|
537
|
+
case "sort":
|
|
538
|
+
return .sortStage(RNFBFirestoreParsedSortStage(
|
|
539
|
+
orderings: try parseOrderingNodes(
|
|
540
|
+
requireArray(options, key: "orderings", fieldName: "\(fieldName).options.orderings"),
|
|
541
|
+
fieldName: "\(fieldName).options.orderings"
|
|
542
|
+
)
|
|
543
|
+
))
|
|
544
|
+
case "limit":
|
|
545
|
+
return .limitStage(RNFBFirestoreParsedLimitStage(
|
|
546
|
+
limit: try requireNumber(options, key: "limit", fieldName: "\(fieldName).options.limit")
|
|
547
|
+
))
|
|
548
|
+
case "offset":
|
|
549
|
+
return .offsetStage(RNFBFirestoreParsedOffsetStage(
|
|
550
|
+
offset: try requireNumber(options, key: "offset", fieldName: "\(fieldName).options.offset")
|
|
551
|
+
))
|
|
552
|
+
case "aggregate":
|
|
553
|
+
return .aggregateStage(try parseAggregateStage(options, fieldName: "\(fieldName).options"))
|
|
554
|
+
case "distinct":
|
|
555
|
+
return .distinctStage(RNFBFirestoreParsedDistinctStage(
|
|
556
|
+
groups: try parseSelectableNodes(
|
|
557
|
+
requireArray(options, key: "groups", fieldName: "\(fieldName).options.groups"),
|
|
558
|
+
fieldName: "\(fieldName).options.groups"
|
|
559
|
+
)
|
|
560
|
+
))
|
|
561
|
+
case "findNearest":
|
|
562
|
+
return .findNearestStage(try parseFindNearestStage(options, fieldName: "\(fieldName).options"))
|
|
563
|
+
case "replaceWith":
|
|
564
|
+
return .replaceWithStage(RNFBFirestoreParsedReplaceWithStage(
|
|
565
|
+
value: try parseExpressionNode(
|
|
566
|
+
requireValue(options, key: "map", fieldName: "\(fieldName).options.map"),
|
|
567
|
+
fieldName: "\(fieldName).options.map"
|
|
568
|
+
)
|
|
569
|
+
))
|
|
570
|
+
case "sample":
|
|
571
|
+
return .sampleStage(try parseSampleStage(options, fieldName: "\(fieldName).options"))
|
|
572
|
+
case "union":
|
|
573
|
+
throw PipelineValidationError("pipelineExecute() failed to parse nested union pipeline.")
|
|
574
|
+
case "unnest":
|
|
575
|
+
return .unnestStage(RNFBFirestoreParsedUnnestStage(
|
|
576
|
+
selectable: try parseSelectableNode(
|
|
577
|
+
requireValue(options, key: "selectable", fieldName: "\(fieldName).options.selectable"),
|
|
578
|
+
fieldName: "\(fieldName).options.selectable"
|
|
579
|
+
),
|
|
580
|
+
indexField: try optionalExpressionNode(options, key: "indexField", fieldName: "\(fieldName).options.indexField")
|
|
581
|
+
))
|
|
582
|
+
case "rawStage":
|
|
583
|
+
return .rawStage(RNFBFirestoreParsedRawStage(
|
|
584
|
+
name: try requireNonEmptyString(options, key: "name", fieldName: "\(fieldName).options.name"),
|
|
585
|
+
params: options["params"],
|
|
586
|
+
options: try optionalMap(options, key: "options", fieldName: "\(fieldName).options.options")
|
|
587
|
+
))
|
|
588
|
+
default:
|
|
589
|
+
throw PipelineValidationError("pipelineExecute() received an unknown stage: \(stageName).")
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
private static func parseOptions(_ options: [String: Any]?) throws -> RNFBFirestoreParsedPipelineExecuteOptions {
|
|
594
|
+
guard let options else {
|
|
595
|
+
return RNFBFirestoreParsedPipelineExecuteOptions(indexMode: nil, rawOptions: nil)
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if let indexMode = options["indexMode"], !(indexMode is String) {
|
|
599
|
+
throw PipelineValidationError("pipelineExecute() expected options.indexMode to be a string.")
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if let indexMode = options["indexMode"] as? String {
|
|
603
|
+
guard indexMode == "recommended" else {
|
|
604
|
+
throw PipelineValidationError("pipelineExecute() only supports options.indexMode=\"recommended\".")
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
throw PipelineValidationError(
|
|
608
|
+
"pipelineExecute() does not support options.indexMode on iOS because the native Firestore pipeline SDK does not expose execute options.")
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if let rawOptions = options["rawOptions"] {
|
|
612
|
+
guard rawOptions is [String: Any] else {
|
|
613
|
+
throw PipelineValidationError("pipelineExecute() expected options.rawOptions to be an object.")
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
throw PipelineValidationError(
|
|
617
|
+
"pipelineExecute() does not support options.rawOptions on iOS because the native Firestore pipeline SDK does not expose execute options.")
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return RNFBFirestoreParsedPipelineExecuteOptions(indexMode: nil, rawOptions: nil)
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
private static func parseUnsupportedSourceRawOptions(
|
|
624
|
+
_ source: [String: Any],
|
|
625
|
+
sourceType: String
|
|
626
|
+
) throws -> [String: Any]? {
|
|
627
|
+
guard let rawOptions = source["rawOptions"] else {
|
|
628
|
+
return nil
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
guard rawOptions is [String: Any] else {
|
|
632
|
+
throw PipelineValidationError("pipelineExecute() expected pipeline.source.rawOptions to be an object.")
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
throw PipelineValidationError(
|
|
636
|
+
"pipelineExecute() does not support pipeline.source.rawOptions for \(sourceType) on iOS because the native Firestore pipeline SDK does not expose source options.")
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
private static func requireMap(
|
|
640
|
+
_ map: [String: Any],
|
|
641
|
+
key: String,
|
|
642
|
+
fieldName: String
|
|
643
|
+
) throws -> [String: Any] {
|
|
644
|
+
guard let value = map[key] as? [String: Any] else {
|
|
645
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be an object.")
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
return value
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
private static func requireStageArray(
|
|
652
|
+
_ map: [String: Any],
|
|
653
|
+
key: String,
|
|
654
|
+
fieldName: String
|
|
655
|
+
) throws -> [[String: Any]] {
|
|
656
|
+
guard let value = map[key] as? [[String: Any]] else {
|
|
657
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be an array.")
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
return value
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
private static func requireArray(
|
|
664
|
+
_ map: [String: Any],
|
|
665
|
+
key: String,
|
|
666
|
+
fieldName: String
|
|
667
|
+
) throws -> [Any] {
|
|
668
|
+
guard let value = map[key] as? [Any] else {
|
|
669
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be an array.")
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
return value
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
private static func requireNonEmptyString(
|
|
676
|
+
_ map: [String: Any],
|
|
677
|
+
key: String,
|
|
678
|
+
fieldName: String
|
|
679
|
+
) throws -> String {
|
|
680
|
+
guard let value = map[key] as? String else {
|
|
681
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be a string.")
|
|
682
|
+
}
|
|
683
|
+
guard !value.isEmpty else {
|
|
684
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be a non-empty string.")
|
|
685
|
+
}
|
|
686
|
+
return value
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
private static func requireValue(
|
|
690
|
+
_ map: [String: Any],
|
|
691
|
+
key: String,
|
|
692
|
+
fieldName: String
|
|
693
|
+
) throws -> Any {
|
|
694
|
+
guard let value = map[key] else {
|
|
695
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be provided.")
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
return value
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
private static func optionalMap(
|
|
702
|
+
_ map: [String: Any],
|
|
703
|
+
key: String,
|
|
704
|
+
fieldName: String
|
|
705
|
+
) throws -> [String: Any]? {
|
|
706
|
+
guard let value = map[key] else {
|
|
707
|
+
return nil
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
guard let value = value as? [String: Any] else {
|
|
711
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be an object.")
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return value
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
private static func optionalExpressionNode(
|
|
718
|
+
_ map: [String: Any],
|
|
719
|
+
key: String,
|
|
720
|
+
fieldName: String
|
|
721
|
+
) throws -> RNFBFirestoreParsedExpressionNode? {
|
|
722
|
+
guard let value = map[key] else {
|
|
723
|
+
return nil
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
return try parseExpressionNode(value, fieldName: fieldName)
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
private static func requireNumber(
|
|
730
|
+
_ map: [String: Any],
|
|
731
|
+
key: String,
|
|
732
|
+
fieldName: String
|
|
733
|
+
) throws -> NSNumber {
|
|
734
|
+
guard let value = map[key] as? NSNumber else {
|
|
735
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be a number.")
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return value
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
private static func optionalNumber(
|
|
742
|
+
_ map: [String: Any],
|
|
743
|
+
key: String,
|
|
744
|
+
fieldName: String
|
|
745
|
+
) throws -> NSNumber? {
|
|
746
|
+
guard let value = map[key] else {
|
|
747
|
+
return nil
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
guard let number = value as? NSNumber else {
|
|
751
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be a number.")
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
return number
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
private static func requireStringArray(
|
|
758
|
+
_ map: [String: Any],
|
|
759
|
+
key: String,
|
|
760
|
+
fieldName: String
|
|
761
|
+
) throws -> [String] {
|
|
762
|
+
let values = try requireArray(map, key: key, fieldName: fieldName)
|
|
763
|
+
guard !values.isEmpty else {
|
|
764
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to contain at least one value.")
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
return try values.enumerated().map { index, value in
|
|
768
|
+
guard let stringValue = value as? String else {
|
|
769
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName)[\(index)] to be a string.")
|
|
770
|
+
}
|
|
771
|
+
return stringValue
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
private static func parseAggregateStage(
|
|
776
|
+
_ options: [String: Any],
|
|
777
|
+
fieldName: String
|
|
778
|
+
) throws -> RNFBFirestoreParsedAggregateStage {
|
|
779
|
+
let accumulators = try parseAggregateNodes(
|
|
780
|
+
requireArray(options, key: "accumulators", fieldName: "\(fieldName).accumulators"),
|
|
781
|
+
fieldName: "\(fieldName).accumulators"
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
let groups: [RNFBFirestoreParsedSelectableNode]
|
|
785
|
+
if options["groups"] != nil {
|
|
786
|
+
groups = try parseSelectableNodes(
|
|
787
|
+
requireArray(options, key: "groups", fieldName: "\(fieldName).groups"),
|
|
788
|
+
fieldName: "\(fieldName).groups",
|
|
789
|
+
requireNonEmpty: false
|
|
790
|
+
)
|
|
791
|
+
} else {
|
|
792
|
+
groups = []
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
return RNFBFirestoreParsedAggregateStage(accumulators: accumulators, groups: groups)
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
private static func parseFindNearestStage(
|
|
799
|
+
_ options: [String: Any],
|
|
800
|
+
fieldName: String
|
|
801
|
+
) throws -> RNFBFirestoreParsedFindNearestStage {
|
|
802
|
+
let distanceMeasure = try requireNonEmptyString(
|
|
803
|
+
options,
|
|
804
|
+
key: "distanceMeasure",
|
|
805
|
+
fieldName: "\(fieldName).distanceMeasure"
|
|
806
|
+
)
|
|
807
|
+
|
|
808
|
+
return RNFBFirestoreParsedFindNearestStage(
|
|
809
|
+
field: try parseExpressionNode(
|
|
810
|
+
requireValue(options, key: "field", fieldName: "\(fieldName).field"),
|
|
811
|
+
fieldName: "\(fieldName).field"
|
|
812
|
+
),
|
|
813
|
+
vectorValue: try parseValueNode(
|
|
814
|
+
requireValue(options, key: "vectorValue", fieldName: "\(fieldName).vectorValue"),
|
|
815
|
+
fieldName: "\(fieldName).vectorValue"
|
|
816
|
+
),
|
|
817
|
+
distanceMeasure: distanceMeasure,
|
|
818
|
+
limit: try optionalNumber(options, key: "limit", fieldName: "\(fieldName).limit"),
|
|
819
|
+
distanceField: try optionalExpressionNode(options, key: "distanceField", fieldName: "\(fieldName).distanceField")
|
|
820
|
+
)
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
private static func parseSampleStage(
|
|
824
|
+
_ options: [String: Any],
|
|
825
|
+
fieldName: String
|
|
826
|
+
) throws -> RNFBFirestoreParsedSampleStage {
|
|
827
|
+
let documents = options["documents"]
|
|
828
|
+
let percentage = options["percentage"]
|
|
829
|
+
|
|
830
|
+
if documents == nil && percentage == nil {
|
|
831
|
+
throw PipelineValidationError("pipelineExecute() expected sample stage to include documents or percentage.")
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
return RNFBFirestoreParsedSampleStage(
|
|
835
|
+
documents: try optionalNumber(options, key: "documents", fieldName: "\(fieldName).documents"),
|
|
836
|
+
percentage: try optionalNumber(options, key: "percentage", fieldName: "\(fieldName).percentage")
|
|
837
|
+
)
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
private static func parseSelectableNodes(
|
|
841
|
+
_ values: [Any],
|
|
842
|
+
fieldName: String,
|
|
843
|
+
requireNonEmpty: Bool = true
|
|
844
|
+
) throws -> [RNFBFirestoreParsedSelectableNode] {
|
|
845
|
+
if requireNonEmpty && values.isEmpty {
|
|
846
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to contain at least one value.")
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
return try values.enumerated().map { index, value in
|
|
850
|
+
try parseSelectableNode(value, fieldName: "\(fieldName)[\(index)]")
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
private static func parseSelectableNode(
|
|
855
|
+
_ value: Any,
|
|
856
|
+
fieldName: String
|
|
857
|
+
) throws -> RNFBFirestoreParsedSelectableNode {
|
|
858
|
+
if value is String {
|
|
859
|
+
return RNFBFirestoreParsedSelectableNode(
|
|
860
|
+
expression: try parseExpressionNode(value, fieldName: fieldName),
|
|
861
|
+
alias: nil,
|
|
862
|
+
isFlatFieldAlias: false
|
|
863
|
+
)
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
guard let map = value as? [String: Any] else {
|
|
867
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be a selectable expression.")
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
let alias = firstString(map["alias"], map["as"], map["name"])
|
|
871
|
+
if let alias, !alias.isEmpty {
|
|
872
|
+
if map["path"] != nil || map["fieldPath"] != nil || map["segments"] != nil {
|
|
873
|
+
return RNFBFirestoreParsedSelectableNode(
|
|
874
|
+
expression: .field(path: try coerceFieldPath(value, fieldName: "\(fieldName).path")),
|
|
875
|
+
alias: alias,
|
|
876
|
+
isFlatFieldAlias: true
|
|
877
|
+
)
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
let expressionValue = firstNonNil(map["expr"], map["expression"], map["field"]) ?? value
|
|
881
|
+
return RNFBFirestoreParsedSelectableNode(
|
|
882
|
+
expression: try parseExpressionNode(expressionValue, fieldName: "\(fieldName).expr"),
|
|
883
|
+
alias: alias,
|
|
884
|
+
isFlatFieldAlias: false
|
|
885
|
+
)
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
return RNFBFirestoreParsedSelectableNode(
|
|
889
|
+
expression: try parseExpressionNode(value, fieldName: fieldName),
|
|
890
|
+
alias: nil,
|
|
891
|
+
isFlatFieldAlias: false
|
|
892
|
+
)
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
private static func parseOrderingNodes(
|
|
896
|
+
_ values: [Any],
|
|
897
|
+
fieldName: String
|
|
898
|
+
) throws -> [RNFBFirestoreParsedOrderingNode] {
|
|
899
|
+
guard !values.isEmpty else {
|
|
900
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to contain at least one value.")
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
return try values.enumerated().map { index, value in
|
|
904
|
+
try parseOrderingNode(value, fieldName: "\(fieldName)[\(index)]")
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
private static func parseOrderingNode(
|
|
909
|
+
_ value: Any,
|
|
910
|
+
fieldName: String
|
|
911
|
+
) throws -> RNFBFirestoreParsedOrderingNode {
|
|
912
|
+
if value is String {
|
|
913
|
+
return RNFBFirestoreParsedOrderingNode(
|
|
914
|
+
expression: try parseExpressionNode(value, fieldName: fieldName),
|
|
915
|
+
descending: false,
|
|
916
|
+
fieldShortcut: true
|
|
917
|
+
)
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
guard let map = value as? [String: Any] else {
|
|
921
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be a string or object.")
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
let direction = (map["direction"] as? String) ?? "asc"
|
|
925
|
+
let expressionValue = firstNonNil(
|
|
926
|
+
map["expression"],
|
|
927
|
+
map["expr"],
|
|
928
|
+
map["field"],
|
|
929
|
+
map["fieldPath"],
|
|
930
|
+
map["path"],
|
|
931
|
+
value
|
|
932
|
+
) as Any
|
|
933
|
+
|
|
934
|
+
return RNFBFirestoreParsedOrderingNode(
|
|
935
|
+
expression: try parseExpressionNode(expressionValue, fieldName: fieldName),
|
|
936
|
+
descending: isDescendingDirection(direction),
|
|
937
|
+
fieldShortcut: false
|
|
938
|
+
)
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
private static func parseAggregateNodes(
|
|
942
|
+
_ values: [Any],
|
|
943
|
+
fieldName: String
|
|
944
|
+
) throws -> [RNFBFirestoreParsedAggregateNode] {
|
|
945
|
+
guard !values.isEmpty else {
|
|
946
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to contain at least one value.")
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
return try values.enumerated().map { index, value in
|
|
950
|
+
try parseAggregateNode(value, fieldName: "\(fieldName)[\(index)]")
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
private static func parseAggregateNode(
|
|
955
|
+
_ value: Any,
|
|
956
|
+
fieldName: String
|
|
957
|
+
) throws -> RNFBFirestoreParsedAggregateNode {
|
|
958
|
+
guard let map = value as? [String: Any] else {
|
|
959
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be an object.")
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
let aggregateValue = map["aggregate"] ?? value
|
|
963
|
+
guard let aggregateMap = aggregateValue as? [String: Any] else {
|
|
964
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).aggregate to be an object.")
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
guard let kind = firstString(
|
|
968
|
+
aggregateMap["kind"],
|
|
969
|
+
aggregateMap["name"],
|
|
970
|
+
aggregateMap["function"],
|
|
971
|
+
aggregateMap["op"]
|
|
972
|
+
), !kind.isEmpty else {
|
|
973
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to include an aggregate kind.")
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
let alias = firstString(map["alias"], map["as"], map["name"]) ?? kind.lowercased()
|
|
977
|
+
let primaryExpression = firstNonNil(aggregateMap["expr"], aggregateMap["field"]) ?? aggregateMap["value"]
|
|
978
|
+
let primaryValue = try primaryExpression.map {
|
|
979
|
+
try parseValueNode($0, fieldName: "\(fieldName).expr")
|
|
980
|
+
}
|
|
981
|
+
let args = try parseArgumentValueNodes(aggregateMap["args"], fieldName: "\(fieldName).args")
|
|
982
|
+
|
|
983
|
+
return RNFBFirestoreParsedAggregateNode(kind: kind, alias: alias, primaryValue: primaryValue, args: args)
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
private static func parseExpressionNode(
|
|
987
|
+
_ value: Any,
|
|
988
|
+
fieldName: String
|
|
989
|
+
) throws -> RNFBFirestoreParsedExpressionNode {
|
|
990
|
+
let rootBox = ParsedExpressionNodeBox()
|
|
991
|
+
try parseExpressionValueTree(.expressionEnter(value, rootBox, fieldName))
|
|
992
|
+
|
|
993
|
+
guard let expression = rootBox.value else {
|
|
994
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be provided.")
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
return expression
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
private static func parseArgumentValueNodes(
|
|
1001
|
+
_ argsValue: Any?,
|
|
1002
|
+
fieldName: String
|
|
1003
|
+
) throws -> [RNFBFirestoreParsedValueNode] {
|
|
1004
|
+
guard let argsValue else {
|
|
1005
|
+
return []
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
if let rawArgs = argsValue as? [Any] {
|
|
1009
|
+
return try rawArgs.enumerated().map { index, value in
|
|
1010
|
+
try parseValueNode(value, fieldName: "\(fieldName)[\(index)]")
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
return [try parseValueNode(argsValue, fieldName: "\(fieldName)[0]")]
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
private static func parseValueNode(
|
|
1018
|
+
_ value: Any,
|
|
1019
|
+
fieldName: String
|
|
1020
|
+
) throws -> RNFBFirestoreParsedValueNode {
|
|
1021
|
+
let rootBox = ParsedValueNodeBox()
|
|
1022
|
+
try parseExpressionValueTree(.valueEnter(value, rootBox, fieldName))
|
|
1023
|
+
|
|
1024
|
+
guard let parsedValue = rootBox.value else {
|
|
1025
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be provided.")
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
return parsedValue
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
private static func parseQuerySourceValueNode(
|
|
1032
|
+
_ value: Any,
|
|
1033
|
+
fieldName: String
|
|
1034
|
+
) throws -> RNFBFirestoreParsedValueNode {
|
|
1035
|
+
let rootBox = ParsedValueNodeBox()
|
|
1036
|
+
var stack: [QuerySourceValueParseFrame] = [
|
|
1037
|
+
.enter(value, rootBox),
|
|
1038
|
+
]
|
|
1039
|
+
|
|
1040
|
+
while let frame = stack.popLast() {
|
|
1041
|
+
switch frame {
|
|
1042
|
+
case let .enter(value, box):
|
|
1043
|
+
if let map = value as? [String: Any] {
|
|
1044
|
+
let entries = map.map { (key: $0.key, value: $0.value, box: ParsedValueNodeBox()) }
|
|
1045
|
+
stack.append(.exitMap(box, entries.map { ($0.key, $0.box) }))
|
|
1046
|
+
for entry in entries.reversed() {
|
|
1047
|
+
stack.append(.enter(entry.value, entry.box))
|
|
1048
|
+
}
|
|
1049
|
+
continue
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
if let list = value as? [Any] {
|
|
1053
|
+
let childBoxes = list.map { _ in ParsedValueNodeBox() }
|
|
1054
|
+
stack.append(.exitList(box, childBoxes))
|
|
1055
|
+
for index in list.indices.reversed() {
|
|
1056
|
+
stack.append(.enter(list[index], childBoxes[index]))
|
|
1057
|
+
}
|
|
1058
|
+
continue
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
box.value = .primitive(value)
|
|
1062
|
+
case let .exitList(box, childBoxes):
|
|
1063
|
+
box.value = .list(try childBoxes.enumerated().map { index, childBox in
|
|
1064
|
+
guard let value = childBox.value else {
|
|
1065
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName)[\(index)] to be provided.")
|
|
1066
|
+
}
|
|
1067
|
+
return value
|
|
1068
|
+
})
|
|
1069
|
+
case let .exitMap(box, entries):
|
|
1070
|
+
box.value = .map(try entries.reduce(into: [String: RNFBFirestoreParsedValueNode]()) { result, entry in
|
|
1071
|
+
guard let value = entry.1.value else {
|
|
1072
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).\(entry.0) to be provided.")
|
|
1073
|
+
}
|
|
1074
|
+
result[entry.0] = value
|
|
1075
|
+
})
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
guard let parsedValue = rootBox.value else {
|
|
1080
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be provided.")
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
return parsedValue
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
private static func isExpressionLike(_ map: [String: Any]) -> Bool {
|
|
1087
|
+
map["exprType"] != nil || map["operator"] != nil || map["name"] != nil || map["expr"] != nil ||
|
|
1088
|
+
map["expression"] != nil || map["fieldPath"] != nil || map["path"] != nil ||
|
|
1089
|
+
map["segments"] != nil || map["_segments"] != nil
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
private static func coerceFieldPath(
|
|
1093
|
+
_ value: Any,
|
|
1094
|
+
fieldName: String
|
|
1095
|
+
) throws -> String {
|
|
1096
|
+
var currentValue: Any = value
|
|
1097
|
+
|
|
1098
|
+
while true {
|
|
1099
|
+
if let fieldPath = currentValue as? String, !fieldPath.isEmpty {
|
|
1100
|
+
return fieldPath
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
if let map = currentValue as? [String: Any] {
|
|
1104
|
+
let path = firstNonNil(map["path"], map["fieldPath"])
|
|
1105
|
+
if let path, !(path is [String: Any]) {
|
|
1106
|
+
currentValue = path
|
|
1107
|
+
continue
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
let segments = (map["segments"] as? [Any]) ?? (map["_segments"] as? [Any])
|
|
1111
|
+
if let segments {
|
|
1112
|
+
let stringSegments = try segments.enumerated().map { _, segment -> String in
|
|
1113
|
+
guard let stringSegment = segment as? String else {
|
|
1114
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) segment values to be strings.")
|
|
1115
|
+
}
|
|
1116
|
+
return stringSegment
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
let pathValue = stringSegments.joined(separator: ".")
|
|
1120
|
+
if !pathValue.isEmpty {
|
|
1121
|
+
return pathValue
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to resolve to a field path string.")
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
private static func parseExpressionValueTree(
|
|
1131
|
+
_ initialFrame: ExpressionValueParseFrame
|
|
1132
|
+
) throws {
|
|
1133
|
+
var stack: [ExpressionValueParseFrame] = [initialFrame]
|
|
1134
|
+
|
|
1135
|
+
while let frame = stack.popLast() {
|
|
1136
|
+
switch frame {
|
|
1137
|
+
case let .expressionEnter(value, box, fieldName):
|
|
1138
|
+
if let stringValue = value as? String {
|
|
1139
|
+
box.value = .field(path: stringValue)
|
|
1140
|
+
continue
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
if let map = value as? [String: Any] {
|
|
1144
|
+
if let nested = map["expr"] {
|
|
1145
|
+
stack.append(.expressionEnter(nested, box, "\(fieldName).expr"))
|
|
1146
|
+
continue
|
|
1147
|
+
}
|
|
1148
|
+
if let nested = map["expression"] {
|
|
1149
|
+
stack.append(.expressionEnter(nested, box, "\(fieldName).expression"))
|
|
1150
|
+
continue
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
if let operatorName = map["operator"] as? String {
|
|
1154
|
+
let normalizedOperator = operatorName.uppercased()
|
|
1155
|
+
if normalizedOperator == "AND" || normalizedOperator == "OR" {
|
|
1156
|
+
guard let queries = map["queries"] as? [Any], !queries.isEmpty else {
|
|
1157
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).queries to contain boolean expressions.")
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
let queryBoxes = queries.map { _ in ParsedExpressionNodeBox() }
|
|
1161
|
+
stack.append(.expressionOperatorLogicalExit(box, normalizedOperator, queryBoxes, fieldName))
|
|
1162
|
+
for index in queries.indices.reversed() {
|
|
1163
|
+
stack.append(.expressionEnter(queries[index], queryBoxes[index], "\(fieldName).queries[\(index)]"))
|
|
1164
|
+
}
|
|
1165
|
+
continue
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
let fieldValue = map["fieldPath"] ?? map["field"]
|
|
1169
|
+
guard let fieldValue else {
|
|
1170
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).fieldPath to be provided.")
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
let rightValue = map["value"] ?? map["right"] ?? map["operand"] ?? NSNull()
|
|
1174
|
+
let fieldBox = ParsedExpressionNodeBox()
|
|
1175
|
+
let valueBox = ParsedValueNodeBox()
|
|
1176
|
+
stack.append(.expressionOperatorBinaryExit(box, normalizedOperator, fieldBox, valueBox, fieldName))
|
|
1177
|
+
stack.append(.valueEnter(rightValue, valueBox, "\(fieldName).value"))
|
|
1178
|
+
stack.append(.expressionEnter(fieldValue, fieldBox, "\(fieldName).fieldPath"))
|
|
1179
|
+
continue
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
if let exprType = map["exprType"] as? String {
|
|
1183
|
+
let normalizedType = exprType.lowercased()
|
|
1184
|
+
if normalizedType == "field" {
|
|
1185
|
+
box.value = .field(path: try coerceFieldPath(value, fieldName: fieldName))
|
|
1186
|
+
continue
|
|
1187
|
+
}
|
|
1188
|
+
if normalizedType == "constant" {
|
|
1189
|
+
let valueBox = ParsedValueNodeBox()
|
|
1190
|
+
stack.append(.expressionConstantExit(box, valueBox, fieldName))
|
|
1191
|
+
stack.append(.valueEnter(map["value"] as Any, valueBox, "\(fieldName).value"))
|
|
1192
|
+
continue
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
if map["name"] != nil {
|
|
1197
|
+
guard let nameValue = map["name"] as? String, !nameValue.isEmpty else {
|
|
1198
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).name to be a non-empty string.")
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
let rawArgs: [Any]
|
|
1202
|
+
if let args = map["args"] as? [Any] {
|
|
1203
|
+
rawArgs = args
|
|
1204
|
+
} else if let singleArg = map["args"] {
|
|
1205
|
+
rawArgs = [singleArg]
|
|
1206
|
+
} else {
|
|
1207
|
+
rawArgs = []
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
let argBoxes = rawArgs.map { _ in ParsedValueNodeBox() }
|
|
1211
|
+
stack.append(.expressionFunctionExit(box, nameValue, argBoxes, fieldName))
|
|
1212
|
+
for index in rawArgs.indices.reversed() {
|
|
1213
|
+
stack.append(.valueEnter(rawArgs[index], argBoxes[index], "\(fieldName).args[\(index)]"))
|
|
1214
|
+
}
|
|
1215
|
+
continue
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
if map["fieldPath"] != nil || map["path"] != nil || map["segments"] != nil || map["_segments"] != nil {
|
|
1219
|
+
box.value = .field(path: try coerceFieldPath(value, fieldName: fieldName))
|
|
1220
|
+
continue
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
let valueBox = ParsedValueNodeBox()
|
|
1225
|
+
stack.append(.expressionConstantExit(box, valueBox, fieldName))
|
|
1226
|
+
stack.append(.valueEnter(value, valueBox, fieldName))
|
|
1227
|
+
case let .expressionValueExit(box, expressionBox, fieldName):
|
|
1228
|
+
guard let expression = expressionBox.value else {
|
|
1229
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be provided.")
|
|
1230
|
+
}
|
|
1231
|
+
box.value = .expression(expression)
|
|
1232
|
+
case let .expressionConstantExit(box, valueBox, fieldName):
|
|
1233
|
+
guard let value = valueBox.value else {
|
|
1234
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName) to be provided.")
|
|
1235
|
+
}
|
|
1236
|
+
box.value = .constant(value)
|
|
1237
|
+
case let .expressionFunctionExit(box, name, argBoxes, fieldName):
|
|
1238
|
+
let args = try argBoxes.enumerated().map { index, argBox in
|
|
1239
|
+
guard let value = argBox.value else {
|
|
1240
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).args[\(index)] to be provided.")
|
|
1241
|
+
}
|
|
1242
|
+
return value
|
|
1243
|
+
}
|
|
1244
|
+
box.value = .function(name: name, args: args)
|
|
1245
|
+
case let .expressionOperatorLogicalExit(box, normalizedOperator, queryBoxes, fieldName):
|
|
1246
|
+
let args = try queryBoxes.enumerated().map { index, queryBox in
|
|
1247
|
+
guard let expression = queryBox.value else {
|
|
1248
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).queries[\(index)] to be provided.")
|
|
1249
|
+
}
|
|
1250
|
+
return RNFBFirestoreParsedValueNode.expression(expression)
|
|
1251
|
+
}
|
|
1252
|
+
box.value = .function(name: normalizedOperator == "AND" ? "and" : "or", args: args)
|
|
1253
|
+
case let .expressionOperatorBinaryExit(box, normalizedOperator, fieldBox, valueBox, fieldName):
|
|
1254
|
+
guard let fieldExpression = fieldBox.value else {
|
|
1255
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).fieldPath to be provided.")
|
|
1256
|
+
}
|
|
1257
|
+
guard let rightValue = valueBox.value else {
|
|
1258
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).value to be provided.")
|
|
1259
|
+
}
|
|
1260
|
+
box.value = .function(name: mapOperatorToFunction(normalizedOperator), args: [
|
|
1261
|
+
.expression(fieldExpression),
|
|
1262
|
+
rightValue,
|
|
1263
|
+
])
|
|
1264
|
+
case let .valueEnter(value, box, fieldName):
|
|
1265
|
+
if let map = value as? [String: Any] {
|
|
1266
|
+
if isExpressionLike(map) {
|
|
1267
|
+
let expressionBox = ParsedExpressionNodeBox()
|
|
1268
|
+
stack.append(.expressionValueExit(box, expressionBox, fieldName))
|
|
1269
|
+
stack.append(.expressionEnter(value, expressionBox, fieldName))
|
|
1270
|
+
continue
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
let entries = map.map { (key: $0.key, value: $0.value, box: ParsedValueNodeBox()) }
|
|
1274
|
+
stack.append(.valueMapExit(box, entries.map { ($0.key, $0.box) }, fieldName))
|
|
1275
|
+
for entry in entries.reversed() {
|
|
1276
|
+
stack.append(.valueEnter(entry.value, entry.box, "\(fieldName).\(entry.key)"))
|
|
1277
|
+
}
|
|
1278
|
+
continue
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
if let list = value as? [Any] {
|
|
1282
|
+
let childBoxes = list.map { _ in ParsedValueNodeBox() }
|
|
1283
|
+
stack.append(.valueListExit(box, childBoxes, fieldName))
|
|
1284
|
+
for index in list.indices.reversed() {
|
|
1285
|
+
stack.append(.valueEnter(list[index], childBoxes[index], "\(fieldName)[\(index)]"))
|
|
1286
|
+
}
|
|
1287
|
+
continue
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
box.value = .primitive(value)
|
|
1291
|
+
case let .valueListExit(box, childBoxes, fieldName):
|
|
1292
|
+
box.value = .list(try childBoxes.enumerated().map { index, childBox in
|
|
1293
|
+
guard let value = childBox.value else {
|
|
1294
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName)[\(index)] to be provided.")
|
|
1295
|
+
}
|
|
1296
|
+
return value
|
|
1297
|
+
})
|
|
1298
|
+
case let .valueMapExit(box, entries, fieldName):
|
|
1299
|
+
box.value = .map(try entries.reduce(into: [String: RNFBFirestoreParsedValueNode]()) { result, entry in
|
|
1300
|
+
guard let value = entry.1.value else {
|
|
1301
|
+
throw PipelineValidationError("pipelineExecute() expected \(fieldName).\(entry.0) to be provided.")
|
|
1302
|
+
}
|
|
1303
|
+
result[entry.0] = value
|
|
1304
|
+
})
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
private static func firstString(_ values: Any?...) -> String? {
|
|
1310
|
+
for value in values {
|
|
1311
|
+
if let stringValue = value as? String, !stringValue.isEmpty {
|
|
1312
|
+
return stringValue
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
return nil
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
private static func firstNonNil(_ values: Any?...) -> Any? {
|
|
1320
|
+
for value in values where value != nil {
|
|
1321
|
+
return value
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
return nil
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
private static func isDescendingDirection(_ direction: String?) -> Bool {
|
|
1328
|
+
guard let direction else {
|
|
1329
|
+
return false
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
let normalized = direction.lowercased()
|
|
1333
|
+
return normalized == "desc" || normalized == "descending"
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
private static func mapOperatorToFunction(_ operatorName: String) -> String {
|
|
1337
|
+
switch operatorName {
|
|
1338
|
+
case "==", "=", "EQUAL": return "equal"
|
|
1339
|
+
case "!=", "<>", "NOT_EQUAL": return "not_equal"
|
|
1340
|
+
case ">", "GREATER_THAN": return "greater_than"
|
|
1341
|
+
case ">=", "GREATER_THAN_OR_EQUAL": return "greater_than_or_equal"
|
|
1342
|
+
case "<", "LESS_THAN": return "less_than"
|
|
1343
|
+
case "<=", "LESS_THAN_OR_EQUAL": return "less_than_or_equal"
|
|
1344
|
+
case "ARRAY_CONTAINS", "ARRAY-CONTAINS": return "array_contains"
|
|
1345
|
+
case "ARRAY_CONTAINS_ANY", "ARRAY-CONTAINS-ANY": return "array_contains_any"
|
|
1346
|
+
case "ARRAY_CONTAINS_ALL", "ARRAY-CONTAINS-ALL": return "array_contains_all"
|
|
1347
|
+
case "IN": return "equal_any"
|
|
1348
|
+
case "NOT_IN": return "not_equal_any"
|
|
1349
|
+
default: return operatorName.lowercased()
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|