@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.
Files changed (359) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/RNFBFirestore.podspec +2 -1
  3. package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreCollectionModule.java +17 -4
  4. package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreDocumentModule.java +2 -2
  5. package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestorePipelineExecutor.java +1243 -0
  6. package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestorePipelineNodeBuilder.java +3919 -0
  7. package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestorePipelineParser.java +1735 -0
  8. package/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreSerialize.java +1 -1
  9. package/dist/module/FieldPath.js +59 -0
  10. package/dist/module/FieldPath.js.map +1 -0
  11. package/dist/module/FieldValue.js +82 -0
  12. package/dist/module/FieldValue.js.map +1 -0
  13. package/{lib → dist/module}/FirestoreAggregate.js +31 -43
  14. package/dist/module/FirestoreAggregate.js.map +1 -0
  15. package/dist/module/FirestoreBlob.js +56 -0
  16. package/dist/module/FirestoreBlob.js.map +1 -0
  17. package/dist/module/FirestoreCollectionReference.js +70 -0
  18. package/dist/module/FirestoreCollectionReference.js.map +1 -0
  19. package/{lib → dist/module}/FirestoreDocumentChange.js +12 -15
  20. package/dist/module/FirestoreDocumentChange.js.map +1 -0
  21. package/dist/module/FirestoreDocumentReference.js +170 -0
  22. package/dist/module/FirestoreDocumentReference.js.map +1 -0
  23. package/dist/module/FirestoreDocumentSnapshot.js +88 -0
  24. package/dist/module/FirestoreDocumentSnapshot.js.map +1 -0
  25. package/dist/module/FirestoreFilter.js +146 -0
  26. package/dist/module/FirestoreFilter.js.map +1 -0
  27. package/dist/module/FirestoreGeoPoint.js +80 -0
  28. package/dist/module/FirestoreGeoPoint.js.map +1 -0
  29. package/{lib → dist/module}/FirestorePath.js +5 -12
  30. package/dist/module/FirestorePath.js.map +1 -0
  31. package/{lib → dist/module}/FirestorePersistentCacheIndexManager.js +11 -4
  32. package/dist/module/FirestorePersistentCacheIndexManager.js.map +1 -0
  33. package/dist/module/FirestoreQuery.js +298 -0
  34. package/dist/module/FirestoreQuery.js.map +1 -0
  35. package/{lib → dist/module}/FirestoreQueryModifiers.js +25 -136
  36. package/dist/module/FirestoreQueryModifiers.js.map +1 -0
  37. package/dist/module/FirestoreQuerySnapshot.js +98 -0
  38. package/dist/module/FirestoreQuerySnapshot.js.map +1 -0
  39. package/dist/module/FirestoreSnapshotMetadata.js +38 -0
  40. package/dist/module/FirestoreSnapshotMetadata.js.map +1 -0
  41. package/dist/module/FirestoreStatics.js +50 -0
  42. package/dist/module/FirestoreStatics.js.map +1 -0
  43. package/{lib → dist/module}/FirestoreTimestamp.js +39 -39
  44. package/dist/module/FirestoreTimestamp.js.map +1 -0
  45. package/dist/module/FirestoreTransaction.js +113 -0
  46. package/dist/module/FirestoreTransaction.js.map +1 -0
  47. package/dist/module/FirestoreTransactionHandler.js +137 -0
  48. package/dist/module/FirestoreTransactionHandler.js.map +1 -0
  49. package/dist/module/FirestoreVectorValue.js +75 -0
  50. package/dist/module/FirestoreVectorValue.js.map +1 -0
  51. package/dist/module/FirestoreWriteBatch.js +113 -0
  52. package/dist/module/FirestoreWriteBatch.js.map +1 -0
  53. package/dist/module/LoadBundleTask.js +70 -0
  54. package/dist/module/LoadBundleTask.js.map +1 -0
  55. package/dist/module/index.js +31 -0
  56. package/dist/module/index.js.map +1 -0
  57. package/dist/module/modular/Bytes.js +67 -0
  58. package/dist/module/modular/Bytes.js.map +1 -0
  59. package/dist/module/modular/FieldPath.js +25 -0
  60. package/dist/module/modular/FieldPath.js.map +1 -0
  61. package/dist/module/modular/FieldValue.js +37 -0
  62. package/dist/module/modular/FieldValue.js.map +1 -0
  63. package/dist/module/modular/GeoPoint.js +22 -0
  64. package/dist/module/modular/GeoPoint.js.map +1 -0
  65. package/dist/module/modular/Timestamp.js +22 -0
  66. package/dist/module/modular/Timestamp.js.map +1 -0
  67. package/dist/module/modular/VectorValue.js +25 -0
  68. package/dist/module/modular/VectorValue.js.map +1 -0
  69. package/dist/module/modular/query.js +222 -0
  70. package/dist/module/modular/query.js.map +1 -0
  71. package/dist/module/modular/snapshot.js +32 -0
  72. package/dist/module/modular/snapshot.js.map +1 -0
  73. package/dist/module/modular.js +229 -0
  74. package/dist/module/modular.js.map +1 -0
  75. package/dist/module/namespaced.js +298 -0
  76. package/dist/module/namespaced.js.map +1 -0
  77. package/dist/module/package.json +1 -0
  78. package/dist/module/pipelines/expressions.js +1273 -0
  79. package/dist/module/pipelines/expressions.js.map +1 -0
  80. package/dist/module/pipelines/index.js +32 -0
  81. package/dist/module/pipelines/index.js.map +1 -0
  82. package/dist/module/pipelines/pipeline-result.js +58 -0
  83. package/dist/module/pipelines/pipeline-result.js.map +1 -0
  84. package/dist/module/pipelines/pipeline-source.js +4 -0
  85. package/dist/module/pipelines/pipeline-source.js.map +1 -0
  86. package/dist/module/pipelines/pipeline.js +4 -0
  87. package/dist/module/pipelines/pipeline.js.map +1 -0
  88. package/dist/module/pipelines/pipeline_impl.js +42 -0
  89. package/dist/module/pipelines/pipeline_impl.js.map +1 -0
  90. package/dist/module/pipelines/pipeline_options.js +4 -0
  91. package/dist/module/pipelines/pipeline_options.js.map +1 -0
  92. package/dist/module/pipelines/pipeline_runtime.js +526 -0
  93. package/dist/module/pipelines/pipeline_runtime.js.map +1 -0
  94. package/dist/module/pipelines/pipeline_support.js +71 -0
  95. package/dist/module/pipelines/pipeline_support.js.map +1 -0
  96. package/dist/module/pipelines/pipeline_validate.js +183 -0
  97. package/dist/module/pipelines/pipeline_validate.js.map +1 -0
  98. package/dist/module/pipelines/stage_options.js +4 -0
  99. package/dist/module/pipelines/stage_options.js.map +1 -0
  100. package/dist/module/pipelines/types.js +2 -0
  101. package/dist/module/pipelines/types.js.map +1 -0
  102. package/dist/module/types/firestore.js +4 -0
  103. package/dist/module/types/firestore.js.map +1 -0
  104. package/dist/module/types/internal.js +4 -0
  105. package/dist/module/types/internal.js.map +1 -0
  106. package/dist/module/types/namespaced.js +338 -0
  107. package/dist/module/types/namespaced.js.map +1 -0
  108. package/{lib → dist/module}/utils/index.js +59 -114
  109. package/dist/module/utils/index.js.map +1 -0
  110. package/{lib → dist/module}/utils/serialize.js +58 -116
  111. package/dist/module/utils/serialize.js.map +1 -0
  112. package/{lib → dist/module}/utils/typemap.js +6 -20
  113. package/dist/module/utils/typemap.js.map +1 -0
  114. package/dist/module/version.js +5 -0
  115. package/dist/module/version.js.map +1 -0
  116. package/dist/module/web/RNFBFirestoreModule.android.js +5 -0
  117. package/dist/module/web/RNFBFirestoreModule.android.js.map +1 -0
  118. package/dist/module/web/RNFBFirestoreModule.ios.js +5 -0
  119. package/dist/module/web/RNFBFirestoreModule.ios.js.map +1 -0
  120. package/dist/module/web/RNFBFirestoreModule.js +387 -0
  121. package/dist/module/web/RNFBFirestoreModule.js.map +1 -0
  122. package/{lib → dist/module}/web/convert.js +60 -94
  123. package/dist/module/web/convert.js.map +1 -0
  124. package/dist/module/web/pipelines/pipeline.js +34 -0
  125. package/dist/module/web/pipelines/pipeline.js.map +1 -0
  126. package/dist/module/web/pipelines/pipeline_bridge_factory.js +217 -0
  127. package/dist/module/web/pipelines/pipeline_bridge_factory.js.map +1 -0
  128. package/dist/module/web/pipelines/pipeline_node_builder.js +294 -0
  129. package/dist/module/web/pipelines/pipeline_node_builder.js.map +1 -0
  130. package/dist/module/web/pipelines/pipeline_parser.js +21 -0
  131. package/dist/module/web/pipelines/pipeline_parser.js.map +1 -0
  132. package/dist/module/web/pipelines/pipeline_snapshot_serializer.js +89 -0
  133. package/dist/module/web/pipelines/pipeline_snapshot_serializer.js.map +1 -0
  134. package/dist/module/web/query.js +95 -0
  135. package/dist/module/web/query.js.map +1 -0
  136. package/dist/typescript/lib/FieldPath.d.ts +10 -0
  137. package/dist/typescript/lib/FieldPath.d.ts.map +1 -0
  138. package/dist/typescript/lib/FieldValue.d.ts +17 -0
  139. package/dist/typescript/lib/FieldValue.d.ts.map +1 -0
  140. package/dist/typescript/lib/FirestoreAggregate.d.ts +40 -0
  141. package/dist/typescript/lib/FirestoreAggregate.d.ts.map +1 -0
  142. package/dist/typescript/lib/FirestoreBlob.d.ts +11 -0
  143. package/dist/typescript/lib/FirestoreBlob.d.ts.map +1 -0
  144. package/dist/typescript/lib/FirestoreCollectionReference.d.ts +15 -0
  145. package/dist/typescript/lib/FirestoreCollectionReference.d.ts.map +1 -0
  146. package/dist/typescript/lib/FirestoreDocumentChange.d.ts +27 -0
  147. package/dist/typescript/lib/FirestoreDocumentChange.d.ts.map +1 -0
  148. package/dist/typescript/lib/FirestoreDocumentReference.d.ts +30 -0
  149. package/dist/typescript/lib/FirestoreDocumentReference.d.ts.map +1 -0
  150. package/dist/typescript/lib/FirestoreDocumentSnapshot.d.ts +30 -0
  151. package/dist/typescript/lib/FirestoreDocumentSnapshot.d.ts.map +1 -0
  152. package/dist/typescript/lib/FirestoreFilter.d.ts +52 -0
  153. package/dist/typescript/lib/FirestoreFilter.d.ts.map +1 -0
  154. package/dist/typescript/lib/FirestoreGeoPoint.d.ts +22 -0
  155. package/dist/typescript/lib/FirestoreGeoPoint.d.ts.map +1 -0
  156. package/dist/typescript/lib/FirestorePath.d.ts +12 -0
  157. package/dist/typescript/lib/FirestorePath.d.ts.map +1 -0
  158. package/dist/typescript/lib/FirestorePersistentCacheIndexManager.d.ts +16 -0
  159. package/dist/typescript/lib/FirestorePersistentCacheIndexManager.d.ts.map +1 -0
  160. package/dist/typescript/lib/FirestoreQuery.d.ts +39 -0
  161. package/dist/typescript/lib/FirestoreQuery.d.ts.map +1 -0
  162. package/dist/typescript/lib/FirestoreQueryModifiers.d.ts +59 -0
  163. package/dist/typescript/lib/FirestoreQueryModifiers.d.ts.map +1 -0
  164. package/dist/typescript/lib/FirestoreQuerySnapshot.d.ts +49 -0
  165. package/dist/typescript/lib/FirestoreQuerySnapshot.d.ts.map +1 -0
  166. package/dist/typescript/lib/FirestoreSnapshotMetadata.d.ts +8 -0
  167. package/dist/typescript/lib/FirestoreSnapshotMetadata.d.ts.map +1 -0
  168. package/dist/typescript/lib/FirestoreStatics.d.ts +23 -0
  169. package/dist/typescript/lib/FirestoreStatics.d.ts.map +1 -0
  170. package/dist/typescript/lib/FirestoreTimestamp.d.ts +33 -0
  171. package/dist/typescript/lib/FirestoreTimestamp.d.ts.map +1 -0
  172. package/dist/typescript/lib/FirestoreTransaction.d.ts +42 -0
  173. package/dist/typescript/lib/FirestoreTransaction.d.ts.map +1 -0
  174. package/dist/typescript/lib/FirestoreTransactionHandler.d.ts +26 -0
  175. package/dist/typescript/lib/FirestoreTransactionHandler.d.ts.map +1 -0
  176. package/dist/typescript/lib/FirestoreVectorValue.d.ts +17 -0
  177. package/dist/typescript/lib/FirestoreVectorValue.d.ts.map +1 -0
  178. package/dist/typescript/lib/FirestoreWriteBatch.d.ts +21 -0
  179. package/dist/typescript/lib/FirestoreWriteBatch.d.ts.map +1 -0
  180. package/dist/typescript/lib/LoadBundleTask.d.ts +16 -0
  181. package/dist/typescript/lib/LoadBundleTask.d.ts.map +1 -0
  182. package/dist/typescript/lib/index.d.ts +6 -0
  183. package/dist/typescript/lib/index.d.ts.map +1 -0
  184. package/dist/typescript/lib/modular/Bytes.d.ts +22 -0
  185. package/dist/typescript/lib/modular/Bytes.d.ts.map +1 -0
  186. package/dist/typescript/lib/modular/FieldPath.d.ts +4 -0
  187. package/dist/typescript/lib/modular/FieldPath.d.ts.map +1 -0
  188. package/dist/typescript/lib/modular/FieldValue.d.ts +8 -0
  189. package/dist/typescript/lib/modular/FieldValue.d.ts.map +1 -0
  190. package/dist/typescript/lib/modular/GeoPoint.d.ts +3 -0
  191. package/dist/typescript/lib/modular/GeoPoint.d.ts.map +1 -0
  192. package/dist/typescript/lib/modular/Timestamp.d.ts +3 -0
  193. package/dist/typescript/lib/modular/Timestamp.d.ts.map +1 -0
  194. package/dist/typescript/lib/modular/VectorValue.d.ts +4 -0
  195. package/dist/typescript/lib/modular/VectorValue.d.ts.map +1 -0
  196. package/dist/typescript/lib/modular/query.d.ts +93 -0
  197. package/dist/typescript/lib/modular/query.d.ts.map +1 -0
  198. package/dist/typescript/lib/modular/snapshot.d.ts +30 -0
  199. package/dist/typescript/lib/modular/snapshot.d.ts.map +1 -0
  200. package/dist/typescript/lib/modular.d.ts +69 -0
  201. package/dist/typescript/lib/modular.d.ts.map +1 -0
  202. package/dist/typescript/lib/namespaced.d.ts +13 -0
  203. package/dist/typescript/lib/namespaced.d.ts.map +1 -0
  204. package/dist/typescript/lib/pipelines/expressions.d.ts +723 -0
  205. package/dist/typescript/lib/pipelines/expressions.d.ts.map +1 -0
  206. package/dist/typescript/lib/pipelines/index.d.ts +31 -0
  207. package/dist/typescript/lib/pipelines/index.d.ts.map +1 -0
  208. package/dist/typescript/lib/pipelines/pipeline-result.d.ts +30 -0
  209. package/dist/typescript/lib/pipelines/pipeline-result.d.ts.map +1 -0
  210. package/dist/typescript/lib/pipelines/pipeline-source.d.ts +64 -0
  211. package/dist/typescript/lib/pipelines/pipeline-source.d.ts.map +1 -0
  212. package/dist/typescript/lib/pipelines/pipeline.d.ts +61 -0
  213. package/dist/typescript/lib/pipelines/pipeline.d.ts.map +1 -0
  214. package/dist/typescript/lib/pipelines/pipeline_impl.d.ts +21 -0
  215. package/dist/typescript/lib/pipelines/pipeline_impl.d.ts.map +1 -0
  216. package/dist/typescript/lib/pipelines/pipeline_options.d.ts +17 -0
  217. package/dist/typescript/lib/pipelines/pipeline_options.d.ts.map +1 -0
  218. package/dist/typescript/lib/pipelines/pipeline_runtime.d.ts +10 -0
  219. package/dist/typescript/lib/pipelines/pipeline_runtime.d.ts.map +1 -0
  220. package/dist/typescript/lib/pipelines/pipeline_support.d.ts +7 -0
  221. package/dist/typescript/lib/pipelines/pipeline_support.d.ts.map +1 -0
  222. package/dist/typescript/lib/pipelines/pipeline_validate.d.ts +9 -0
  223. package/dist/typescript/lib/pipelines/pipeline_validate.d.ts.map +1 -0
  224. package/dist/typescript/lib/pipelines/stage_options.d.ts +326 -0
  225. package/dist/typescript/lib/pipelines/stage_options.d.ts.map +1 -0
  226. package/dist/typescript/lib/pipelines/types.d.ts +10 -0
  227. package/dist/typescript/lib/pipelines/types.d.ts.map +1 -0
  228. package/dist/typescript/lib/types/firestore.d.ts +263 -0
  229. package/dist/typescript/lib/types/firestore.d.ts.map +1 -0
  230. package/dist/typescript/lib/types/internal.d.ts +483 -0
  231. package/dist/typescript/lib/types/internal.d.ts.map +1 -0
  232. package/dist/typescript/lib/types/namespaced.d.ts +2285 -0
  233. package/dist/typescript/lib/types/namespaced.d.ts.map +1 -0
  234. package/dist/typescript/lib/utils/index.d.ts +15 -0
  235. package/dist/typescript/lib/utils/index.d.ts.map +1 -0
  236. package/dist/typescript/lib/utils/serialize.d.ts +17 -0
  237. package/dist/typescript/lib/utils/serialize.d.ts.map +1 -0
  238. package/dist/typescript/lib/utils/typemap.d.ts +3 -0
  239. package/dist/typescript/lib/utils/typemap.d.ts.map +1 -0
  240. package/dist/typescript/lib/version.d.ts +2 -0
  241. package/dist/typescript/lib/version.d.ts.map +1 -0
  242. package/dist/typescript/lib/web/RNFBFirestoreModule.android.d.ts +3 -0
  243. package/dist/typescript/lib/web/RNFBFirestoreModule.android.d.ts.map +1 -0
  244. package/dist/typescript/lib/web/RNFBFirestoreModule.d.ts +75 -0
  245. package/dist/typescript/lib/web/RNFBFirestoreModule.d.ts.map +1 -0
  246. package/dist/typescript/lib/web/RNFBFirestoreModule.ios.d.ts +3 -0
  247. package/dist/typescript/lib/web/RNFBFirestoreModule.ios.d.ts.map +1 -0
  248. package/dist/typescript/lib/web/convert.d.ts +14 -0
  249. package/dist/typescript/lib/web/convert.d.ts.map +1 -0
  250. package/dist/typescript/lib/web/pipelines/pipeline.d.ts +4 -0
  251. package/dist/typescript/lib/web/pipelines/pipeline.d.ts.map +1 -0
  252. package/dist/typescript/lib/web/pipelines/pipeline_bridge_factory.d.ts +5 -0
  253. package/dist/typescript/lib/web/pipelines/pipeline_bridge_factory.d.ts.map +1 -0
  254. package/dist/typescript/lib/web/pipelines/pipeline_node_builder.d.ts +5 -0
  255. package/dist/typescript/lib/web/pipelines/pipeline_node_builder.d.ts.map +1 -0
  256. package/dist/typescript/lib/web/pipelines/pipeline_parser.d.ts +3 -0
  257. package/dist/typescript/lib/web/pipelines/pipeline_parser.d.ts.map +1 -0
  258. package/dist/typescript/lib/web/pipelines/pipeline_snapshot_serializer.d.ts +4 -0
  259. package/dist/typescript/lib/web/pipelines/pipeline_snapshot_serializer.d.ts.map +1 -0
  260. package/dist/typescript/lib/web/query.d.ts +23 -0
  261. package/dist/typescript/lib/web/query.d.ts.map +1 -0
  262. package/dist/typescript/package.json +1 -0
  263. package/ios/RNFBFirestore/RNFBFirestoreCollectionModule.m +52 -2
  264. package/ios/RNFBFirestore/RNFBFirestorePipelineBridgeFactory.swift +384 -0
  265. package/ios/RNFBFirestore/RNFBFirestorePipelineCallHandler.swift +86 -0
  266. package/ios/RNFBFirestore/RNFBFirestorePipelineNodeBuilder.swift +1500 -0
  267. package/ios/RNFBFirestore/RNFBFirestorePipelineParser.swift +1352 -0
  268. package/ios/RNFBFirestore/RNFBFirestorePipelineSnapshotSerializer.swift +98 -0
  269. package/lib/{FirestoreFieldPath.js → FieldPath.ts} +10 -12
  270. package/lib/{FirestoreFieldValue.js → FieldValue.ts} +22 -19
  271. package/lib/FirestoreAggregate.ts +124 -0
  272. package/lib/FirestoreBlob.ts +73 -0
  273. package/lib/FirestoreCollectionReference.ts +99 -0
  274. package/lib/FirestoreDocumentChange.ts +71 -0
  275. package/lib/FirestoreDocumentReference.ts +310 -0
  276. package/lib/FirestoreDocumentSnapshot.ts +149 -0
  277. package/lib/FirestoreFilter.ts +232 -0
  278. package/lib/{FirestoreGeoPoint.js → FirestoreGeoPoint.ts} +48 -8
  279. package/lib/FirestorePath.ts +54 -0
  280. package/lib/FirestorePersistentCacheIndexManager.ts +46 -0
  281. package/lib/{FirestoreQuery.js → FirestoreQuery.ts} +208 -100
  282. package/lib/FirestoreQueryModifiers.ts +411 -0
  283. package/lib/{FirestoreQuerySnapshot.js → FirestoreQuerySnapshot.ts} +61 -32
  284. package/lib/{FirestoreSnapshotMetadata.js → FirestoreSnapshotMetadata.ts} +8 -6
  285. package/lib/{FirestoreStatics.js → FirestoreStatics.ts} +18 -11
  286. package/lib/FirestoreTimestamp.ts +161 -0
  287. package/lib/{FirestoreTransaction.js → FirestoreTransaction.ts} +64 -27
  288. package/lib/{FirestoreTransactionHandler.js → FirestoreTransactionHandler.ts} +54 -75
  289. package/lib/{FirestoreVectorValue.js → FirestoreVectorValue.ts} +36 -15
  290. package/lib/{FirestoreWriteBatch.js → FirestoreWriteBatch.ts} +45 -21
  291. package/lib/LoadBundleTask.ts +85 -0
  292. package/lib/index.ts +71 -0
  293. package/lib/modular/Bytes.ts +81 -0
  294. package/lib/modular/FieldPath.ts +24 -0
  295. package/lib/modular/FieldValue.ts +40 -0
  296. package/lib/modular/GeoPoint.ts +20 -0
  297. package/lib/modular/Timestamp.ts +20 -0
  298. package/lib/modular/VectorValue.ts +24 -0
  299. package/lib/modular/query.ts +368 -0
  300. package/lib/modular/snapshot.ts +137 -0
  301. package/lib/modular.ts +552 -0
  302. package/lib/{index.js → namespaced.ts} +170 -80
  303. package/lib/pipelines/expressions.ts +2321 -0
  304. package/lib/pipelines/index.ts +203 -0
  305. package/lib/pipelines/pipeline-result.ts +78 -0
  306. package/lib/pipelines/pipeline-source.ts +83 -0
  307. package/lib/pipelines/pipeline.ts +99 -0
  308. package/lib/pipelines/pipeline_impl.ts +46 -0
  309. package/lib/pipelines/pipeline_options.ts +32 -0
  310. package/lib/pipelines/pipeline_runtime.ts +863 -0
  311. package/lib/pipelines/pipeline_support.ts +134 -0
  312. package/lib/pipelines/pipeline_validate.ts +242 -0
  313. package/lib/pipelines/stage_options.ts +376 -0
  314. package/lib/pipelines/types.ts +26 -0
  315. package/lib/types/firestore.ts +477 -0
  316. package/lib/types/internal.ts +747 -0
  317. package/lib/{index.d.ts → types/namespaced.ts} +280 -79
  318. package/lib/utils/index.ts +244 -0
  319. package/lib/utils/serialize.ts +314 -0
  320. package/lib/utils/typemap.ts +65 -0
  321. package/lib/version.ts +2 -0
  322. package/lib/web/{RNFBFirestoreModule.js → RNFBFirestoreModule.ts} +222 -234
  323. package/lib/web/convert.ts +287 -0
  324. package/lib/web/pipelines/pipeline.ts +47 -0
  325. package/lib/web/pipelines/pipeline_bridge_factory.ts +377 -0
  326. package/lib/web/pipelines/pipeline_node_builder.ts +413 -0
  327. package/lib/web/pipelines/pipeline_parser.ts +23 -0
  328. package/lib/web/pipelines/pipeline_snapshot_serializer.ts +133 -0
  329. package/lib/web/query.ts +150 -0
  330. package/package.json +46 -7
  331. package/tsconfig.json +35 -0
  332. package/lib/FirestoreBlob.js +0 -107
  333. package/lib/FirestoreCollectionReference.js +0 -70
  334. package/lib/FirestoreDocumentReference.js +0 -222
  335. package/lib/FirestoreDocumentSnapshot.js +0 -132
  336. package/lib/FirestoreFilter.js +0 -156
  337. package/lib/modular/Bytes.d.ts +0 -11
  338. package/lib/modular/Bytes.js +0 -62
  339. package/lib/modular/FieldPath.d.ts +0 -20
  340. package/lib/modular/FieldPath.js +0 -7
  341. package/lib/modular/FieldValue.d.ts +0 -67
  342. package/lib/modular/FieldValue.js +0 -41
  343. package/lib/modular/GeoPoint.d.ts +0 -17
  344. package/lib/modular/GeoPoint.js +0 -3
  345. package/lib/modular/Timestamp.d.ts +0 -85
  346. package/lib/modular/Timestamp.js +0 -3
  347. package/lib/modular/VectorValue.d.ts +0 -30
  348. package/lib/modular/VectorValue.js +0 -11
  349. package/lib/modular/index.d.ts +0 -788
  350. package/lib/modular/index.js +0 -410
  351. package/lib/modular/query.d.ts +0 -370
  352. package/lib/modular/query.js +0 -233
  353. package/lib/modular/snapshot.d.ts +0 -256
  354. package/lib/modular/snapshot.js +0 -33
  355. package/lib/modular/utils/observer.js +0 -16
  356. package/lib/version.js +0 -2
  357. package/lib/web/query.js +0 -112
  358. /package/lib/web/{RNFBFirestoreModule.android.js → RNFBFirestoreModule.android.ts} +0 -0
  359. /package/lib/web/{RNFBFirestoreModule.ios.js → RNFBFirestoreModule.ios.ts} +0 -0
@@ -0,0 +1,3919 @@
1
+ package io.invertase.firebase.firestore;
2
+
3
+ import com.google.firebase.Timestamp;
4
+ import com.google.firebase.firestore.DocumentReference;
5
+ import com.google.firebase.firestore.pipeline.AggregateFunction;
6
+ import com.google.firebase.firestore.pipeline.AliasedAggregate;
7
+ import com.google.firebase.firestore.pipeline.BooleanExpression;
8
+ import com.google.firebase.firestore.pipeline.Expression;
9
+ import com.google.firebase.firestore.pipeline.Ordering;
10
+ import com.google.firebase.firestore.pipeline.Selectable;
11
+ import java.util.ArrayDeque;
12
+ import java.util.ArrayList;
13
+ import java.util.Arrays;
14
+ import java.util.LinkedHashMap;
15
+ import java.util.List;
16
+ import java.util.Locale;
17
+ import java.util.Map;
18
+
19
+ final class ReactNativeFirebaseFirestorePipelineNodeBuilder {
20
+ private static final class ResolvedValueBox {
21
+ Object value;
22
+ }
23
+
24
+ private static final class SerializedValueBox {
25
+ Object value;
26
+ }
27
+
28
+ private static final class SerializedExpressionBox {
29
+ Object value;
30
+ }
31
+
32
+ private static final class ConstantValueBox {
33
+ Object value;
34
+ }
35
+
36
+ private static final class LoweredBooleanBox {
37
+ BooleanExpression value;
38
+ }
39
+
40
+ private static final class LoweredExpressionBox {
41
+ Expression value;
42
+ }
43
+
44
+ private static final class LoweredObjectBox {
45
+ Object value;
46
+ }
47
+
48
+ private static final class PendingReceiverOperation {
49
+ final String normalizedName;
50
+ final List<Object> args;
51
+ final String fieldName;
52
+ final String originalName;
53
+
54
+ PendingReceiverOperation(
55
+ String normalizedName, String originalName, List<Object> args, String fieldName) {
56
+ this.normalizedName = normalizedName;
57
+ this.originalName = originalName;
58
+ this.args = args;
59
+ this.fieldName = fieldName;
60
+ }
61
+ }
62
+
63
+ private static final class PendingBooleanReceiverOperation {
64
+ final String normalizedName;
65
+ final List<Object> args;
66
+ final String fieldName;
67
+ final String originalName;
68
+
69
+ PendingBooleanReceiverOperation(
70
+ String normalizedName, String originalName, List<Object> args, String fieldName) {
71
+ this.normalizedName = normalizedName;
72
+ this.originalName = originalName;
73
+ this.args = args;
74
+ this.fieldName = fieldName;
75
+ }
76
+ }
77
+
78
+ private interface ValueResolutionFrame {}
79
+
80
+ private interface SerializationFrame {}
81
+
82
+ private interface ConstantResolutionFrame {}
83
+
84
+ private interface BooleanLoweringFrame {}
85
+
86
+ private interface ObjectLoweringFrame {}
87
+
88
+ private static final class ReceiverChainSeed {
89
+ final Object baseValue;
90
+ final String baseFieldName;
91
+ final List<PendingReceiverOperation> pendingOperations;
92
+
93
+ ReceiverChainSeed(
94
+ Object baseValue, String baseFieldName, List<PendingReceiverOperation> pendingOperations) {
95
+ this.baseValue = baseValue;
96
+ this.baseFieldName = baseFieldName;
97
+ this.pendingOperations = pendingOperations;
98
+ }
99
+ }
100
+
101
+ private static final class EnterObjectExpressionFrame implements ObjectLoweringFrame {
102
+ final Object value;
103
+ final String fieldName;
104
+ final LoweredExpressionBox box;
105
+
106
+ EnterObjectExpressionFrame(Object value, String fieldName, LoweredExpressionBox box) {
107
+ this.value = value;
108
+ this.fieldName = fieldName;
109
+ this.box = box;
110
+ }
111
+ }
112
+
113
+ private static final class EnterObjectBooleanFrame implements ObjectLoweringFrame {
114
+ final Object value;
115
+ final String fieldName;
116
+ final LoweredBooleanBox box;
117
+
118
+ EnterObjectBooleanFrame(Object value, String fieldName, LoweredBooleanBox box) {
119
+ this.value = value;
120
+ this.fieldName = fieldName;
121
+ this.box = box;
122
+ }
123
+ }
124
+
125
+ private static final class EnterObjectExpressionValueFrame implements ObjectLoweringFrame {
126
+ final Object value;
127
+ final String fieldName;
128
+ final LoweredExpressionBox box;
129
+
130
+ EnterObjectExpressionValueFrame(Object value, String fieldName, LoweredExpressionBox box) {
131
+ this.value = value;
132
+ this.fieldName = fieldName;
133
+ this.box = box;
134
+ }
135
+ }
136
+
137
+ private static final class EnterObjectBooleanValueFrame implements ObjectLoweringFrame {
138
+ final Object value;
139
+ final String fieldName;
140
+ final LoweredBooleanBox box;
141
+
142
+ EnterObjectBooleanValueFrame(Object value, String fieldName, LoweredBooleanBox box) {
143
+ this.value = value;
144
+ this.fieldName = fieldName;
145
+ this.box = box;
146
+ }
147
+ }
148
+
149
+ private static final class EnterObjectValueOrExpressionFrame implements ObjectLoweringFrame {
150
+ final Object value;
151
+ final String fieldName;
152
+ final LoweredObjectBox box;
153
+
154
+ EnterObjectValueOrExpressionFrame(Object value, String fieldName, LoweredObjectBox box) {
155
+ this.value = value;
156
+ this.fieldName = fieldName;
157
+ this.box = box;
158
+ }
159
+ }
160
+
161
+ private static final class EnterObjectVectorExpressionValueFrame implements ObjectLoweringFrame {
162
+ final Object value;
163
+ final String fieldName;
164
+ final LoweredExpressionBox box;
165
+
166
+ EnterObjectVectorExpressionValueFrame(
167
+ Object value, String fieldName, LoweredExpressionBox box) {
168
+ this.value = value;
169
+ this.fieldName = fieldName;
170
+ this.box = box;
171
+ }
172
+ }
173
+
174
+ private static final class ExitApplyPendingUnaryFrame implements ObjectLoweringFrame {
175
+ final LoweredExpressionBox box;
176
+ final LoweredExpressionBox childBox;
177
+ final List<String> pendingUnaryFunctions;
178
+
179
+ ExitApplyPendingUnaryFrame(
180
+ LoweredExpressionBox box,
181
+ LoweredExpressionBox childBox,
182
+ List<String> pendingUnaryFunctions) {
183
+ this.box = box;
184
+ this.childBox = childBox;
185
+ this.pendingUnaryFunctions = pendingUnaryFunctions;
186
+ }
187
+ }
188
+
189
+ private static final class ExitApplyPendingUnaryBooleanFrame implements ObjectLoweringFrame {
190
+ final LoweredExpressionBox box;
191
+ final LoweredBooleanBox childBox;
192
+ final List<String> pendingUnaryFunctions;
193
+
194
+ ExitApplyPendingUnaryBooleanFrame(
195
+ LoweredExpressionBox box, LoweredBooleanBox childBox, List<String> pendingUnaryFunctions) {
196
+ this.box = box;
197
+ this.childBox = childBox;
198
+ this.pendingUnaryFunctions = pendingUnaryFunctions;
199
+ }
200
+ }
201
+
202
+ private static final class ExitCastExpressionToBooleanFrame implements ObjectLoweringFrame {
203
+ final LoweredBooleanBox box;
204
+ final LoweredExpressionBox expressionBox;
205
+ final String fieldName;
206
+
207
+ ExitCastExpressionToBooleanFrame(
208
+ LoweredBooleanBox box, LoweredExpressionBox expressionBox, String fieldName) {
209
+ this.box = box;
210
+ this.expressionBox = expressionBox;
211
+ this.fieldName = fieldName;
212
+ }
213
+ }
214
+
215
+ private static final class ExitSetObjectFromExpressionFrame implements ObjectLoweringFrame {
216
+ final LoweredObjectBox box;
217
+ final LoweredExpressionBox expressionBox;
218
+
219
+ ExitSetObjectFromExpressionFrame(LoweredObjectBox box, LoweredExpressionBox expressionBox) {
220
+ this.box = box;
221
+ this.expressionBox = expressionBox;
222
+ }
223
+ }
224
+
225
+ private static final class ExitObjectConditionalExpressionFrame implements ObjectLoweringFrame {
226
+ final LoweredExpressionBox box;
227
+ final LoweredBooleanBox conditionBox;
228
+ final LoweredExpressionBox trueBox;
229
+ final LoweredExpressionBox falseBox;
230
+
231
+ ExitObjectConditionalExpressionFrame(
232
+ LoweredExpressionBox box,
233
+ LoweredBooleanBox conditionBox,
234
+ LoweredExpressionBox trueBox,
235
+ LoweredExpressionBox falseBox) {
236
+ this.box = box;
237
+ this.conditionBox = conditionBox;
238
+ this.trueBox = trueBox;
239
+ this.falseBox = falseBox;
240
+ }
241
+ }
242
+
243
+ private static final class ExitObjectIsTypeExpressionFrame implements ObjectLoweringFrame {
244
+ final LoweredExpressionBox box;
245
+ final LoweredExpressionBox expressionBox;
246
+ final String typeName;
247
+
248
+ ExitObjectIsTypeExpressionFrame(
249
+ LoweredExpressionBox box, LoweredExpressionBox expressionBox, String typeName) {
250
+ this.box = box;
251
+ this.expressionBox = expressionBox;
252
+ this.typeName = typeName;
253
+ }
254
+ }
255
+
256
+ private static final class ExitObjectArrayExpressionFrame implements ObjectLoweringFrame {
257
+ final LoweredExpressionBox box;
258
+ final List<LoweredExpressionBox> childBoxes;
259
+
260
+ ExitObjectArrayExpressionFrame(
261
+ LoweredExpressionBox box, List<LoweredExpressionBox> childBoxes) {
262
+ this.box = box;
263
+ this.childBoxes = childBoxes;
264
+ }
265
+ }
266
+
267
+ private static final class ExitObjectMapExpressionFrame implements ObjectLoweringFrame {
268
+ final LoweredExpressionBox box;
269
+ final List<Map.Entry<String, LoweredExpressionBox>> entries;
270
+
271
+ ExitObjectMapExpressionFrame(
272
+ LoweredExpressionBox box, List<Map.Entry<String, LoweredExpressionBox>> entries) {
273
+ this.box = box;
274
+ this.entries = entries;
275
+ }
276
+ }
277
+
278
+ private static final class ExitObjectRawExpressionFunctionFrame implements ObjectLoweringFrame {
279
+ final LoweredExpressionBox box;
280
+ final String functionName;
281
+ final List<LoweredExpressionBox> childBoxes;
282
+
283
+ ExitObjectRawExpressionFunctionFrame(
284
+ LoweredExpressionBox box, String functionName, List<LoweredExpressionBox> childBoxes) {
285
+ this.box = box;
286
+ this.functionName = functionName;
287
+ this.childBoxes = childBoxes;
288
+ }
289
+ }
290
+
291
+ private static final class ExitObjectRawBooleanFunctionFrame implements ObjectLoweringFrame {
292
+ final LoweredBooleanBox box;
293
+ final String functionName;
294
+ final List<LoweredExpressionBox> childBoxes;
295
+
296
+ ExitObjectRawBooleanFunctionFrame(
297
+ LoweredBooleanBox box, String functionName, List<LoweredExpressionBox> childBoxes) {
298
+ this.box = box;
299
+ this.functionName = functionName;
300
+ this.childBoxes = childBoxes;
301
+ }
302
+ }
303
+
304
+ private static final class ContinueReceiverExpressionChainFrame implements ObjectLoweringFrame {
305
+ final LoweredExpressionBox box;
306
+ final LoweredExpressionBox baseBox;
307
+ final List<PendingReceiverOperation> pendingOperations;
308
+ final int nextIndex;
309
+ final Expression currentExpression;
310
+
311
+ ContinueReceiverExpressionChainFrame(
312
+ LoweredExpressionBox box,
313
+ LoweredExpressionBox baseBox,
314
+ List<PendingReceiverOperation> pendingOperations,
315
+ int nextIndex,
316
+ Expression currentExpression) {
317
+ this.box = box;
318
+ this.baseBox = baseBox;
319
+ this.pendingOperations = pendingOperations;
320
+ this.nextIndex = nextIndex;
321
+ this.currentExpression = currentExpression;
322
+ }
323
+ }
324
+
325
+ private static final class ExitReceiverLogicalExtremaFrame implements ObjectLoweringFrame {
326
+ final LoweredExpressionBox box;
327
+ final List<PendingReceiverOperation> pendingOperations;
328
+ final int nextIndex;
329
+ final Expression currentExpression;
330
+ final boolean maximum;
331
+ final List<LoweredExpressionBox> childBoxes;
332
+
333
+ ExitReceiverLogicalExtremaFrame(
334
+ LoweredExpressionBox box,
335
+ List<PendingReceiverOperation> pendingOperations,
336
+ int nextIndex,
337
+ Expression currentExpression,
338
+ boolean maximum,
339
+ List<LoweredExpressionBox> childBoxes) {
340
+ this.box = box;
341
+ this.pendingOperations = pendingOperations;
342
+ this.nextIndex = nextIndex;
343
+ this.currentExpression = currentExpression;
344
+ this.maximum = maximum;
345
+ this.childBoxes = childBoxes;
346
+ }
347
+ }
348
+
349
+ private static final class ExitReceiverMapGetFrame implements ObjectLoweringFrame {
350
+ final LoweredExpressionBox box;
351
+ final List<PendingReceiverOperation> pendingOperations;
352
+ final int nextIndex;
353
+ final Expression currentExpression;
354
+ final LoweredExpressionBox keyBox;
355
+
356
+ ExitReceiverMapGetFrame(
357
+ LoweredExpressionBox box,
358
+ List<PendingReceiverOperation> pendingOperations,
359
+ int nextIndex,
360
+ Expression currentExpression,
361
+ LoweredExpressionBox keyBox) {
362
+ this.box = box;
363
+ this.pendingOperations = pendingOperations;
364
+ this.nextIndex = nextIndex;
365
+ this.currentExpression = currentExpression;
366
+ this.keyBox = keyBox;
367
+ }
368
+ }
369
+
370
+ private static final class ExitReceiverMapMergeFrame implements ObjectLoweringFrame {
371
+ final LoweredExpressionBox box;
372
+ final List<PendingReceiverOperation> pendingOperations;
373
+ final int nextIndex;
374
+ final Expression currentExpression;
375
+ final List<LoweredExpressionBox> childBoxes;
376
+
377
+ ExitReceiverMapMergeFrame(
378
+ LoweredExpressionBox box,
379
+ List<PendingReceiverOperation> pendingOperations,
380
+ int nextIndex,
381
+ Expression currentExpression,
382
+ List<LoweredExpressionBox> childBoxes) {
383
+ this.box = box;
384
+ this.pendingOperations = pendingOperations;
385
+ this.nextIndex = nextIndex;
386
+ this.currentExpression = currentExpression;
387
+ this.childBoxes = childBoxes;
388
+ }
389
+ }
390
+
391
+ private static final class ExitReceiverArrayGetFrame implements ObjectLoweringFrame {
392
+ final LoweredExpressionBox box;
393
+ final List<PendingReceiverOperation> pendingOperations;
394
+ final int nextIndex;
395
+ final Expression currentExpression;
396
+ final LoweredExpressionBox indexBox;
397
+
398
+ ExitReceiverArrayGetFrame(
399
+ LoweredExpressionBox box,
400
+ List<PendingReceiverOperation> pendingOperations,
401
+ int nextIndex,
402
+ Expression currentExpression,
403
+ LoweredExpressionBox indexBox) {
404
+ this.box = box;
405
+ this.pendingOperations = pendingOperations;
406
+ this.nextIndex = nextIndex;
407
+ this.currentExpression = currentExpression;
408
+ this.indexBox = indexBox;
409
+ }
410
+ }
411
+
412
+ private static final class ExitReceiverArrayConcatFrame implements ObjectLoweringFrame {
413
+ final LoweredExpressionBox box;
414
+ final List<PendingReceiverOperation> pendingOperations;
415
+ final int nextIndex;
416
+ final Expression currentExpression;
417
+ final List<LoweredObjectBox> childBoxes;
418
+
419
+ ExitReceiverArrayConcatFrame(
420
+ LoweredExpressionBox box,
421
+ List<PendingReceiverOperation> pendingOperations,
422
+ int nextIndex,
423
+ Expression currentExpression,
424
+ List<LoweredObjectBox> childBoxes) {
425
+ this.box = box;
426
+ this.pendingOperations = pendingOperations;
427
+ this.nextIndex = nextIndex;
428
+ this.currentExpression = currentExpression;
429
+ this.childBoxes = childBoxes;
430
+ }
431
+ }
432
+
433
+ private static final class ExitReceiverVectorDistanceFrame implements ObjectLoweringFrame {
434
+ final LoweredExpressionBox box;
435
+ final List<PendingReceiverOperation> pendingOperations;
436
+ final int nextIndex;
437
+ final Expression currentExpression;
438
+ final String normalizedName;
439
+ final LoweredExpressionBox rightBox;
440
+
441
+ ExitReceiverVectorDistanceFrame(
442
+ LoweredExpressionBox box,
443
+ List<PendingReceiverOperation> pendingOperations,
444
+ int nextIndex,
445
+ Expression currentExpression,
446
+ String normalizedName,
447
+ LoweredExpressionBox rightBox) {
448
+ this.box = box;
449
+ this.pendingOperations = pendingOperations;
450
+ this.nextIndex = nextIndex;
451
+ this.currentExpression = currentExpression;
452
+ this.normalizedName = normalizedName;
453
+ this.rightBox = rightBox;
454
+ }
455
+ }
456
+
457
+ private static final class ExitReceiverTimestampMathFrame implements ObjectLoweringFrame {
458
+ final LoweredExpressionBox box;
459
+ final List<PendingReceiverOperation> pendingOperations;
460
+ final int nextIndex;
461
+ final Expression currentExpression;
462
+ final String normalizedName;
463
+ final LoweredExpressionBox unitBox;
464
+ final LoweredExpressionBox amountBox;
465
+
466
+ ExitReceiverTimestampMathFrame(
467
+ LoweredExpressionBox box,
468
+ List<PendingReceiverOperation> pendingOperations,
469
+ int nextIndex,
470
+ Expression currentExpression,
471
+ String normalizedName,
472
+ LoweredExpressionBox unitBox,
473
+ LoweredExpressionBox amountBox) {
474
+ this.box = box;
475
+ this.pendingOperations = pendingOperations;
476
+ this.nextIndex = nextIndex;
477
+ this.currentExpression = currentExpression;
478
+ this.normalizedName = normalizedName;
479
+ this.unitBox = unitBox;
480
+ this.amountBox = amountBox;
481
+ }
482
+ }
483
+
484
+ private static final class ExitReceiverTimestampTruncateFrame implements ObjectLoweringFrame {
485
+ final LoweredExpressionBox box;
486
+ final List<PendingReceiverOperation> pendingOperations;
487
+ final int nextIndex;
488
+ final Expression currentExpression;
489
+ final LoweredExpressionBox granularityBox;
490
+
491
+ ExitReceiverTimestampTruncateFrame(
492
+ LoweredExpressionBox box,
493
+ List<PendingReceiverOperation> pendingOperations,
494
+ int nextIndex,
495
+ Expression currentExpression,
496
+ LoweredExpressionBox granularityBox) {
497
+ this.box = box;
498
+ this.pendingOperations = pendingOperations;
499
+ this.nextIndex = nextIndex;
500
+ this.currentExpression = currentExpression;
501
+ this.granularityBox = granularityBox;
502
+ }
503
+ }
504
+
505
+ private static final class ExitApplyBooleanReceiverFrame implements ObjectLoweringFrame {
506
+ final LoweredBooleanBox box;
507
+ final LoweredExpressionBox leftBox;
508
+ final String normalizedName;
509
+ final Object rightArg;
510
+ final String fieldName;
511
+
512
+ ExitApplyBooleanReceiverFrame(
513
+ LoweredBooleanBox box,
514
+ LoweredExpressionBox leftBox,
515
+ String normalizedName,
516
+ Object rightArg,
517
+ String fieldName) {
518
+ this.box = box;
519
+ this.leftBox = leftBox;
520
+ this.normalizedName = normalizedName;
521
+ this.rightArg = rightArg;
522
+ this.fieldName = fieldName;
523
+ }
524
+ }
525
+
526
+ private static final class ExitFinalizeBooleanReceiverFrame implements ObjectLoweringFrame {
527
+ final LoweredBooleanBox box;
528
+ final Expression leftExpression;
529
+ final String normalizedName;
530
+ final LoweredExpressionBox rightBox;
531
+ final String fieldName;
532
+
533
+ ExitFinalizeBooleanReceiverFrame(
534
+ LoweredBooleanBox box,
535
+ Expression leftExpression,
536
+ String normalizedName,
537
+ LoweredExpressionBox rightBox,
538
+ String fieldName) {
539
+ this.box = box;
540
+ this.leftExpression = leftExpression;
541
+ this.normalizedName = normalizedName;
542
+ this.rightBox = rightBox;
543
+ this.fieldName = fieldName;
544
+ }
545
+ }
546
+
547
+ private static final class EnterValueResolutionFrame implements ValueResolutionFrame {
548
+ final ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value;
549
+ final String fieldName;
550
+ final ResolvedValueBox box;
551
+
552
+ EnterValueResolutionFrame(
553
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value,
554
+ String fieldName,
555
+ ResolvedValueBox box) {
556
+ this.value = value;
557
+ this.fieldName = fieldName;
558
+ this.box = box;
559
+ }
560
+ }
561
+
562
+ private static final class ExitListValueResolutionFrame implements ValueResolutionFrame {
563
+ final ResolvedValueBox box;
564
+ final List<ResolvedValueBox> childBoxes;
565
+
566
+ ExitListValueResolutionFrame(ResolvedValueBox box, List<ResolvedValueBox> childBoxes) {
567
+ this.box = box;
568
+ this.childBoxes = childBoxes;
569
+ }
570
+ }
571
+
572
+ private static final class ExitMapValueResolutionFrame implements ValueResolutionFrame {
573
+ final ResolvedValueBox box;
574
+ final List<Map.Entry<String, ResolvedValueBox>> entries;
575
+
576
+ ExitMapValueResolutionFrame(
577
+ ResolvedValueBox box, List<Map.Entry<String, ResolvedValueBox>> entries) {
578
+ this.box = box;
579
+ this.entries = entries;
580
+ }
581
+ }
582
+
583
+ private static final class EnterSerializedExpressionFrame implements SerializationFrame {
584
+ final ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionNode value;
585
+ final SerializedExpressionBox box;
586
+
587
+ EnterSerializedExpressionFrame(
588
+ ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionNode value,
589
+ SerializedExpressionBox box) {
590
+ this.value = value;
591
+ this.box = box;
592
+ }
593
+ }
594
+
595
+ private static final class ExitSerializedExpressionFunctionFrame implements SerializationFrame {
596
+ final SerializedExpressionBox box;
597
+ final String name;
598
+ final List<SerializedValueBox> argBoxes;
599
+
600
+ ExitSerializedExpressionFunctionFrame(
601
+ SerializedExpressionBox box, String name, List<SerializedValueBox> argBoxes) {
602
+ this.box = box;
603
+ this.name = name;
604
+ this.argBoxes = argBoxes;
605
+ }
606
+ }
607
+
608
+ private static final class EnterSerializedValueFrame implements SerializationFrame {
609
+ final ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value;
610
+ final SerializedValueBox box;
611
+
612
+ EnterSerializedValueFrame(
613
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value, SerializedValueBox box) {
614
+ this.value = value;
615
+ this.box = box;
616
+ }
617
+ }
618
+
619
+ private static final class ExitSerializedValueListFrame implements SerializationFrame {
620
+ final SerializedValueBox box;
621
+ final List<SerializedValueBox> childBoxes;
622
+
623
+ ExitSerializedValueListFrame(SerializedValueBox box, List<SerializedValueBox> childBoxes) {
624
+ this.box = box;
625
+ this.childBoxes = childBoxes;
626
+ }
627
+ }
628
+
629
+ private static final class ExitSerializedValueMapFrame implements SerializationFrame {
630
+ final SerializedValueBox box;
631
+ final List<Map.Entry<String, SerializedValueBox>> entries;
632
+
633
+ ExitSerializedValueMapFrame(
634
+ SerializedValueBox box, List<Map.Entry<String, SerializedValueBox>> entries) {
635
+ this.box = box;
636
+ this.entries = entries;
637
+ }
638
+ }
639
+
640
+ private static final class ExitSerializedExpressionConstantFrame implements SerializationFrame {
641
+ final SerializedExpressionBox expressionBox;
642
+ final SerializedValueBox valueBox;
643
+
644
+ ExitSerializedExpressionConstantFrame(
645
+ SerializedExpressionBox expressionBox, SerializedValueBox valueBox) {
646
+ this.expressionBox = expressionBox;
647
+ this.valueBox = valueBox;
648
+ }
649
+ }
650
+
651
+ private static final class ExitSerializedValueExpressionFrame implements SerializationFrame {
652
+ final SerializedValueBox valueBox;
653
+ final SerializedExpressionBox expressionBox;
654
+
655
+ ExitSerializedValueExpressionFrame(
656
+ SerializedValueBox valueBox, SerializedExpressionBox expressionBox) {
657
+ this.valueBox = valueBox;
658
+ this.expressionBox = expressionBox;
659
+ }
660
+ }
661
+
662
+ private static final class EnterConstantResolutionFrame implements ConstantResolutionFrame {
663
+ final Object value;
664
+ final String fieldName;
665
+ final ConstantValueBox box;
666
+
667
+ EnterConstantResolutionFrame(Object value, String fieldName, ConstantValueBox box) {
668
+ this.value = value;
669
+ this.fieldName = fieldName;
670
+ this.box = box;
671
+ }
672
+ }
673
+
674
+ private static final class ExitConstantListFrame implements ConstantResolutionFrame {
675
+ final ConstantValueBox box;
676
+ final List<ConstantValueBox> childBoxes;
677
+
678
+ ExitConstantListFrame(ConstantValueBox box, List<ConstantValueBox> childBoxes) {
679
+ this.box = box;
680
+ this.childBoxes = childBoxes;
681
+ }
682
+ }
683
+
684
+ private static final class ExitConstantMapFrame implements ConstantResolutionFrame {
685
+ final ConstantValueBox box;
686
+ final List<Map.Entry<String, ConstantValueBox>> entries;
687
+
688
+ ExitConstantMapFrame(ConstantValueBox box, List<Map.Entry<String, ConstantValueBox>> entries) {
689
+ this.box = box;
690
+ this.entries = entries;
691
+ }
692
+ }
693
+
694
+ private static final class EnterBooleanLoweringFrame implements BooleanLoweringFrame {
695
+ final Object value;
696
+ final String fieldName;
697
+ final LoweredBooleanBox box;
698
+
699
+ EnterBooleanLoweringFrame(Object value, String fieldName, LoweredBooleanBox box) {
700
+ this.value = value;
701
+ this.fieldName = fieldName;
702
+ this.box = box;
703
+ }
704
+ }
705
+
706
+ private static final class ExitBooleanLogicalFrame
707
+ implements BooleanLoweringFrame, ObjectLoweringFrame {
708
+ final LoweredBooleanBox box;
709
+ final boolean andOperator;
710
+ final List<LoweredBooleanBox> childBoxes;
711
+ final String fieldName;
712
+
713
+ ExitBooleanLogicalFrame(
714
+ LoweredBooleanBox box,
715
+ boolean andOperator,
716
+ List<LoweredBooleanBox> childBoxes,
717
+ String fieldName) {
718
+ this.box = box;
719
+ this.andOperator = andOperator;
720
+ this.childBoxes = childBoxes;
721
+ this.fieldName = fieldName;
722
+ }
723
+ }
724
+
725
+ Expression coerceExpression(
726
+ ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionNode value, String fieldName)
727
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
728
+ return coerceExpression(serializeExpressionNode(value), fieldName);
729
+ }
730
+
731
+ Selectable coerceSelectable(
732
+ ReactNativeFirebaseFirestorePipelineParser.ParsedSelectableNode value, String fieldName)
733
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
734
+ Expression expr = coerceExpression(value.expression, fieldName + ".expr");
735
+ if (value.alias != null && !value.alias.isEmpty()) {
736
+ return expr.alias(value.alias);
737
+ }
738
+ if (expr instanceof Selectable) {
739
+ return (Selectable) expr;
740
+ }
741
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
742
+ "pipelineExecute() expected "
743
+ + fieldName
744
+ + " to include an alias for computed expressions.");
745
+ }
746
+
747
+ Ordering coerceOrdering(
748
+ ReactNativeFirebaseFirestorePipelineParser.ParsedOrderingNode value, String fieldName)
749
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
750
+ Expression expression = coerceExpression(value.expression, fieldName + ".expr");
751
+ if (value.fieldShortcut
752
+ && value.expression
753
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedFieldExpressionNode) {
754
+ String fieldPath =
755
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedFieldExpressionNode) value.expression)
756
+ .path;
757
+ return value.descending ? Ordering.descending(fieldPath) : Ordering.ascending(fieldPath);
758
+ }
759
+ return value.descending ? expression.descending() : expression.ascending();
760
+ }
761
+
762
+ BooleanExpression coerceBooleanExpression(
763
+ ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionNode value, String fieldName)
764
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
765
+ return coerceBooleanExpression(serializeExpressionNode(value), fieldName);
766
+ }
767
+
768
+ AliasedAggregate coerceAliasedAggregate(
769
+ ReactNativeFirebaseFirestorePipelineParser.ParsedAggregateNode value, String fieldName)
770
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
771
+ AggregateFunction function = buildAggregateFunction(value, fieldName);
772
+ return function.alias(value.alias);
773
+ }
774
+
775
+ String coerceFieldPath(Object value, String fieldName)
776
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
777
+ Object currentValue = value;
778
+
779
+ while (true) {
780
+ if (currentValue instanceof String) {
781
+ String fieldPath = (String) currentValue;
782
+ if (!fieldPath.isEmpty()) {
783
+ return fieldPath;
784
+ }
785
+ }
786
+
787
+ if (currentValue instanceof Map) {
788
+ Map<?, ?> map = (Map<?, ?>) currentValue;
789
+ Object directPath = map.get("path");
790
+ if (directPath instanceof String && !((String) directPath).isEmpty()) {
791
+ return (String) directPath;
792
+ }
793
+
794
+ Object fieldPath = map.get("fieldPath");
795
+ if (fieldPath != null && fieldPath != map) {
796
+ currentValue = fieldPath;
797
+ continue;
798
+ }
799
+
800
+ Object segments = map.get("segments");
801
+ if (!(segments instanceof List)) {
802
+ segments = map.get("_segments");
803
+ }
804
+ if (segments instanceof List) {
805
+ List<?> list = (List<?>) segments;
806
+ StringBuilder builder = new StringBuilder();
807
+ for (int i = 0; i < list.size(); i++) {
808
+ Object segment = list.get(i);
809
+ if (!(segment instanceof String)) {
810
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
811
+ "pipelineExecute() expected " + fieldName + " segment values to be strings.");
812
+ }
813
+ if (i > 0) {
814
+ builder.append('.');
815
+ }
816
+ builder.append((String) segment);
817
+ }
818
+ String path = builder.toString();
819
+ if (!path.isEmpty()) {
820
+ return path;
821
+ }
822
+ }
823
+ }
824
+
825
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
826
+ "pipelineExecute() expected " + fieldName + " to resolve to a field path string.");
827
+ }
828
+ }
829
+
830
+ double[] coerceVectorValue(Object value)
831
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
832
+ Object currentValue = value;
833
+
834
+ while (currentValue instanceof Map) {
835
+ currentValue = ((Map<?, ?>) currentValue).get("values");
836
+ }
837
+
838
+ if (!(currentValue instanceof List)) {
839
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
840
+ "pipelineExecute() expected findNearest.vectorValue to be an array.");
841
+ }
842
+
843
+ List<?> values = (List<?>) currentValue;
844
+ double[] vector = new double[values.size()];
845
+ for (int i = 0; i < values.size(); i++) {
846
+ Object item = values.get(i);
847
+ if (!(item instanceof Number)) {
848
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
849
+ "pipelineExecute() expected findNearest.vectorValue to contain only numbers.");
850
+ }
851
+ vector[i] = ((Number) item).doubleValue();
852
+ }
853
+
854
+ return vector;
855
+ }
856
+
857
+ Expression coerceExpression(Object value, String fieldName)
858
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
859
+ return lowerExpressionObject(value, fieldName);
860
+ }
861
+
862
+ BooleanExpression coerceBooleanExpression(Object value, String fieldName)
863
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
864
+ return lowerBooleanObject(value, fieldName);
865
+ }
866
+
867
+ private Expression lowerExpressionObject(Object value, String fieldName)
868
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
869
+ LoweredExpressionBox rootBox = new LoweredExpressionBox();
870
+ ArrayDeque<ObjectLoweringFrame> stack = new ArrayDeque<>();
871
+ stack.push(new EnterObjectExpressionFrame(value, fieldName, rootBox));
872
+ processObjectLoweringStack(stack);
873
+ return rootBox.value;
874
+ }
875
+
876
+ private BooleanExpression lowerBooleanObject(Object value, String fieldName)
877
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
878
+ LoweredBooleanBox rootBox = new LoweredBooleanBox();
879
+ ArrayDeque<ObjectLoweringFrame> stack = new ArrayDeque<>();
880
+ stack.push(new EnterObjectBooleanFrame(value, fieldName, rootBox));
881
+ processObjectLoweringStack(stack);
882
+ return rootBox.value;
883
+ }
884
+
885
+ private Expression lowerExpressionValueObject(Object value, String fieldName)
886
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
887
+ LoweredExpressionBox rootBox = new LoweredExpressionBox();
888
+ ArrayDeque<ObjectLoweringFrame> stack = new ArrayDeque<>();
889
+ stack.push(new EnterObjectExpressionValueFrame(value, fieldName, rootBox));
890
+ processObjectLoweringStack(stack);
891
+ return rootBox.value;
892
+ }
893
+
894
+ private BooleanExpression lowerBooleanValueObject(Object value, String fieldName)
895
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
896
+ LoweredBooleanBox rootBox = new LoweredBooleanBox();
897
+ ArrayDeque<ObjectLoweringFrame> stack = new ArrayDeque<>();
898
+ stack.push(new EnterObjectBooleanValueFrame(value, fieldName, rootBox));
899
+ processObjectLoweringStack(stack);
900
+ return rootBox.value;
901
+ }
902
+
903
+ private Object lowerValueOrExpressionObject(Object value, String fieldName)
904
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
905
+ LoweredObjectBox rootBox = new LoweredObjectBox();
906
+ ArrayDeque<ObjectLoweringFrame> stack = new ArrayDeque<>();
907
+ stack.push(new EnterObjectValueOrExpressionFrame(value, fieldName, rootBox));
908
+ processObjectLoweringStack(stack);
909
+ return rootBox.value;
910
+ }
911
+
912
+ private Expression lowerVectorExpressionValueObject(Object value, String fieldName)
913
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
914
+ LoweredExpressionBox rootBox = new LoweredExpressionBox();
915
+ ArrayDeque<ObjectLoweringFrame> stack = new ArrayDeque<>();
916
+ stack.push(new EnterObjectVectorExpressionValueFrame(value, fieldName, rootBox));
917
+ processObjectLoweringStack(stack);
918
+ return rootBox.value;
919
+ }
920
+
921
+ private void processObjectLoweringStack(ArrayDeque<ObjectLoweringFrame> stack)
922
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
923
+ while (!stack.isEmpty()) {
924
+ ObjectLoweringFrame frame = stack.pop();
925
+
926
+ if (frame instanceof EnterObjectExpressionFrame) {
927
+ EnterObjectExpressionFrame enterFrame = (EnterObjectExpressionFrame) frame;
928
+ Object currentValue = enterFrame.value;
929
+ String currentFieldName = enterFrame.fieldName;
930
+ List<String> pendingUnaryFunctions = new ArrayList<>();
931
+
932
+ while (true) {
933
+ if (currentValue instanceof String) {
934
+ enterFrame.box.value =
935
+ applyPendingUnaryExpressionFunctions(
936
+ Expression.field((String) currentValue), pendingUnaryFunctions);
937
+ break;
938
+ }
939
+
940
+ if (currentValue instanceof Expression) {
941
+ enterFrame.box.value =
942
+ applyPendingUnaryExpressionFunctions(
943
+ (Expression) currentValue, pendingUnaryFunctions);
944
+ break;
945
+ }
946
+
947
+ if (currentValue == null
948
+ || currentValue instanceof Number
949
+ || currentValue instanceof Boolean
950
+ || currentValue instanceof java.util.Date
951
+ || currentValue instanceof Timestamp
952
+ || currentValue instanceof com.google.firebase.firestore.GeoPoint
953
+ || currentValue instanceof com.google.firebase.firestore.Blob
954
+ || currentValue instanceof DocumentReference
955
+ || currentValue instanceof com.google.firebase.firestore.VectorValue
956
+ || currentValue instanceof byte[]) {
957
+ enterFrame.box.value =
958
+ applyPendingUnaryExpressionFunctions(
959
+ constantExpression(currentValue), pendingUnaryFunctions);
960
+ break;
961
+ }
962
+
963
+ if (!(currentValue instanceof Map)) {
964
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
965
+ "pipelineExecute() could not convert "
966
+ + currentFieldName
967
+ + " into a pipeline expression.");
968
+ }
969
+
970
+ @SuppressWarnings("unchecked")
971
+ Map<String, Object> map = (Map<String, Object>) currentValue;
972
+
973
+ Object nested = map.get("expr");
974
+ if (nested != null) {
975
+ currentValue = nested;
976
+ currentFieldName = currentFieldName + ".expr";
977
+ continue;
978
+ }
979
+
980
+ nested = map.get("expression");
981
+ if (nested != null) {
982
+ currentValue = nested;
983
+ currentFieldName = currentFieldName + ".expression";
984
+ continue;
985
+ }
986
+
987
+ Object operatorName = map.get("operator");
988
+ if (operatorName instanceof String) {
989
+ LoweredBooleanBox booleanBox = new LoweredBooleanBox();
990
+ stack.push(
991
+ new ExitApplyPendingUnaryBooleanFrame(
992
+ enterFrame.box, booleanBox, new ArrayList<>(pendingUnaryFunctions)));
993
+ stack.push(new EnterObjectBooleanFrame(currentValue, currentFieldName, booleanBox));
994
+ break;
995
+ }
996
+
997
+ Object name = map.get("name");
998
+ if (name instanceof String) {
999
+ String functionName = (String) name;
1000
+ if (isBooleanFunctionName(functionName)) {
1001
+ LoweredBooleanBox booleanBox = new LoweredBooleanBox();
1002
+ stack.push(
1003
+ new ExitApplyPendingUnaryBooleanFrame(
1004
+ enterFrame.box, booleanBox, new ArrayList<>(pendingUnaryFunctions)));
1005
+ stack.push(new EnterObjectBooleanFrame(currentValue, currentFieldName, booleanBox));
1006
+ break;
1007
+ }
1008
+
1009
+ List<Object> args = normalizeArgs(map.get("args"));
1010
+ String normalizedFunctionName = canonicalizeExpressionFunctionName(functionName);
1011
+ if (isDeferredUnaryExpressionFunction(normalizedFunctionName) && args.size() == 1) {
1012
+ pendingUnaryFunctions.add(normalizedFunctionName);
1013
+ currentValue = args.get(0);
1014
+ currentFieldName = currentFieldName + ".args[0]";
1015
+ continue;
1016
+ }
1017
+
1018
+ LoweredExpressionBox targetBox =
1019
+ preparePendingUnaryTarget(enterFrame.box, pendingUnaryFunctions, stack);
1020
+ scheduleExpressionFunctionLowering(
1021
+ functionName, args, currentFieldName, targetBox, stack);
1022
+ break;
1023
+ }
1024
+
1025
+ Object exprType = map.get("exprType");
1026
+ if (exprType instanceof String) {
1027
+ String normalizedType = ((String) exprType).toLowerCase(Locale.ROOT);
1028
+ if ("field".equals(normalizedType)) {
1029
+ enterFrame.box.value =
1030
+ applyPendingUnaryExpressionFunctions(
1031
+ Expression.field(coerceFieldPath(currentValue, currentFieldName)),
1032
+ pendingUnaryFunctions);
1033
+ break;
1034
+ }
1035
+ if ("constant".equals(normalizedType)) {
1036
+ enterFrame.box.value =
1037
+ applyPendingUnaryExpressionFunctions(
1038
+ constantExpression(
1039
+ resolveConstantValue(map.get("value"), currentFieldName + ".value")),
1040
+ pendingUnaryFunctions);
1041
+ break;
1042
+ }
1043
+ }
1044
+
1045
+ if (map.containsKey("fieldPath")
1046
+ || map.containsKey("path")
1047
+ || map.containsKey("segments")
1048
+ || map.containsKey("_segments")) {
1049
+ enterFrame.box.value =
1050
+ applyPendingUnaryExpressionFunctions(
1051
+ Expression.field(coerceFieldPath(currentValue, currentFieldName)),
1052
+ pendingUnaryFunctions);
1053
+ break;
1054
+ }
1055
+
1056
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
1057
+ "pipelineExecute() could not convert "
1058
+ + currentFieldName
1059
+ + " into a pipeline expression.");
1060
+ }
1061
+ continue;
1062
+ }
1063
+
1064
+ if (frame instanceof EnterObjectBooleanFrame) {
1065
+ EnterObjectBooleanFrame enterFrame = (EnterObjectBooleanFrame) frame;
1066
+ Object currentValue = enterFrame.value;
1067
+ String currentFieldName = enterFrame.fieldName;
1068
+
1069
+ while (currentValue instanceof Map) {
1070
+ Map<?, ?> map = (Map<?, ?>) currentValue;
1071
+ Object nested = map.get("condition");
1072
+ if (nested == null) {
1073
+ break;
1074
+ }
1075
+ currentValue = nested;
1076
+ currentFieldName = currentFieldName + ".condition";
1077
+ }
1078
+
1079
+ if (currentValue instanceof Map) {
1080
+ @SuppressWarnings("unchecked")
1081
+ Map<String, Object> map = (Map<String, Object>) currentValue;
1082
+
1083
+ Object operatorName = map.get("operator");
1084
+ if (operatorName instanceof String) {
1085
+ String normalizedOperator = ((String) operatorName).toUpperCase(Locale.ROOT);
1086
+ if ("AND".equals(normalizedOperator) || "OR".equals(normalizedOperator)) {
1087
+ Object queriesValue = map.get("queries");
1088
+ if (!(queriesValue instanceof List) || ((List<?>) queriesValue).isEmpty()) {
1089
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
1090
+ "pipelineExecute() expected "
1091
+ + currentFieldName
1092
+ + ".queries to contain boolean expressions.");
1093
+ }
1094
+
1095
+ List<?> queries = (List<?>) queriesValue;
1096
+ List<LoweredBooleanBox> childBoxes = new ArrayList<>(queries.size());
1097
+ for (int i = 0; i < queries.size(); i++) {
1098
+ childBoxes.add(new LoweredBooleanBox());
1099
+ }
1100
+ stack.push(
1101
+ new ExitBooleanLogicalFrame(
1102
+ enterFrame.box,
1103
+ "AND".equals(normalizedOperator),
1104
+ childBoxes,
1105
+ currentFieldName));
1106
+ for (int i = queries.size() - 1; i >= 0; i--) {
1107
+ stack.push(
1108
+ new EnterObjectBooleanFrame(
1109
+ queries.get(i),
1110
+ currentFieldName + ".queries[" + i + "]",
1111
+ childBoxes.get(i)));
1112
+ }
1113
+ continue;
1114
+ }
1115
+
1116
+ Object fieldValue =
1117
+ map.get("fieldPath") != null ? map.get("fieldPath") : map.get("field");
1118
+ if (fieldValue == null) {
1119
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
1120
+ "pipelineExecute() expected " + currentFieldName + ".fieldPath to be provided.");
1121
+ }
1122
+
1123
+ List<Object> args = new ArrayList<>(2);
1124
+ args.add(fieldValue);
1125
+ args.add(
1126
+ map.containsKey("value")
1127
+ ? map.get("value")
1128
+ : map.containsKey("right") ? map.get("right") : map.get("operand"));
1129
+ scheduleBooleanFunctionLowering(
1130
+ mapOperatorToFunctionName(normalizedOperator),
1131
+ args,
1132
+ currentFieldName,
1133
+ enterFrame.box,
1134
+ stack);
1135
+ continue;
1136
+ }
1137
+
1138
+ Object name = map.get("name");
1139
+ if (name instanceof String) {
1140
+ String normalizedName = canonicalizeExpressionFunctionName((String) name);
1141
+ List<Object> args = normalizeArgs(map.get("args"));
1142
+ if ("and".equals(normalizedName) || "or".equals(normalizedName)) {
1143
+ if (args.isEmpty()) {
1144
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
1145
+ "pipelineExecute() expected "
1146
+ + currentFieldName
1147
+ + ".args to contain boolean expressions.");
1148
+ }
1149
+
1150
+ List<LoweredBooleanBox> childBoxes = new ArrayList<>(args.size());
1151
+ for (int i = 0; i < args.size(); i++) {
1152
+ childBoxes.add(new LoweredBooleanBox());
1153
+ }
1154
+ stack.push(
1155
+ new ExitBooleanLogicalFrame(
1156
+ enterFrame.box, "and".equals(normalizedName), childBoxes, currentFieldName));
1157
+ for (int i = args.size() - 1; i >= 0; i--) {
1158
+ stack.push(
1159
+ new EnterObjectBooleanFrame(
1160
+ args.get(i), currentFieldName + ".args[" + i + "]", childBoxes.get(i)));
1161
+ }
1162
+ continue;
1163
+ }
1164
+
1165
+ scheduleBooleanFunctionLowering(
1166
+ (String) name, args, currentFieldName, enterFrame.box, stack);
1167
+ continue;
1168
+ }
1169
+ }
1170
+
1171
+ LoweredExpressionBox expressionBox = new LoweredExpressionBox();
1172
+ stack.push(
1173
+ new ExitCastExpressionToBooleanFrame(enterFrame.box, expressionBox, currentFieldName));
1174
+ stack.push(new EnterObjectExpressionFrame(currentValue, currentFieldName, expressionBox));
1175
+ continue;
1176
+ }
1177
+
1178
+ if (frame instanceof EnterObjectExpressionValueFrame) {
1179
+ EnterObjectExpressionValueFrame enterFrame = (EnterObjectExpressionValueFrame) frame;
1180
+ if (enterFrame.value instanceof Expression) {
1181
+ enterFrame.box.value = (Expression) enterFrame.value;
1182
+ continue;
1183
+ }
1184
+ if (containsLowerableExpression(enterFrame.value)) {
1185
+ stack.push(
1186
+ new EnterObjectExpressionFrame(
1187
+ enterFrame.value, enterFrame.fieldName, enterFrame.box));
1188
+ continue;
1189
+ }
1190
+ enterFrame.box.value =
1191
+ constantExpression(resolveConstantValue(enterFrame.value, enterFrame.fieldName));
1192
+ continue;
1193
+ }
1194
+
1195
+ if (frame instanceof EnterObjectBooleanValueFrame) {
1196
+ EnterObjectBooleanValueFrame enterFrame = (EnterObjectBooleanValueFrame) frame;
1197
+ if (enterFrame.value instanceof BooleanExpression) {
1198
+ enterFrame.box.value = (BooleanExpression) enterFrame.value;
1199
+ continue;
1200
+ }
1201
+ if (containsLowerableExpression(enterFrame.value)) {
1202
+ stack.push(
1203
+ new EnterObjectBooleanFrame(enterFrame.value, enterFrame.fieldName, enterFrame.box));
1204
+ continue;
1205
+ }
1206
+
1207
+ Expression expression =
1208
+ constantExpression(resolveConstantValue(enterFrame.value, enterFrame.fieldName));
1209
+ if (expression instanceof BooleanExpression) {
1210
+ enterFrame.box.value = (BooleanExpression) expression;
1211
+ continue;
1212
+ }
1213
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
1214
+ "pipelineExecute() expected "
1215
+ + enterFrame.fieldName
1216
+ + " to resolve to a boolean expression.");
1217
+ }
1218
+
1219
+ if (frame instanceof EnterObjectValueOrExpressionFrame) {
1220
+ EnterObjectValueOrExpressionFrame enterFrame = (EnterObjectValueOrExpressionFrame) frame;
1221
+ if (enterFrame.value instanceof Expression) {
1222
+ enterFrame.box.value = enterFrame.value;
1223
+ continue;
1224
+ }
1225
+ if (containsLowerableExpression(enterFrame.value)) {
1226
+ LoweredExpressionBox expressionBox = new LoweredExpressionBox();
1227
+ stack.push(new ExitSetObjectFromExpressionFrame(enterFrame.box, expressionBox));
1228
+ stack.push(
1229
+ new EnterObjectExpressionValueFrame(
1230
+ enterFrame.value, enterFrame.fieldName, expressionBox));
1231
+ continue;
1232
+ }
1233
+ enterFrame.box.value = resolveConstantValue(enterFrame.value, enterFrame.fieldName);
1234
+ continue;
1235
+ }
1236
+
1237
+ if (frame instanceof EnterObjectVectorExpressionValueFrame) {
1238
+ EnterObjectVectorExpressionValueFrame enterFrame =
1239
+ (EnterObjectVectorExpressionValueFrame) frame;
1240
+ Object currentValue = enterFrame.value;
1241
+
1242
+ if (currentValue instanceof Expression) {
1243
+ enterFrame.box.value = (Expression) currentValue;
1244
+ continue;
1245
+ }
1246
+
1247
+ while (currentValue instanceof Map) {
1248
+ @SuppressWarnings("unchecked")
1249
+ Map<String, Object> map = (Map<String, Object>) currentValue;
1250
+ Object constantValue = unwrapConstantValue(map, enterFrame.fieldName);
1251
+ if (constantValue != null) {
1252
+ currentValue = constantValue;
1253
+ if (currentValue instanceof Expression) {
1254
+ enterFrame.box.value = (Expression) currentValue;
1255
+ break;
1256
+ }
1257
+ continue;
1258
+ }
1259
+
1260
+ if (map.get("values") != null) {
1261
+ double[] vector = coerceVectorValue(map.get("values"));
1262
+ enterFrame.box.value = Expression.vector(vector);
1263
+ break;
1264
+ }
1265
+ break;
1266
+ }
1267
+
1268
+ if (enterFrame.box.value != null) {
1269
+ continue;
1270
+ }
1271
+
1272
+ if (currentValue instanceof List) {
1273
+ double[] vector = coerceVectorValue(currentValue);
1274
+ enterFrame.box.value = Expression.vector(vector);
1275
+ continue;
1276
+ }
1277
+
1278
+ stack.push(
1279
+ new EnterObjectExpressionValueFrame(
1280
+ currentValue, enterFrame.fieldName, enterFrame.box));
1281
+ continue;
1282
+ }
1283
+
1284
+ if (frame instanceof ExitApplyPendingUnaryFrame) {
1285
+ ExitApplyPendingUnaryFrame exitFrame = (ExitApplyPendingUnaryFrame) frame;
1286
+ exitFrame.box.value =
1287
+ applyPendingUnaryExpressionFunctions(
1288
+ exitFrame.childBox.value, exitFrame.pendingUnaryFunctions);
1289
+ continue;
1290
+ }
1291
+
1292
+ if (frame instanceof ExitApplyPendingUnaryBooleanFrame) {
1293
+ ExitApplyPendingUnaryBooleanFrame exitFrame = (ExitApplyPendingUnaryBooleanFrame) frame;
1294
+ exitFrame.box.value =
1295
+ applyPendingUnaryExpressionFunctions(
1296
+ exitFrame.childBox.value, exitFrame.pendingUnaryFunctions);
1297
+ continue;
1298
+ }
1299
+
1300
+ if (frame instanceof ExitCastExpressionToBooleanFrame) {
1301
+ ExitCastExpressionToBooleanFrame exitFrame = (ExitCastExpressionToBooleanFrame) frame;
1302
+ if (exitFrame.expressionBox.value instanceof BooleanExpression) {
1303
+ exitFrame.box.value = (BooleanExpression) exitFrame.expressionBox.value;
1304
+ continue;
1305
+ }
1306
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
1307
+ "pipelineExecute() expected "
1308
+ + exitFrame.fieldName
1309
+ + " to resolve to a boolean expression.");
1310
+ }
1311
+
1312
+ if (frame instanceof ExitSetObjectFromExpressionFrame) {
1313
+ ExitSetObjectFromExpressionFrame exitFrame = (ExitSetObjectFromExpressionFrame) frame;
1314
+ exitFrame.box.value = exitFrame.expressionBox.value;
1315
+ continue;
1316
+ }
1317
+
1318
+ if (frame instanceof ExitBooleanLogicalFrame) {
1319
+ ExitBooleanLogicalFrame exitFrame = (ExitBooleanLogicalFrame) frame;
1320
+ BooleanExpression[] expressions = new BooleanExpression[exitFrame.childBoxes.size()];
1321
+ for (int i = 0; i < exitFrame.childBoxes.size(); i++) {
1322
+ BooleanExpression valueAtIndex = exitFrame.childBoxes.get(i).value;
1323
+ if (valueAtIndex == null) {
1324
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
1325
+ "pipelineExecute() expected "
1326
+ + exitFrame.fieldName
1327
+ + " to contain boolean expressions.");
1328
+ }
1329
+ expressions[i] = valueAtIndex;
1330
+ }
1331
+ BooleanExpression first = expressions[0];
1332
+ BooleanExpression[] rest = Arrays.copyOfRange(expressions, 1, expressions.length);
1333
+ exitFrame.box.value =
1334
+ exitFrame.andOperator ? Expression.and(first, rest) : Expression.or(first, rest);
1335
+ continue;
1336
+ }
1337
+
1338
+ if (frame instanceof ExitObjectConditionalExpressionFrame) {
1339
+ ExitObjectConditionalExpressionFrame exitFrame =
1340
+ (ExitObjectConditionalExpressionFrame) frame;
1341
+ exitFrame.box.value =
1342
+ Expression.conditional(
1343
+ exitFrame.conditionBox.value, exitFrame.trueBox.value, exitFrame.falseBox.value);
1344
+ continue;
1345
+ }
1346
+
1347
+ if (frame instanceof ExitObjectIsTypeExpressionFrame) {
1348
+ ExitObjectIsTypeExpressionFrame exitFrame = (ExitObjectIsTypeExpressionFrame) frame;
1349
+ exitFrame.box.value = exitFrame.expressionBox.value.type().equal(exitFrame.typeName);
1350
+ continue;
1351
+ }
1352
+
1353
+ if (frame instanceof ExitObjectArrayExpressionFrame) {
1354
+ ExitObjectArrayExpressionFrame exitFrame = (ExitObjectArrayExpressionFrame) frame;
1355
+ Expression[] expressions = new Expression[exitFrame.childBoxes.size()];
1356
+ for (int i = 0; i < exitFrame.childBoxes.size(); i++) {
1357
+ expressions[i] = exitFrame.childBoxes.get(i).value;
1358
+ }
1359
+ exitFrame.box.value = Expression.rawFunction("array", expressions);
1360
+ continue;
1361
+ }
1362
+
1363
+ if (frame instanceof ExitObjectMapExpressionFrame) {
1364
+ ExitObjectMapExpressionFrame exitFrame = (ExitObjectMapExpressionFrame) frame;
1365
+ Expression[] expressions = new Expression[exitFrame.entries.size() * 2];
1366
+ int index = 0;
1367
+ for (Map.Entry<String, LoweredExpressionBox> entry : exitFrame.entries) {
1368
+ expressions[index++] = constantExpression(entry.getKey());
1369
+ expressions[index++] = entry.getValue().value;
1370
+ }
1371
+ exitFrame.box.value = Expression.rawFunction("map", expressions);
1372
+ continue;
1373
+ }
1374
+
1375
+ if (frame instanceof ExitObjectRawExpressionFunctionFrame) {
1376
+ ExitObjectRawExpressionFunctionFrame exitFrame =
1377
+ (ExitObjectRawExpressionFunctionFrame) frame;
1378
+ Expression[] expressions = new Expression[exitFrame.childBoxes.size()];
1379
+ for (int i = 0; i < exitFrame.childBoxes.size(); i++) {
1380
+ expressions[i] = exitFrame.childBoxes.get(i).value;
1381
+ }
1382
+ exitFrame.box.value =
1383
+ Expression.rawFunction(
1384
+ normalizeExpressionFunctionName(exitFrame.functionName), expressions);
1385
+ continue;
1386
+ }
1387
+
1388
+ if (frame instanceof ExitObjectRawBooleanFunctionFrame) {
1389
+ ExitObjectRawBooleanFunctionFrame exitFrame = (ExitObjectRawBooleanFunctionFrame) frame;
1390
+ Expression[] expressions = new Expression[exitFrame.childBoxes.size()];
1391
+ for (int i = 0; i < exitFrame.childBoxes.size(); i++) {
1392
+ expressions[i] = exitFrame.childBoxes.get(i).value;
1393
+ }
1394
+ exitFrame.box.value =
1395
+ BooleanExpression.rawFunction(
1396
+ normalizeExpressionFunctionName(exitFrame.functionName), expressions);
1397
+ continue;
1398
+ }
1399
+
1400
+ if (frame instanceof ContinueReceiverExpressionChainFrame) {
1401
+ ContinueReceiverExpressionChainFrame continueFrame =
1402
+ (ContinueReceiverExpressionChainFrame) frame;
1403
+ Expression currentExpression =
1404
+ continueFrame.currentExpression != null
1405
+ ? continueFrame.currentExpression
1406
+ : continueFrame.baseBox.value;
1407
+ if (continueFrame.nextIndex < 0) {
1408
+ continueFrame.box.value = currentExpression;
1409
+ continue;
1410
+ }
1411
+
1412
+ PendingReceiverOperation operation =
1413
+ continueFrame.pendingOperations.get(continueFrame.nextIndex);
1414
+ List<Object> args = operation.args;
1415
+ String operationFieldName = operation.fieldName;
1416
+ int nextIndex = continueFrame.nextIndex - 1;
1417
+
1418
+ switch (operation.normalizedName) {
1419
+ case "logicalmaximum":
1420
+ case "logicalminimum":
1421
+ {
1422
+ if (args.size() < 2) {
1423
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
1424
+ "pipelineExecute() expected "
1425
+ + operationFieldName
1426
+ + "."
1427
+ + operation.originalName
1428
+ + " to include at least 2 arguments.");
1429
+ }
1430
+ List<LoweredExpressionBox> childBoxes = new ArrayList<>(args.size() - 1);
1431
+ for (int i = 1; i < args.size(); i++) {
1432
+ childBoxes.add(new LoweredExpressionBox());
1433
+ }
1434
+ stack.push(
1435
+ new ExitReceiverLogicalExtremaFrame(
1436
+ continueFrame.box,
1437
+ continueFrame.pendingOperations,
1438
+ nextIndex,
1439
+ currentExpression,
1440
+ "logicalmaximum".equals(operation.normalizedName),
1441
+ childBoxes));
1442
+ for (int i = childBoxes.size() - 1; i >= 0; i--) {
1443
+ int argIndex = i + 1;
1444
+ stack.push(
1445
+ new EnterObjectExpressionValueFrame(
1446
+ args.get(argIndex),
1447
+ operationFieldName + ".args[" + argIndex + "]",
1448
+ childBoxes.get(i)));
1449
+ }
1450
+ continue;
1451
+ }
1452
+ case "mapget":
1453
+ {
1454
+ Object keyArg = args.get(1);
1455
+ if (!containsLowerableExpression(keyArg)) {
1456
+ Object keyValue = resolveConstantValue(keyArg, operationFieldName + ".args[1]");
1457
+ if (keyValue instanceof String) {
1458
+ stack.push(
1459
+ new ContinueReceiverExpressionChainFrame(
1460
+ continueFrame.box,
1461
+ null,
1462
+ continueFrame.pendingOperations,
1463
+ nextIndex,
1464
+ currentExpression.mapGet((String) keyValue)));
1465
+ continue;
1466
+ }
1467
+ }
1468
+
1469
+ LoweredExpressionBox keyBox = new LoweredExpressionBox();
1470
+ stack.push(
1471
+ new ExitReceiverMapGetFrame(
1472
+ continueFrame.box,
1473
+ continueFrame.pendingOperations,
1474
+ nextIndex,
1475
+ currentExpression,
1476
+ keyBox));
1477
+ stack.push(
1478
+ new EnterObjectExpressionValueFrame(
1479
+ keyArg, operationFieldName + ".args[1]", keyBox));
1480
+ continue;
1481
+ }
1482
+ case "mapmerge":
1483
+ {
1484
+ if (args.size() < 2) {
1485
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
1486
+ "pipelineExecute() expected "
1487
+ + operationFieldName
1488
+ + "."
1489
+ + operation.originalName
1490
+ + " to include at least 2 arguments.");
1491
+ }
1492
+ List<LoweredExpressionBox> childBoxes = new ArrayList<>(args.size() - 1);
1493
+ for (int i = 1; i < args.size(); i++) {
1494
+ childBoxes.add(new LoweredExpressionBox());
1495
+ }
1496
+ stack.push(
1497
+ new ExitReceiverMapMergeFrame(
1498
+ continueFrame.box,
1499
+ continueFrame.pendingOperations,
1500
+ nextIndex,
1501
+ currentExpression,
1502
+ childBoxes));
1503
+ for (int i = childBoxes.size() - 1; i >= 0; i--) {
1504
+ int argIndex = i + 1;
1505
+ stack.push(
1506
+ new EnterObjectExpressionValueFrame(
1507
+ args.get(argIndex),
1508
+ operationFieldName + ".args[" + argIndex + "]",
1509
+ childBoxes.get(i)));
1510
+ }
1511
+ continue;
1512
+ }
1513
+ case "arrayget":
1514
+ {
1515
+ Object indexArg = args.get(1);
1516
+ if (!containsLowerableExpression(indexArg)) {
1517
+ Object indexValue = resolveConstantValue(indexArg, operationFieldName + ".args[1]");
1518
+ if (indexValue instanceof Number) {
1519
+ stack.push(
1520
+ new ContinueReceiverExpressionChainFrame(
1521
+ continueFrame.box,
1522
+ null,
1523
+ continueFrame.pendingOperations,
1524
+ nextIndex,
1525
+ currentExpression.arrayGet(((Number) indexValue).intValue())));
1526
+ continue;
1527
+ }
1528
+ }
1529
+
1530
+ LoweredExpressionBox indexBox = new LoweredExpressionBox();
1531
+ stack.push(
1532
+ new ExitReceiverArrayGetFrame(
1533
+ continueFrame.box,
1534
+ continueFrame.pendingOperations,
1535
+ nextIndex,
1536
+ currentExpression,
1537
+ indexBox));
1538
+ stack.push(
1539
+ new EnterObjectExpressionValueFrame(
1540
+ indexArg, operationFieldName + ".args[1]", indexBox));
1541
+ continue;
1542
+ }
1543
+ case "arrayconcat":
1544
+ {
1545
+ if (args.size() < 2) {
1546
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
1547
+ "pipelineExecute() expected "
1548
+ + operationFieldName
1549
+ + "."
1550
+ + operation.originalName
1551
+ + " to include at least 2 arguments.");
1552
+ }
1553
+ List<LoweredObjectBox> childBoxes = new ArrayList<>(args.size() - 1);
1554
+ for (int i = 1; i < args.size(); i++) {
1555
+ childBoxes.add(new LoweredObjectBox());
1556
+ }
1557
+ stack.push(
1558
+ new ExitReceiverArrayConcatFrame(
1559
+ continueFrame.box,
1560
+ continueFrame.pendingOperations,
1561
+ nextIndex,
1562
+ currentExpression,
1563
+ childBoxes));
1564
+ for (int i = childBoxes.size() - 1; i >= 0; i--) {
1565
+ int argIndex = i + 1;
1566
+ stack.push(
1567
+ new EnterObjectValueOrExpressionFrame(
1568
+ args.get(argIndex),
1569
+ operationFieldName + ".args[" + argIndex + "]",
1570
+ childBoxes.get(i)));
1571
+ }
1572
+ continue;
1573
+ }
1574
+ case "cosinedistance":
1575
+ case "dotproduct":
1576
+ case "euclideandistance":
1577
+ {
1578
+ Object rightArg = args.get(1);
1579
+ if (!containsLowerableExpression(rightArg)) {
1580
+ Object rightValue = resolveConstantValue(rightArg, operationFieldName + ".args[1]");
1581
+ if (rightValue instanceof List
1582
+ || (rightValue instanceof Map
1583
+ && ((Map<?, ?>) rightValue).get("values") != null)) {
1584
+ double[] vector = coerceVectorValue(rightValue);
1585
+ Expression nextExpression =
1586
+ "cosinedistance".equals(operation.normalizedName)
1587
+ ? currentExpression.cosineDistance(vector)
1588
+ : "dotproduct".equals(operation.normalizedName)
1589
+ ? currentExpression.dotProduct(vector)
1590
+ : currentExpression.euclideanDistance(vector);
1591
+ stack.push(
1592
+ new ContinueReceiverExpressionChainFrame(
1593
+ continueFrame.box,
1594
+ null,
1595
+ continueFrame.pendingOperations,
1596
+ nextIndex,
1597
+ nextExpression));
1598
+ continue;
1599
+ }
1600
+ }
1601
+
1602
+ LoweredExpressionBox rightBox = new LoweredExpressionBox();
1603
+ stack.push(
1604
+ new ExitReceiverVectorDistanceFrame(
1605
+ continueFrame.box,
1606
+ continueFrame.pendingOperations,
1607
+ nextIndex,
1608
+ currentExpression,
1609
+ operation.normalizedName,
1610
+ rightBox));
1611
+ stack.push(
1612
+ new EnterObjectVectorExpressionValueFrame(
1613
+ rightArg, operationFieldName + ".args[1]", rightBox));
1614
+ continue;
1615
+ }
1616
+ case "timestampadd":
1617
+ case "timestampsubtract":
1618
+ {
1619
+ if (args.size() != 3) {
1620
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
1621
+ "pipelineExecute() expected "
1622
+ + operationFieldName
1623
+ + "."
1624
+ + operation.originalName
1625
+ + " to include exactly 3 arguments.");
1626
+ }
1627
+ Object unitArg = args.get(1);
1628
+ Object amountArg = args.get(2);
1629
+ if (!containsLowerableExpression(unitArg)
1630
+ && !containsLowerableExpression(amountArg)) {
1631
+ Object unitValue = resolveConstantValue(unitArg, operationFieldName + ".args[1]");
1632
+ Object amountValue =
1633
+ resolveConstantValue(amountArg, operationFieldName + ".args[2]");
1634
+ if (unitValue instanceof String && amountValue instanceof Number) {
1635
+ long amount = ((Number) amountValue).longValue();
1636
+ Expression nextExpression =
1637
+ "timestampadd".equals(operation.normalizedName)
1638
+ ? currentExpression.timestampAdd((String) unitValue, amount)
1639
+ : currentExpression.timestampSubtract((String) unitValue, amount);
1640
+ stack.push(
1641
+ new ContinueReceiverExpressionChainFrame(
1642
+ continueFrame.box,
1643
+ null,
1644
+ continueFrame.pendingOperations,
1645
+ nextIndex,
1646
+ nextExpression));
1647
+ continue;
1648
+ }
1649
+ }
1650
+
1651
+ LoweredExpressionBox unitBox = new LoweredExpressionBox();
1652
+ LoweredExpressionBox amountBox = new LoweredExpressionBox();
1653
+ stack.push(
1654
+ new ExitReceiverTimestampMathFrame(
1655
+ continueFrame.box,
1656
+ continueFrame.pendingOperations,
1657
+ nextIndex,
1658
+ currentExpression,
1659
+ operation.normalizedName,
1660
+ unitBox,
1661
+ amountBox));
1662
+ stack.push(
1663
+ new EnterObjectExpressionValueFrame(
1664
+ amountArg, operationFieldName + ".args[2]", amountBox));
1665
+ stack.push(
1666
+ new EnterObjectExpressionValueFrame(
1667
+ unitArg, operationFieldName + ".args[1]", unitBox));
1668
+ continue;
1669
+ }
1670
+ case "timestamptruncate":
1671
+ {
1672
+ Object granularityArg = args.get(1);
1673
+ if (!containsLowerableExpression(granularityArg)) {
1674
+ Object granularityValue =
1675
+ resolveConstantValue(granularityArg, operationFieldName + ".args[1]");
1676
+ if (granularityValue instanceof String) {
1677
+ stack.push(
1678
+ new ContinueReceiverExpressionChainFrame(
1679
+ continueFrame.box,
1680
+ null,
1681
+ continueFrame.pendingOperations,
1682
+ nextIndex,
1683
+ currentExpression.timestampTruncate((String) granularityValue)));
1684
+ continue;
1685
+ }
1686
+ }
1687
+
1688
+ LoweredExpressionBox granularityBox = new LoweredExpressionBox();
1689
+ stack.push(
1690
+ new ExitReceiverTimestampTruncateFrame(
1691
+ continueFrame.box,
1692
+ continueFrame.pendingOperations,
1693
+ nextIndex,
1694
+ currentExpression,
1695
+ granularityBox));
1696
+ stack.push(
1697
+ new EnterObjectExpressionValueFrame(
1698
+ granularityArg, operationFieldName + ".args[1]", granularityBox));
1699
+ continue;
1700
+ }
1701
+ default:
1702
+ stack.push(
1703
+ new ContinueReceiverExpressionChainFrame(
1704
+ continueFrame.box,
1705
+ null,
1706
+ continueFrame.pendingOperations,
1707
+ nextIndex,
1708
+ currentExpression));
1709
+ continue;
1710
+ }
1711
+ }
1712
+
1713
+ if (frame instanceof ExitReceiverLogicalExtremaFrame) {
1714
+ ExitReceiverLogicalExtremaFrame exitFrame = (ExitReceiverLogicalExtremaFrame) frame;
1715
+ Expression[] others = new Expression[exitFrame.childBoxes.size()];
1716
+ for (int i = 0; i < exitFrame.childBoxes.size(); i++) {
1717
+ others[i] = exitFrame.childBoxes.get(i).value;
1718
+ }
1719
+ Expression nextExpression =
1720
+ exitFrame.maximum
1721
+ ? exitFrame.currentExpression.logicalMaximum(others)
1722
+ : exitFrame.currentExpression.logicalMinimum(others);
1723
+ stack.push(
1724
+ new ContinueReceiverExpressionChainFrame(
1725
+ exitFrame.box,
1726
+ null,
1727
+ exitFrame.pendingOperations,
1728
+ exitFrame.nextIndex,
1729
+ nextExpression));
1730
+ continue;
1731
+ }
1732
+
1733
+ if (frame instanceof ExitReceiverMapGetFrame) {
1734
+ ExitReceiverMapGetFrame exitFrame = (ExitReceiverMapGetFrame) frame;
1735
+ stack.push(
1736
+ new ContinueReceiverExpressionChainFrame(
1737
+ exitFrame.box,
1738
+ null,
1739
+ exitFrame.pendingOperations,
1740
+ exitFrame.nextIndex,
1741
+ exitFrame.currentExpression.mapGet(exitFrame.keyBox.value)));
1742
+ continue;
1743
+ }
1744
+
1745
+ if (frame instanceof ExitReceiverMapMergeFrame) {
1746
+ ExitReceiverMapMergeFrame exitFrame = (ExitReceiverMapMergeFrame) frame;
1747
+ Expression right = exitFrame.childBoxes.get(0).value;
1748
+ Expression[] others = new Expression[Math.max(0, exitFrame.childBoxes.size() - 1)];
1749
+ for (int i = 1; i < exitFrame.childBoxes.size(); i++) {
1750
+ others[i - 1] = exitFrame.childBoxes.get(i).value;
1751
+ }
1752
+ stack.push(
1753
+ new ContinueReceiverExpressionChainFrame(
1754
+ exitFrame.box,
1755
+ null,
1756
+ exitFrame.pendingOperations,
1757
+ exitFrame.nextIndex,
1758
+ exitFrame.currentExpression.mapMerge(right, others)));
1759
+ continue;
1760
+ }
1761
+
1762
+ if (frame instanceof ExitReceiverArrayGetFrame) {
1763
+ ExitReceiverArrayGetFrame exitFrame = (ExitReceiverArrayGetFrame) frame;
1764
+ stack.push(
1765
+ new ContinueReceiverExpressionChainFrame(
1766
+ exitFrame.box,
1767
+ null,
1768
+ exitFrame.pendingOperations,
1769
+ exitFrame.nextIndex,
1770
+ exitFrame.currentExpression.arrayGet(exitFrame.indexBox.value)));
1771
+ continue;
1772
+ }
1773
+
1774
+ if (frame instanceof ExitReceiverArrayConcatFrame) {
1775
+ ExitReceiverArrayConcatFrame exitFrame = (ExitReceiverArrayConcatFrame) frame;
1776
+ Object secondValue = exitFrame.childBoxes.get(0).value;
1777
+ Object[] rest = new Object[Math.max(0, exitFrame.childBoxes.size() - 1)];
1778
+ for (int i = 1; i < exitFrame.childBoxes.size(); i++) {
1779
+ rest[i - 1] = exitFrame.childBoxes.get(i).value;
1780
+ }
1781
+ stack.push(
1782
+ new ContinueReceiverExpressionChainFrame(
1783
+ exitFrame.box,
1784
+ null,
1785
+ exitFrame.pendingOperations,
1786
+ exitFrame.nextIndex,
1787
+ exitFrame.currentExpression.arrayConcat(secondValue, rest)));
1788
+ continue;
1789
+ }
1790
+
1791
+ if (frame instanceof ExitReceiverVectorDistanceFrame) {
1792
+ ExitReceiverVectorDistanceFrame exitFrame = (ExitReceiverVectorDistanceFrame) frame;
1793
+ Expression nextExpression =
1794
+ "cosinedistance".equals(exitFrame.normalizedName)
1795
+ ? exitFrame.currentExpression.cosineDistance(exitFrame.rightBox.value)
1796
+ : "dotproduct".equals(exitFrame.normalizedName)
1797
+ ? exitFrame.currentExpression.dotProduct(exitFrame.rightBox.value)
1798
+ : exitFrame.currentExpression.euclideanDistance(exitFrame.rightBox.value);
1799
+ stack.push(
1800
+ new ContinueReceiverExpressionChainFrame(
1801
+ exitFrame.box,
1802
+ null,
1803
+ exitFrame.pendingOperations,
1804
+ exitFrame.nextIndex,
1805
+ nextExpression));
1806
+ continue;
1807
+ }
1808
+
1809
+ if (frame instanceof ExitReceiverTimestampMathFrame) {
1810
+ ExitReceiverTimestampMathFrame exitFrame = (ExitReceiverTimestampMathFrame) frame;
1811
+ Expression nextExpression =
1812
+ "timestampadd".equals(exitFrame.normalizedName)
1813
+ ? exitFrame.currentExpression.timestampAdd(
1814
+ exitFrame.unitBox.value, exitFrame.amountBox.value)
1815
+ : exitFrame.currentExpression.timestampSubtract(
1816
+ exitFrame.unitBox.value, exitFrame.amountBox.value);
1817
+ stack.push(
1818
+ new ContinueReceiverExpressionChainFrame(
1819
+ exitFrame.box,
1820
+ null,
1821
+ exitFrame.pendingOperations,
1822
+ exitFrame.nextIndex,
1823
+ nextExpression));
1824
+ continue;
1825
+ }
1826
+
1827
+ if (frame instanceof ExitReceiverTimestampTruncateFrame) {
1828
+ ExitReceiverTimestampTruncateFrame exitFrame = (ExitReceiverTimestampTruncateFrame) frame;
1829
+ stack.push(
1830
+ new ContinueReceiverExpressionChainFrame(
1831
+ exitFrame.box,
1832
+ null,
1833
+ exitFrame.pendingOperations,
1834
+ exitFrame.nextIndex,
1835
+ exitFrame.currentExpression.timestampTruncate(exitFrame.granularityBox.value)));
1836
+ continue;
1837
+ }
1838
+
1839
+ if (frame instanceof ExitApplyBooleanReceiverFrame) {
1840
+ ExitApplyBooleanReceiverFrame exitFrame = (ExitApplyBooleanReceiverFrame) frame;
1841
+ Expression leftExpression = exitFrame.leftBox.value;
1842
+ Object rightArg = exitFrame.rightArg;
1843
+ String rightFieldName = exitFrame.fieldName + ".args[1]";
1844
+
1845
+ if (!containsLowerableExpression(rightArg)) {
1846
+ Object resolved = resolveConstantValue(rightArg, rightFieldName);
1847
+ BooleanExpression directResult =
1848
+ applyBooleanReceiverConstant(exitFrame.normalizedName, leftExpression, resolved);
1849
+ if (directResult != null) {
1850
+ exitFrame.box.value = directResult;
1851
+ continue;
1852
+ }
1853
+ }
1854
+
1855
+ LoweredExpressionBox rightBox = new LoweredExpressionBox();
1856
+ stack.push(
1857
+ new ExitFinalizeBooleanReceiverFrame(
1858
+ exitFrame.box,
1859
+ leftExpression,
1860
+ exitFrame.normalizedName,
1861
+ rightBox,
1862
+ exitFrame.fieldName));
1863
+ stack.push(new EnterObjectExpressionValueFrame(rightArg, rightFieldName, rightBox));
1864
+ continue;
1865
+ }
1866
+
1867
+ ExitFinalizeBooleanReceiverFrame exitFrame = (ExitFinalizeBooleanReceiverFrame) frame;
1868
+ exitFrame.box.value =
1869
+ applyBooleanReceiverExpression(
1870
+ exitFrame.normalizedName, exitFrame.leftExpression, exitFrame.rightBox.value);
1871
+ }
1872
+ }
1873
+
1874
+ private void scheduleExpressionFunctionLowering(
1875
+ String functionName,
1876
+ List<Object> args,
1877
+ String fieldName,
1878
+ LoweredExpressionBox box,
1879
+ ArrayDeque<ObjectLoweringFrame> stack)
1880
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
1881
+ String normalizedName = canonicalizeExpressionFunctionName(functionName);
1882
+
1883
+ switch (normalizedName) {
1884
+ case "array":
1885
+ {
1886
+ List<Object> elements = args;
1887
+ if (args.size() == 1) {
1888
+ List<Object> unwrapped = unwrapConstantArray(args.get(0), fieldName + ".args[0]");
1889
+ if (unwrapped != null) {
1890
+ elements = unwrapped;
1891
+ }
1892
+ }
1893
+
1894
+ boolean allConstant = true;
1895
+ for (Object element : elements) {
1896
+ if (containsLowerableExpression(element)) {
1897
+ allConstant = false;
1898
+ break;
1899
+ }
1900
+ }
1901
+
1902
+ if (allConstant) {
1903
+ List<Object> resolved = new ArrayList<>(elements.size());
1904
+ for (int i = 0; i < elements.size(); i++) {
1905
+ resolved.add(resolveConstantValue(elements.get(i), fieldName + ".args[" + i + "]"));
1906
+ }
1907
+ box.value = constantExpression(resolved);
1908
+ return;
1909
+ }
1910
+
1911
+ List<LoweredExpressionBox> childBoxes = new ArrayList<>(elements.size());
1912
+ for (int i = 0; i < elements.size(); i++) {
1913
+ childBoxes.add(new LoweredExpressionBox());
1914
+ }
1915
+ stack.push(new ExitObjectArrayExpressionFrame(box, childBoxes));
1916
+ for (int i = elements.size() - 1; i >= 0; i--) {
1917
+ stack.push(
1918
+ new EnterObjectExpressionValueFrame(
1919
+ elements.get(i), fieldName + ".args[" + i + "]", childBoxes.get(i)));
1920
+ }
1921
+ return;
1922
+ }
1923
+ case "map":
1924
+ {
1925
+ requireArgumentCount(args, 1, "map", fieldName);
1926
+ Map<String, Object> entries = unwrapConstantMap(args.get(0), fieldName + ".args[0]");
1927
+ if (entries == null) {
1928
+ scheduleRawExpressionFunction(functionName, args, fieldName, box, stack);
1929
+ return;
1930
+ }
1931
+
1932
+ boolean allConstant = true;
1933
+ for (Object entryValue : entries.values()) {
1934
+ if (containsLowerableExpression(entryValue)) {
1935
+ allConstant = false;
1936
+ break;
1937
+ }
1938
+ }
1939
+
1940
+ if (allConstant) {
1941
+ Map<String, Object> resolved = new LinkedHashMap<>();
1942
+ for (Map.Entry<String, Object> entry : entries.entrySet()) {
1943
+ resolved.put(
1944
+ entry.getKey(),
1945
+ resolveConstantValue(entry.getValue(), fieldName + ".args[0]." + entry.getKey()));
1946
+ }
1947
+ box.value = constantExpression(resolved);
1948
+ return;
1949
+ }
1950
+
1951
+ List<Map.Entry<String, LoweredExpressionBox>> boxedEntries =
1952
+ new ArrayList<>(entries.size());
1953
+ for (Map.Entry<String, Object> entry : entries.entrySet()) {
1954
+ boxedEntries.add(
1955
+ new java.util.AbstractMap.SimpleEntry<>(
1956
+ entry.getKey(), new LoweredExpressionBox()));
1957
+ }
1958
+ stack.push(new ExitObjectMapExpressionFrame(box, boxedEntries));
1959
+ List<Map.Entry<String, Object>> pendingEntries = new ArrayList<>(entries.entrySet());
1960
+ for (int i = pendingEntries.size() - 1; i >= 0; i--) {
1961
+ Map.Entry<String, Object> entry = pendingEntries.get(i);
1962
+ stack.push(
1963
+ new EnterObjectExpressionValueFrame(
1964
+ entry.getValue(),
1965
+ fieldName + ".args[0]." + entry.getKey(),
1966
+ boxedEntries.get(i).getValue()));
1967
+ }
1968
+ return;
1969
+ }
1970
+ case "conditional":
1971
+ {
1972
+ requireArgumentCount(args, 3, functionName, fieldName);
1973
+ LoweredBooleanBox conditionBox = new LoweredBooleanBox();
1974
+ LoweredExpressionBox trueBox = new LoweredExpressionBox();
1975
+ LoweredExpressionBox falseBox = new LoweredExpressionBox();
1976
+ stack.push(
1977
+ new ExitObjectConditionalExpressionFrame(box, conditionBox, trueBox, falseBox));
1978
+ stack.push(
1979
+ new EnterObjectExpressionValueFrame(args.get(2), fieldName + ".args[2]", falseBox));
1980
+ stack.push(
1981
+ new EnterObjectExpressionValueFrame(args.get(1), fieldName + ".args[1]", trueBox));
1982
+ stack.push(
1983
+ new EnterObjectBooleanValueFrame(args.get(0), fieldName + ".args[0]", conditionBox));
1984
+ return;
1985
+ }
1986
+ case "currenttimestamp":
1987
+ requireArgumentCount(args, 0, functionName, fieldName);
1988
+ box.value = Expression.currentTimestamp();
1989
+ return;
1990
+ case "istype":
1991
+ {
1992
+ requireArgumentCount(args, 2, functionName, fieldName);
1993
+ String typeName = coerceStringValue(args.get(1), fieldName + ".args[1]");
1994
+ LoweredExpressionBox expressionBox = new LoweredExpressionBox();
1995
+ stack.push(new ExitObjectIsTypeExpressionFrame(box, expressionBox, typeName));
1996
+ stack.push(
1997
+ new EnterObjectExpressionValueFrame(
1998
+ args.get(0), fieldName + ".args[0]", expressionBox));
1999
+ return;
2000
+ }
2001
+ case "logicalmaximum":
2002
+ case "logicalminimum":
2003
+ case "mapget":
2004
+ case "mapmerge":
2005
+ case "arrayget":
2006
+ case "arrayconcat":
2007
+ case "cosinedistance":
2008
+ case "dotproduct":
2009
+ case "euclideandistance":
2010
+ case "timestampadd":
2011
+ case "timestampsubtract":
2012
+ scheduleReceiverExpressionChain(normalizedName, functionName, args, fieldName, box, stack);
2013
+ return;
2014
+ case "timestamptruncate":
2015
+ if (args.size() == 2) {
2016
+ scheduleReceiverExpressionChain(
2017
+ normalizedName, functionName, args, fieldName, box, stack);
2018
+ return;
2019
+ }
2020
+ box.value = null;
2021
+ return;
2022
+ default:
2023
+ scheduleRawExpressionFunction(functionName, args, fieldName, box, stack);
2024
+ }
2025
+ }
2026
+
2027
+ private void scheduleRawExpressionFunction(
2028
+ String functionName,
2029
+ List<Object> args,
2030
+ String fieldName,
2031
+ LoweredExpressionBox box,
2032
+ ArrayDeque<ObjectLoweringFrame> stack) {
2033
+ List<LoweredExpressionBox> childBoxes = new ArrayList<>(args.size());
2034
+ for (int i = 0; i < args.size(); i++) {
2035
+ childBoxes.add(new LoweredExpressionBox());
2036
+ }
2037
+ stack.push(new ExitObjectRawExpressionFunctionFrame(box, functionName, childBoxes));
2038
+ for (int i = args.size() - 1; i >= 0; i--) {
2039
+ stack.push(
2040
+ new EnterObjectExpressionValueFrame(
2041
+ args.get(i), fieldName + ".args[" + i + "]", childBoxes.get(i)));
2042
+ }
2043
+ }
2044
+
2045
+ private void scheduleBooleanFunctionLowering(
2046
+ String functionName,
2047
+ List<Object> args,
2048
+ String fieldName,
2049
+ LoweredBooleanBox box,
2050
+ ArrayDeque<ObjectLoweringFrame> stack)
2051
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2052
+ String normalizedName = canonicalizeExpressionFunctionName(functionName);
2053
+
2054
+ if ("equal".equals(normalizedName)
2055
+ || "notequal".equals(normalizedName)
2056
+ || "greaterthan".equals(normalizedName)
2057
+ || "greaterthanorequal".equals(normalizedName)
2058
+ || "lessthan".equals(normalizedName)
2059
+ || "lessthanorequal".equals(normalizedName)
2060
+ || "arraycontains".equals(normalizedName)
2061
+ || "arraycontainsany".equals(normalizedName)
2062
+ || "arraycontainsall".equals(normalizedName)
2063
+ || "equalany".equals(normalizedName)
2064
+ || "notequalany".equals(normalizedName)) {
2065
+ if (args.size() < 2) {
2066
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
2067
+ "pipelineExecute() expected "
2068
+ + fieldName
2069
+ + ".args to include left and right operands.");
2070
+ }
2071
+ scheduleBooleanReceiverChain(normalizedName, functionName, args, fieldName, box, stack);
2072
+ return;
2073
+ }
2074
+
2075
+ List<LoweredExpressionBox> childBoxes = new ArrayList<>(args.size());
2076
+ for (int i = 0; i < args.size(); i++) {
2077
+ childBoxes.add(new LoweredExpressionBox());
2078
+ }
2079
+ stack.push(new ExitObjectRawBooleanFunctionFrame(box, functionName, childBoxes));
2080
+ for (int i = args.size() - 1; i >= 0; i--) {
2081
+ stack.push(
2082
+ new EnterObjectExpressionValueFrame(
2083
+ args.get(i), fieldName + ".args[" + i + "]", childBoxes.get(i)));
2084
+ }
2085
+ }
2086
+
2087
+ private void scheduleReceiverExpressionChain(
2088
+ String normalizedName,
2089
+ String originalName,
2090
+ List<Object> args,
2091
+ String fieldName,
2092
+ LoweredExpressionBox box,
2093
+ ArrayDeque<ObjectLoweringFrame> stack)
2094
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2095
+ ReceiverChainSeed seed =
2096
+ collectReceiverExpressionChain(normalizedName, originalName, args, fieldName);
2097
+ LoweredExpressionBox baseBox = new LoweredExpressionBox();
2098
+ stack.push(
2099
+ new ContinueReceiverExpressionChainFrame(
2100
+ box, baseBox, seed.pendingOperations, seed.pendingOperations.size() - 1, null));
2101
+ stack.push(new EnterObjectExpressionFrame(seed.baseValue, seed.baseFieldName, baseBox));
2102
+ }
2103
+
2104
+ private void scheduleBooleanReceiverChain(
2105
+ String normalizedName,
2106
+ String originalName,
2107
+ List<Object> args,
2108
+ String fieldName,
2109
+ LoweredBooleanBox box,
2110
+ ArrayDeque<ObjectLoweringFrame> stack)
2111
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2112
+ ReceiverChainSeed seed =
2113
+ collectReceiverExpressionChain(normalizedName, originalName, args, fieldName);
2114
+ LoweredExpressionBox leftBox = new LoweredExpressionBox();
2115
+ LoweredExpressionBox baseBox = new LoweredExpressionBox();
2116
+ stack.push(
2117
+ new ExitApplyBooleanReceiverFrame(box, leftBox, normalizedName, args.get(1), fieldName));
2118
+ stack.push(
2119
+ new ContinueReceiverExpressionChainFrame(
2120
+ leftBox, baseBox, seed.pendingOperations, seed.pendingOperations.size() - 1, null));
2121
+ stack.push(new EnterObjectExpressionFrame(seed.baseValue, seed.baseFieldName, baseBox));
2122
+ }
2123
+
2124
+ private ReceiverChainSeed collectReceiverExpressionChain(
2125
+ String normalizedName, String originalName, List<Object> args, String fieldName)
2126
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2127
+ if (args.isEmpty()) {
2128
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
2129
+ "pipelineExecute() expected "
2130
+ + fieldName
2131
+ + "."
2132
+ + originalName
2133
+ + " to include at least 1 argument.");
2134
+ }
2135
+
2136
+ List<PendingReceiverOperation> pendingOperations = new ArrayList<>();
2137
+ pendingOperations.add(
2138
+ new PendingReceiverOperation(normalizedName, originalName, args, fieldName));
2139
+
2140
+ Object currentValue = args.get(0);
2141
+ String currentFieldName = fieldName + ".args[0]";
2142
+
2143
+ while (currentValue instanceof Map) {
2144
+ @SuppressWarnings("unchecked")
2145
+ Map<String, Object> map = (Map<String, Object>) currentValue;
2146
+
2147
+ Object nested = map.get("expr");
2148
+ if (nested != null) {
2149
+ currentValue = nested;
2150
+ currentFieldName = currentFieldName + ".expr";
2151
+ continue;
2152
+ }
2153
+
2154
+ nested = map.get("expression");
2155
+ if (nested != null) {
2156
+ currentValue = nested;
2157
+ currentFieldName = currentFieldName + ".expression";
2158
+ continue;
2159
+ }
2160
+
2161
+ Object nestedName = map.get("name");
2162
+ if (!(nestedName instanceof String)) {
2163
+ break;
2164
+ }
2165
+
2166
+ List<Object> nestedArgs = normalizeArgs(map.get("args"));
2167
+ String nestedNormalizedName = canonicalizeExpressionFunctionName((String) nestedName);
2168
+ if (!isDeferredReceiverExpressionFunction(nestedNormalizedName) || nestedArgs.isEmpty()) {
2169
+ break;
2170
+ }
2171
+
2172
+ pendingOperations.add(
2173
+ new PendingReceiverOperation(
2174
+ nestedNormalizedName, (String) nestedName, nestedArgs, currentFieldName));
2175
+ currentValue = nestedArgs.get(0);
2176
+ currentFieldName = currentFieldName + ".args[0]";
2177
+ }
2178
+
2179
+ return new ReceiverChainSeed(currentValue, currentFieldName, pendingOperations);
2180
+ }
2181
+
2182
+ private BooleanExpression applyBooleanReceiverConstant(
2183
+ String normalizedName, Expression expression, Object value)
2184
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2185
+ switch (normalizedName) {
2186
+ case "equal":
2187
+ return expression.equal(value);
2188
+ case "notequal":
2189
+ return expression.notEqual(value);
2190
+ case "greaterthan":
2191
+ return expression.greaterThan(value);
2192
+ case "greaterthanorequal":
2193
+ return expression.greaterThanOrEqual(value);
2194
+ case "lessthan":
2195
+ return expression.lessThan(value);
2196
+ case "lessthanorequal":
2197
+ return expression.lessThanOrEqual(value);
2198
+ case "arraycontains":
2199
+ return expression.arrayContains(value);
2200
+ case "arraycontainsany":
2201
+ return value instanceof List ? expression.arrayContainsAny((List<?>) value) : null;
2202
+ case "arraycontainsall":
2203
+ return value instanceof List ? expression.arrayContainsAll((List<?>) value) : null;
2204
+ case "equalany":
2205
+ return value instanceof List ? expression.equalAny((List<?>) value) : null;
2206
+ case "notequalany":
2207
+ return value instanceof List ? expression.notEqualAny((List<?>) value) : null;
2208
+ default:
2209
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
2210
+ "pipelineExecute() expected a boolean receiver operation.");
2211
+ }
2212
+ }
2213
+
2214
+ private BooleanExpression applyBooleanReceiverExpression(
2215
+ String normalizedName, Expression expression, Expression value)
2216
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2217
+ switch (normalizedName) {
2218
+ case "equal":
2219
+ return expression.equal(value);
2220
+ case "notequal":
2221
+ return expression.notEqual(value);
2222
+ case "greaterthan":
2223
+ return expression.greaterThan(value);
2224
+ case "greaterthanorequal":
2225
+ return expression.greaterThanOrEqual(value);
2226
+ case "lessthan":
2227
+ return expression.lessThan(value);
2228
+ case "lessthanorequal":
2229
+ return expression.lessThanOrEqual(value);
2230
+ case "arraycontains":
2231
+ return expression.arrayContains(value);
2232
+ case "arraycontainsany":
2233
+ return expression.arrayContainsAny(value);
2234
+ case "arraycontainsall":
2235
+ return expression.arrayContainsAll(value);
2236
+ case "equalany":
2237
+ return expression.equalAny(value);
2238
+ case "notequalany":
2239
+ return expression.notEqualAny(value);
2240
+ default:
2241
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
2242
+ "pipelineExecute() expected a boolean receiver operation.");
2243
+ }
2244
+ }
2245
+
2246
+ private boolean containsLowerableExpression(Object value) {
2247
+ return value instanceof Expression || containsSerializedExpression(value);
2248
+ }
2249
+
2250
+ private LoweredExpressionBox preparePendingUnaryTarget(
2251
+ LoweredExpressionBox box,
2252
+ List<String> pendingUnaryFunctions,
2253
+ ArrayDeque<ObjectLoweringFrame> stack) {
2254
+ if (pendingUnaryFunctions.isEmpty()) {
2255
+ return box;
2256
+ }
2257
+ LoweredExpressionBox childBox = new LoweredExpressionBox();
2258
+ stack.push(
2259
+ new ExitApplyPendingUnaryFrame(box, childBox, new ArrayList<>(pendingUnaryFunctions)));
2260
+ return childBox;
2261
+ }
2262
+
2263
+ private void requireArgumentCount(
2264
+ List<Object> args, int expectedCount, String functionName, String fieldName)
2265
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2266
+ if (args.size() != expectedCount) {
2267
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
2268
+ "pipelineExecute() expected "
2269
+ + fieldName
2270
+ + "."
2271
+ + functionName
2272
+ + " to include exactly "
2273
+ + expectedCount
2274
+ + " arguments.");
2275
+ }
2276
+ }
2277
+
2278
+ private Expression coerceExpressionValue(Object value, String fieldName)
2279
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2280
+ return lowerExpressionValueObject(value, fieldName);
2281
+ }
2282
+
2283
+ private BooleanExpression coerceBooleanValue(Object value, String fieldName)
2284
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2285
+ return lowerBooleanValueObject(value, fieldName);
2286
+ }
2287
+
2288
+ private String coerceStringValue(Object value, String fieldName)
2289
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2290
+ Object rawValue = resolveConstantValue(value, fieldName);
2291
+ if (rawValue instanceof String) {
2292
+ return (String) rawValue;
2293
+ }
2294
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
2295
+ "pipelineExecute() expected " + fieldName + " to resolve to a string.");
2296
+ }
2297
+
2298
+ private Object resolveValueOrExpression(Object value, String fieldName)
2299
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2300
+ return lowerValueOrExpressionObject(value, fieldName);
2301
+ }
2302
+
2303
+ private Expression coerceVectorExpressionValue(Object value, String fieldName)
2304
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2305
+ return lowerVectorExpressionValueObject(value, fieldName);
2306
+ }
2307
+
2308
+ private Object resolveConstantValue(Object value, String fieldName)
2309
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2310
+ ConstantValueBox rootBox = new ConstantValueBox();
2311
+ ArrayDeque<ConstantResolutionFrame> stack = new ArrayDeque<>();
2312
+ stack.push(new EnterConstantResolutionFrame(value, fieldName, rootBox));
2313
+
2314
+ while (!stack.isEmpty()) {
2315
+ ConstantResolutionFrame frame = stack.pop();
2316
+ if (frame instanceof EnterConstantResolutionFrame) {
2317
+ EnterConstantResolutionFrame enterFrame = (EnterConstantResolutionFrame) frame;
2318
+ Object currentValue = enterFrame.value;
2319
+ String currentFieldName = enterFrame.fieldName;
2320
+
2321
+ while (currentValue instanceof Map) {
2322
+ @SuppressWarnings("unchecked")
2323
+ Map<String, Object> map = (Map<String, Object>) currentValue;
2324
+ Object constantValue = unwrapConstantValue(map, currentFieldName);
2325
+ if (constantValue == null) {
2326
+ break;
2327
+ }
2328
+ currentValue = constantValue;
2329
+ }
2330
+
2331
+ if (currentValue instanceof Map) {
2332
+ @SuppressWarnings("unchecked")
2333
+ Map<String, Object> map = (Map<String, Object>) currentValue;
2334
+ if (isSerializedExpressionLike(map)) {
2335
+ enterFrame.box.value = coerceExpression(map, currentFieldName);
2336
+ continue;
2337
+ }
2338
+
2339
+ List<Map.Entry<String, ConstantValueBox>> entries = new ArrayList<>(map.size());
2340
+ stack.push(new ExitConstantMapFrame(enterFrame.box, entries));
2341
+ List<Map.Entry<String, Object>> pendingEntries = new ArrayList<>(map.entrySet());
2342
+ for (Map.Entry<String, Object> entry : pendingEntries) {
2343
+ ConstantValueBox childBox = new ConstantValueBox();
2344
+ entries.add(new java.util.AbstractMap.SimpleEntry<>(entry.getKey(), childBox));
2345
+ }
2346
+ for (int i = pendingEntries.size() - 1; i >= 0; i--) {
2347
+ Map.Entry<String, Object> entry = pendingEntries.get(i);
2348
+ stack.push(
2349
+ new EnterConstantResolutionFrame(
2350
+ entry.getValue(),
2351
+ currentFieldName + "." + entry.getKey(),
2352
+ entries.get(i).getValue()));
2353
+ }
2354
+ continue;
2355
+ }
2356
+
2357
+ if (currentValue instanceof List) {
2358
+ List<?> values = (List<?>) currentValue;
2359
+ List<ConstantValueBox> childBoxes = new ArrayList<>(values.size());
2360
+ for (int i = 0; i < values.size(); i++) {
2361
+ childBoxes.add(new ConstantValueBox());
2362
+ }
2363
+ stack.push(new ExitConstantListFrame(enterFrame.box, childBoxes));
2364
+ for (int i = values.size() - 1; i >= 0; i--) {
2365
+ stack.push(
2366
+ new EnterConstantResolutionFrame(
2367
+ values.get(i), currentFieldName + "[" + i + "]", childBoxes.get(i)));
2368
+ }
2369
+ continue;
2370
+ }
2371
+
2372
+ enterFrame.box.value = currentValue;
2373
+ continue;
2374
+ }
2375
+
2376
+ if (frame instanceof ExitConstantListFrame) {
2377
+ ExitConstantListFrame exitFrame = (ExitConstantListFrame) frame;
2378
+ List<Object> output = new ArrayList<>(exitFrame.childBoxes.size());
2379
+ for (ConstantValueBox childBox : exitFrame.childBoxes) {
2380
+ output.add(childBox.value);
2381
+ }
2382
+ exitFrame.box.value = output;
2383
+ continue;
2384
+ }
2385
+
2386
+ ExitConstantMapFrame exitFrame = (ExitConstantMapFrame) frame;
2387
+ Map<String, Object> output = new LinkedHashMap<>();
2388
+ for (Map.Entry<String, ConstantValueBox> entry : exitFrame.entries) {
2389
+ output.put(entry.getKey(), entry.getValue().value);
2390
+ }
2391
+ exitFrame.box.value = output;
2392
+ }
2393
+
2394
+ return rootBox.value;
2395
+ }
2396
+
2397
+ private boolean containsSerializedExpression(Object value) {
2398
+ ArrayDeque<Object> stack = new ArrayDeque<>();
2399
+ if (value != null) {
2400
+ stack.push(value);
2401
+ }
2402
+
2403
+ while (!stack.isEmpty()) {
2404
+ Object currentValue = stack.pop();
2405
+ if (currentValue == null) {
2406
+ continue;
2407
+ }
2408
+
2409
+ while (currentValue instanceof Map) {
2410
+ @SuppressWarnings("unchecked")
2411
+ Map<String, Object> map = (Map<String, Object>) currentValue;
2412
+ Object constantValue = null;
2413
+ try {
2414
+ constantValue = unwrapConstantValue(map, "");
2415
+ } catch (ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException e) {
2416
+ constantValue = null;
2417
+ }
2418
+ if (constantValue == null) {
2419
+ break;
2420
+ }
2421
+ currentValue = constantValue;
2422
+ }
2423
+
2424
+ if (currentValue instanceof Map) {
2425
+ @SuppressWarnings("unchecked")
2426
+ Map<String, Object> map = (Map<String, Object>) currentValue;
2427
+ if (isSerializedExpressionLike(map)) {
2428
+ return true;
2429
+ }
2430
+ for (Object nestedValue : map.values()) {
2431
+ if (nestedValue != null) {
2432
+ stack.push(nestedValue);
2433
+ }
2434
+ }
2435
+ continue;
2436
+ }
2437
+
2438
+ if (currentValue instanceof List) {
2439
+ for (Object nestedValue : (List<?>) currentValue) {
2440
+ if (nestedValue != null) {
2441
+ stack.push(nestedValue);
2442
+ }
2443
+ }
2444
+ }
2445
+ }
2446
+
2447
+ return false;
2448
+ }
2449
+
2450
+ private List<Object> normalizeArgs(Object argsValue) {
2451
+ List<Object> args = new ArrayList<>();
2452
+ if (argsValue == null) {
2453
+ return args;
2454
+ }
2455
+ if (argsValue instanceof List) {
2456
+ for (Object arg : (List<?>) argsValue) {
2457
+ args.add(arg);
2458
+ }
2459
+ return args;
2460
+ }
2461
+ args.add(argsValue);
2462
+ return args;
2463
+ }
2464
+
2465
+ private List<Object> unwrapConstantArray(Object value, String fieldName)
2466
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2467
+ if (value instanceof List) {
2468
+ List<Object> output = new ArrayList<>();
2469
+ for (Object entry : (List<?>) value) {
2470
+ output.add(entry);
2471
+ }
2472
+ return output;
2473
+ }
2474
+
2475
+ if (!(value instanceof Map)) {
2476
+ return null;
2477
+ }
2478
+
2479
+ @SuppressWarnings("unchecked")
2480
+ Map<String, Object> map = (Map<String, Object>) value;
2481
+ Object constantValue = unwrapConstantValue(map, fieldName);
2482
+ if (constantValue instanceof List) {
2483
+ List<Object> output = new ArrayList<>();
2484
+ for (Object entry : (List<?>) constantValue) {
2485
+ output.add(entry);
2486
+ }
2487
+ return output;
2488
+ }
2489
+ return null;
2490
+ }
2491
+
2492
+ private Map<String, Object> unwrapConstantMap(Object value, String fieldName)
2493
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2494
+ if (!(value instanceof Map)) {
2495
+ return null;
2496
+ }
2497
+
2498
+ @SuppressWarnings("unchecked")
2499
+ Map<String, Object> map = (Map<String, Object>) value;
2500
+ Object constantValue = unwrapConstantValue(map, fieldName);
2501
+ if (constantValue instanceof Map) {
2502
+ @SuppressWarnings("unchecked")
2503
+ Map<String, Object> constantMap = (Map<String, Object>) constantValue;
2504
+ return constantMap;
2505
+ }
2506
+
2507
+ return isSerializedExpressionLike(map) ? null : map;
2508
+ }
2509
+
2510
+ private Object unwrapConstantValue(Map<String, Object> map, String fieldName)
2511
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2512
+ Object exprType = map.get("exprType");
2513
+ if (!(exprType instanceof String)
2514
+ || !"constant".equals(((String) exprType).toLowerCase(Locale.ROOT))) {
2515
+ return null;
2516
+ }
2517
+
2518
+ if (!map.containsKey("value")) {
2519
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
2520
+ "pipelineExecute() expected " + fieldName + ".value to be provided.");
2521
+ }
2522
+ return map.get("value");
2523
+ }
2524
+
2525
+ private boolean isSerializedExpressionLike(Map<String, Object> map) {
2526
+ return map.get("exprType") != null
2527
+ || map.get("operator") != null
2528
+ || map.get("name") != null
2529
+ || map.get("expr") != null
2530
+ || map.get("expression") != null
2531
+ || map.get("fieldPath") != null
2532
+ || map.get("path") != null
2533
+ || map.get("segments") != null
2534
+ || map.get("_segments") != null;
2535
+ }
2536
+
2537
+ private boolean isBooleanFunctionName(String functionName) {
2538
+ String normalizedName = canonicalizeExpressionFunctionName(functionName);
2539
+ return "and".equals(normalizedName)
2540
+ || "or".equals(normalizedName)
2541
+ || "equal".equals(normalizedName)
2542
+ || "notequal".equals(normalizedName)
2543
+ || "greaterthan".equals(normalizedName)
2544
+ || "greaterthanorequal".equals(normalizedName)
2545
+ || "lessthan".equals(normalizedName)
2546
+ || "lessthanorequal".equals(normalizedName)
2547
+ || "arraycontains".equals(normalizedName)
2548
+ || "arraycontainsany".equals(normalizedName)
2549
+ || "arraycontainsall".equals(normalizedName)
2550
+ || "equalany".equals(normalizedName)
2551
+ || "notequalany".equals(normalizedName);
2552
+ }
2553
+
2554
+ private boolean isDeferredUnaryExpressionFunction(String normalizedFunctionName) {
2555
+ return "type".equals(normalizedFunctionName)
2556
+ || "collectionid".equals(normalizedFunctionName)
2557
+ || "documentid".equals(normalizedFunctionName)
2558
+ || "arraylength".equals(normalizedFunctionName)
2559
+ || "arraysum".equals(normalizedFunctionName)
2560
+ || "vectorlength".equals(normalizedFunctionName)
2561
+ || "timestamptounixmicros".equals(normalizedFunctionName)
2562
+ || "timestamptounixmillis".equals(normalizedFunctionName)
2563
+ || "timestamptounixseconds".equals(normalizedFunctionName)
2564
+ || "unixmicrostotimestamp".equals(normalizedFunctionName)
2565
+ || "unixmillistotimestamp".equals(normalizedFunctionName)
2566
+ || "unixsecondstotimestamp".equals(normalizedFunctionName);
2567
+ }
2568
+
2569
+ private boolean isDeferredReceiverExpressionFunction(String normalizedFunctionName) {
2570
+ return "logicalmaximum".equals(normalizedFunctionName)
2571
+ || "logicalminimum".equals(normalizedFunctionName)
2572
+ || "mapget".equals(normalizedFunctionName)
2573
+ || "mapmerge".equals(normalizedFunctionName)
2574
+ || "arrayget".equals(normalizedFunctionName)
2575
+ || "arrayconcat".equals(normalizedFunctionName)
2576
+ || "cosinedistance".equals(normalizedFunctionName)
2577
+ || "dotproduct".equals(normalizedFunctionName)
2578
+ || "euclideandistance".equals(normalizedFunctionName)
2579
+ || "timestampadd".equals(normalizedFunctionName)
2580
+ || "timestampsubtract".equals(normalizedFunctionName)
2581
+ || "timestamptruncate".equals(normalizedFunctionName);
2582
+ }
2583
+
2584
+ private Expression applyPendingUnaryExpressionFunctions(
2585
+ Expression expression, List<String> pendingUnaryFunctions) {
2586
+ Expression currentExpression = expression;
2587
+ for (int i = pendingUnaryFunctions.size() - 1; i >= 0; i--) {
2588
+ String functionName = pendingUnaryFunctions.get(i);
2589
+ switch (functionName) {
2590
+ case "type":
2591
+ currentExpression = currentExpression.type();
2592
+ break;
2593
+ case "collectionid":
2594
+ currentExpression = currentExpression.collectionId();
2595
+ break;
2596
+ case "documentid":
2597
+ currentExpression = currentExpression.documentId();
2598
+ break;
2599
+ case "arraylength":
2600
+ currentExpression = currentExpression.arrayLength();
2601
+ break;
2602
+ case "arraysum":
2603
+ currentExpression = currentExpression.arraySum();
2604
+ break;
2605
+ case "vectorlength":
2606
+ currentExpression = currentExpression.vectorLength();
2607
+ break;
2608
+ case "timestamptounixmicros":
2609
+ currentExpression = currentExpression.timestampToUnixMicros();
2610
+ break;
2611
+ case "timestamptounixmillis":
2612
+ currentExpression = currentExpression.timestampToUnixMillis();
2613
+ break;
2614
+ case "timestamptounixseconds":
2615
+ currentExpression = currentExpression.timestampToUnixSeconds();
2616
+ break;
2617
+ case "unixmicrostotimestamp":
2618
+ currentExpression = currentExpression.unixMicrosToTimestamp();
2619
+ break;
2620
+ case "unixmillistotimestamp":
2621
+ currentExpression = currentExpression.unixMillisToTimestamp();
2622
+ break;
2623
+ case "unixsecondstotimestamp":
2624
+ currentExpression = currentExpression.unixSecondsToTimestamp();
2625
+ break;
2626
+ default:
2627
+ break;
2628
+ }
2629
+ }
2630
+ return currentExpression;
2631
+ }
2632
+
2633
+ private String mapOperatorToFunctionName(String operatorName) {
2634
+ switch (operatorName) {
2635
+ case "==":
2636
+ case "=":
2637
+ case "EQUAL":
2638
+ return "equal";
2639
+ case "!=":
2640
+ case "<>":
2641
+ case "NOT_EQUAL":
2642
+ return "not_equal";
2643
+ case ">":
2644
+ case "GREATER_THAN":
2645
+ return "greater_than";
2646
+ case ">=":
2647
+ case "GREATER_THAN_OR_EQUAL":
2648
+ return "greater_than_or_equal";
2649
+ case "<":
2650
+ case "LESS_THAN":
2651
+ return "less_than";
2652
+ case "<=":
2653
+ case "LESS_THAN_OR_EQUAL":
2654
+ return "less_than_or_equal";
2655
+ case "ARRAY_CONTAINS":
2656
+ case "ARRAY-CONTAINS":
2657
+ return "array_contains";
2658
+ case "ARRAY_CONTAINS_ANY":
2659
+ case "ARRAY-CONTAINS-ANY":
2660
+ return "array_contains_any";
2661
+ case "ARRAY_CONTAINS_ALL":
2662
+ case "ARRAY-CONTAINS-ALL":
2663
+ return "array_contains_all";
2664
+ case "IN":
2665
+ case "EQUAL_ANY":
2666
+ case "EQUAL-ANY":
2667
+ return "equal_any";
2668
+ case "NOT_IN":
2669
+ case "NOT_EQUAL_ANY":
2670
+ case "NOT-EQUAL-ANY":
2671
+ return "not_equal_any";
2672
+ default:
2673
+ return normalizeExpressionFunctionName(operatorName);
2674
+ }
2675
+ }
2676
+
2677
+ private Expression buildParsedFunctionExpression(
2678
+ ReactNativeFirebaseFirestorePipelineParser.ParsedFunctionExpressionNode function,
2679
+ String fieldName)
2680
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2681
+ Expression specialized =
2682
+ buildSpecialParsedExpressionFunction(function.name, function.args, fieldName);
2683
+ if (specialized != null) {
2684
+ return specialized;
2685
+ }
2686
+
2687
+ Expression[] expressions = new Expression[function.args.size()];
2688
+ for (int i = 0; i < function.args.size(); i++) {
2689
+ expressions[i] =
2690
+ coerceExpressionValueNode(function.args.get(i), fieldName + ".args[" + i + "]");
2691
+ }
2692
+ return Expression.rawFunction(normalizeExpressionFunctionName(function.name), expressions);
2693
+ }
2694
+
2695
+ private Expression buildSpecialParsedExpressionFunction(
2696
+ String functionName,
2697
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args,
2698
+ String fieldName)
2699
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2700
+ String normalizedName = canonicalizeExpressionFunctionName(functionName);
2701
+
2702
+ switch (normalizedName) {
2703
+ case "array":
2704
+ return buildParsedArrayExpression(args, fieldName);
2705
+ case "map":
2706
+ return buildParsedMapExpression(args, fieldName);
2707
+ case "conditional":
2708
+ requireParsedArgumentCount(args, 3, functionName, fieldName);
2709
+ return Expression.conditional(
2710
+ coerceBooleanValueNode(args.get(0), fieldName + ".args[0]"),
2711
+ coerceExpressionValueNode(args.get(1), fieldName + ".args[1]"),
2712
+ coerceExpressionValueNode(args.get(2), fieldName + ".args[2]"));
2713
+ case "currenttimestamp":
2714
+ requireParsedArgumentCount(args, 0, functionName, fieldName);
2715
+ return Expression.currentTimestamp();
2716
+ case "type":
2717
+ requireParsedArgumentCount(args, 1, functionName, fieldName);
2718
+ return coerceExpressionValueNode(args.get(0), fieldName + ".args[0]").type();
2719
+ case "collectionid":
2720
+ requireParsedArgumentCount(args, 1, functionName, fieldName);
2721
+ return coerceExpressionValueNode(args.get(0), fieldName + ".args[0]").collectionId();
2722
+ case "documentid":
2723
+ requireParsedArgumentCount(args, 1, functionName, fieldName);
2724
+ return coerceExpressionValueNode(args.get(0), fieldName + ".args[0]").documentId();
2725
+ case "istype":
2726
+ requireParsedArgumentCount(args, 2, functionName, fieldName);
2727
+ return coerceExpressionValueNode(args.get(0), fieldName + ".args[0]")
2728
+ .type()
2729
+ .equal(coerceStringValueNode(args.get(1), fieldName + ".args[1]"));
2730
+ case "logicalmaximum":
2731
+ return buildParsedLogicalExtremaExpression(true, args, functionName, fieldName);
2732
+ case "logicalminimum":
2733
+ return buildParsedLogicalExtremaExpression(false, args, functionName, fieldName);
2734
+ case "mapget":
2735
+ requireParsedArgumentCount(args, 2, functionName, fieldName);
2736
+ return buildParsedMapGetExpression(args, fieldName);
2737
+ case "mapmerge":
2738
+ return buildParsedMapMergeExpression(args, functionName, fieldName);
2739
+ case "arraylength":
2740
+ requireParsedArgumentCount(args, 1, functionName, fieldName);
2741
+ return coerceExpressionValueNode(args.get(0), fieldName + ".args[0]").arrayLength();
2742
+ case "arrayget":
2743
+ requireParsedArgumentCount(args, 2, functionName, fieldName);
2744
+ return buildParsedArrayGetExpression(args, fieldName);
2745
+ case "arrayconcat":
2746
+ return buildParsedArrayConcatExpression(args, functionName, fieldName);
2747
+ case "arraysum":
2748
+ requireParsedArgumentCount(args, 1, functionName, fieldName);
2749
+ return coerceExpressionValueNode(args.get(0), fieldName + ".args[0]").arraySum();
2750
+ case "vectorlength":
2751
+ requireParsedArgumentCount(args, 1, functionName, fieldName);
2752
+ return coerceExpressionValueNode(args.get(0), fieldName + ".args[0]").vectorLength();
2753
+ case "cosinedistance":
2754
+ requireParsedArgumentCount(args, 2, functionName, fieldName);
2755
+ return buildParsedVectorDistanceExpression("cosineDistance", args, fieldName);
2756
+ case "dotproduct":
2757
+ requireParsedArgumentCount(args, 2, functionName, fieldName);
2758
+ return buildParsedVectorDistanceExpression("dotProduct", args, fieldName);
2759
+ case "euclideandistance":
2760
+ requireParsedArgumentCount(args, 2, functionName, fieldName);
2761
+ return buildParsedVectorDistanceExpression("euclideanDistance", args, fieldName);
2762
+ case "timestamptounixmicros":
2763
+ requireParsedArgumentCount(args, 1, functionName, fieldName);
2764
+ return coerceExpressionValueNode(args.get(0), fieldName + ".args[0]")
2765
+ .timestampToUnixMicros();
2766
+ case "timestamptounixmillis":
2767
+ requireParsedArgumentCount(args, 1, functionName, fieldName);
2768
+ return coerceExpressionValueNode(args.get(0), fieldName + ".args[0]")
2769
+ .timestampToUnixMillis();
2770
+ case "timestamptounixseconds":
2771
+ requireParsedArgumentCount(args, 1, functionName, fieldName);
2772
+ return coerceExpressionValueNode(args.get(0), fieldName + ".args[0]")
2773
+ .timestampToUnixSeconds();
2774
+ case "unixmicrostotimestamp":
2775
+ requireParsedArgumentCount(args, 1, functionName, fieldName);
2776
+ return coerceExpressionValueNode(args.get(0), fieldName + ".args[0]")
2777
+ .unixMicrosToTimestamp();
2778
+ case "unixmillistotimestamp":
2779
+ requireParsedArgumentCount(args, 1, functionName, fieldName);
2780
+ return coerceExpressionValueNode(args.get(0), fieldName + ".args[0]")
2781
+ .unixMillisToTimestamp();
2782
+ case "unixsecondstotimestamp":
2783
+ requireParsedArgumentCount(args, 1, functionName, fieldName);
2784
+ return coerceExpressionValueNode(args.get(0), fieldName + ".args[0]")
2785
+ .unixSecondsToTimestamp();
2786
+ case "timestampadd":
2787
+ requireParsedArgumentCount(args, 3, functionName, fieldName);
2788
+ return buildParsedTimestampMathExpression(true, args, fieldName);
2789
+ case "timestampsubtract":
2790
+ requireParsedArgumentCount(args, 3, functionName, fieldName);
2791
+ return buildParsedTimestampMathExpression(false, args, fieldName);
2792
+ case "timestamptruncate":
2793
+ if (args.size() == 2) {
2794
+ return buildParsedTimestampTruncateExpression(args, fieldName);
2795
+ }
2796
+ return null;
2797
+ default:
2798
+ return null;
2799
+ }
2800
+ }
2801
+
2802
+ private Expression buildParsedArrayExpression(
2803
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args, String fieldName)
2804
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2805
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> elements;
2806
+ if (args.size() == 1
2807
+ && args.get(0) instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedListValueNode) {
2808
+ elements =
2809
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedListValueNode) args.get(0)).values;
2810
+ } else {
2811
+ elements = args;
2812
+ }
2813
+
2814
+ boolean allConstant = true;
2815
+ for (ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode element : elements) {
2816
+ if (containsParsedExpression(element)) {
2817
+ allConstant = false;
2818
+ break;
2819
+ }
2820
+ }
2821
+
2822
+ if (allConstant) {
2823
+ java.util.List<Object> resolved = new java.util.ArrayList<>(elements.size());
2824
+ for (int i = 0; i < elements.size(); i++) {
2825
+ resolved.add(resolveValueNode(elements.get(i), fieldName + ".args[" + i + "]"));
2826
+ }
2827
+ return constantExpression(resolved);
2828
+ }
2829
+
2830
+ Expression[] expressions = new Expression[elements.size()];
2831
+ for (int i = 0; i < elements.size(); i++) {
2832
+ expressions[i] = coerceExpressionValueNode(elements.get(i), fieldName + ".args[" + i + "]");
2833
+ }
2834
+ return Expression.rawFunction("array", expressions);
2835
+ }
2836
+
2837
+ private Expression buildParsedMapExpression(
2838
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args, String fieldName)
2839
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2840
+ requireParsedArgumentCount(args, 1, "map", fieldName);
2841
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode mapArg = args.get(0);
2842
+ if (mapArg instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionValueNode) {
2843
+ ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionNode expression =
2844
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionValueNode) mapArg)
2845
+ .expression;
2846
+ if (expression
2847
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedConstantExpressionNode) {
2848
+ mapArg =
2849
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedConstantExpressionNode) expression)
2850
+ .value;
2851
+ }
2852
+ }
2853
+
2854
+ if (!(mapArg instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedMapValueNode)) {
2855
+ return null;
2856
+ }
2857
+
2858
+ Map<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> entries =
2859
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedMapValueNode) mapArg).values;
2860
+ boolean allConstant = true;
2861
+ for (ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value : entries.values()) {
2862
+ if (containsParsedExpression(value)) {
2863
+ allConstant = false;
2864
+ break;
2865
+ }
2866
+ }
2867
+
2868
+ if (allConstant) {
2869
+ Map<String, Object> resolved = new java.util.LinkedHashMap<>();
2870
+ for (Map.Entry<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> entry :
2871
+ entries.entrySet()) {
2872
+ resolved.put(
2873
+ entry.getKey(),
2874
+ resolveValueNode(entry.getValue(), fieldName + ".args[0]." + entry.getKey()));
2875
+ }
2876
+ return constantExpression(resolved);
2877
+ }
2878
+
2879
+ Expression[] expressions = new Expression[entries.size() * 2];
2880
+ int index = 0;
2881
+ for (Map.Entry<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> entry :
2882
+ entries.entrySet()) {
2883
+ expressions[index++] = constantExpression(entry.getKey());
2884
+ expressions[index++] =
2885
+ coerceExpressionValueNode(entry.getValue(), fieldName + ".args[0]." + entry.getKey());
2886
+ }
2887
+ return Expression.rawFunction("map", expressions);
2888
+ }
2889
+
2890
+ private Expression buildParsedLogicalExtremaExpression(
2891
+ boolean maximum,
2892
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args,
2893
+ String functionName,
2894
+ String fieldName)
2895
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2896
+ if (args.size() < 2) {
2897
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
2898
+ "pipelineExecute() expected "
2899
+ + fieldName
2900
+ + "."
2901
+ + functionName
2902
+ + " to include at least 2 arguments.");
2903
+ }
2904
+
2905
+ Expression left = coerceExpressionValueNode(args.get(0), fieldName + ".args[0]");
2906
+ Expression[] others = new Expression[args.size() - 1];
2907
+ for (int i = 1; i < args.size(); i++) {
2908
+ others[i - 1] = coerceExpressionValueNode(args.get(i), fieldName + ".args[" + i + "]");
2909
+ }
2910
+ return maximum ? left.logicalMaximum(others) : left.logicalMinimum(others);
2911
+ }
2912
+
2913
+ private Expression buildParsedMapGetExpression(
2914
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args, String fieldName)
2915
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2916
+ Expression mapExpr = coerceExpressionValueNode(args.get(0), fieldName + ".args[0]");
2917
+ if (!containsParsedExpression(args.get(1))) {
2918
+ Object keyValue = resolveValueNode(args.get(1), fieldName + ".args[1]");
2919
+ if (keyValue instanceof String) {
2920
+ return mapExpr.mapGet((String) keyValue);
2921
+ }
2922
+ }
2923
+ return mapExpr.mapGet(coerceExpressionValueNode(args.get(1), fieldName + ".args[1]"));
2924
+ }
2925
+
2926
+ private Expression buildParsedMapMergeExpression(
2927
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args,
2928
+ String functionName,
2929
+ String fieldName)
2930
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2931
+ if (args.size() < 2) {
2932
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
2933
+ "pipelineExecute() expected "
2934
+ + fieldName
2935
+ + "."
2936
+ + functionName
2937
+ + " to include at least 2 arguments.");
2938
+ }
2939
+
2940
+ Expression left = coerceExpressionValueNode(args.get(0), fieldName + ".args[0]");
2941
+ Expression right = coerceExpressionValueNode(args.get(1), fieldName + ".args[1]");
2942
+ Expression[] others = new Expression[Math.max(0, args.size() - 2)];
2943
+ for (int i = 2; i < args.size(); i++) {
2944
+ others[i - 2] = coerceExpressionValueNode(args.get(i), fieldName + ".args[" + i + "]");
2945
+ }
2946
+ return left.mapMerge(right, others);
2947
+ }
2948
+
2949
+ private Expression buildParsedArrayGetExpression(
2950
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args, String fieldName)
2951
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2952
+ Expression arrayExpr = coerceExpressionValueNode(args.get(0), fieldName + ".args[0]");
2953
+ if (!containsParsedExpression(args.get(1))) {
2954
+ Object indexValue = resolveValueNode(args.get(1), fieldName + ".args[1]");
2955
+ if (indexValue instanceof Number) {
2956
+ return arrayExpr.arrayGet(((Number) indexValue).intValue());
2957
+ }
2958
+ }
2959
+ return arrayExpr.arrayGet(coerceExpressionValueNode(args.get(1), fieldName + ".args[1]"));
2960
+ }
2961
+
2962
+ private Expression buildParsedArrayConcatExpression(
2963
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args,
2964
+ String functionName,
2965
+ String fieldName)
2966
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2967
+ if (args.size() < 2) {
2968
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
2969
+ "pipelineExecute() expected "
2970
+ + fieldName
2971
+ + "."
2972
+ + functionName
2973
+ + " to include at least 2 arguments.");
2974
+ }
2975
+
2976
+ Expression arrayExpr = coerceExpressionValueNode(args.get(0), fieldName + ".args[0]");
2977
+ Object secondValue = resolveValueOrExpressionNode(args.get(1), fieldName + ".args[1]");
2978
+ Object[] rest = new Object[Math.max(0, args.size() - 2)];
2979
+ for (int i = 2; i < args.size(); i++) {
2980
+ rest[i - 2] = resolveValueOrExpressionNode(args.get(i), fieldName + ".args[" + i + "]");
2981
+ }
2982
+ return arrayExpr.arrayConcat(secondValue, rest);
2983
+ }
2984
+
2985
+ private Expression buildParsedVectorDistanceExpression(
2986
+ String functionName,
2987
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args,
2988
+ String fieldName)
2989
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
2990
+ Expression left = coerceExpressionValueNode(args.get(0), fieldName + ".args[0]");
2991
+ if (!containsParsedExpression(args.get(1))) {
2992
+ Object rightValue = resolveValueNode(args.get(1), fieldName + ".args[1]");
2993
+ if (rightValue instanceof List
2994
+ || (rightValue instanceof Map && ((Map<?, ?>) rightValue).get("values") != null)) {
2995
+ double[] vector = coerceVectorValue(rightValue);
2996
+ switch (functionName) {
2997
+ case "cosineDistance":
2998
+ return left.cosineDistance(vector);
2999
+ case "dotProduct":
3000
+ return left.dotProduct(vector);
3001
+ default:
3002
+ return left.euclideanDistance(vector);
3003
+ }
3004
+ }
3005
+ }
3006
+
3007
+ Expression right = coerceExpressionValueNode(args.get(1), fieldName + ".args[1]");
3008
+ switch (functionName) {
3009
+ case "cosineDistance":
3010
+ return left.cosineDistance(right);
3011
+ case "dotProduct":
3012
+ return left.dotProduct(right);
3013
+ default:
3014
+ return left.euclideanDistance(right);
3015
+ }
3016
+ }
3017
+
3018
+ private Expression buildParsedTimestampMathExpression(
3019
+ boolean addition,
3020
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args,
3021
+ String fieldName)
3022
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3023
+ Expression base = coerceExpressionValueNode(args.get(0), fieldName + ".args[0]");
3024
+ if (!containsParsedExpression(args.get(1)) && !containsParsedExpression(args.get(2))) {
3025
+ Object unitValue = resolveValueNode(args.get(1), fieldName + ".args[1]");
3026
+ Object amountValue = resolveValueNode(args.get(2), fieldName + ".args[2]");
3027
+ if (unitValue instanceof String && amountValue instanceof Number) {
3028
+ long amount = ((Number) amountValue).longValue();
3029
+ return addition
3030
+ ? base.timestampAdd((String) unitValue, amount)
3031
+ : base.timestampSubtract((String) unitValue, amount);
3032
+ }
3033
+ }
3034
+
3035
+ Expression unitExpression = coerceExpressionValueNode(args.get(1), fieldName + ".args[1]");
3036
+ Expression amountExpression = coerceExpressionValueNode(args.get(2), fieldName + ".args[2]");
3037
+ return addition
3038
+ ? base.timestampAdd(unitExpression, amountExpression)
3039
+ : base.timestampSubtract(unitExpression, amountExpression);
3040
+ }
3041
+
3042
+ private Expression buildParsedTimestampTruncateExpression(
3043
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args, String fieldName)
3044
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3045
+ Expression base = coerceExpressionValueNode(args.get(0), fieldName + ".args[0]");
3046
+ if (!containsParsedExpression(args.get(1))) {
3047
+ Object granularityValue = resolveValueNode(args.get(1), fieldName + ".args[1]");
3048
+ if (granularityValue instanceof String) {
3049
+ return base.timestampTruncate((String) granularityValue);
3050
+ }
3051
+ }
3052
+ return base.timestampTruncate(coerceExpressionValueNode(args.get(1), fieldName + ".args[1]"));
3053
+ }
3054
+
3055
+ private void requireParsedArgumentCount(
3056
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args,
3057
+ int expectedCount,
3058
+ String functionName,
3059
+ String fieldName)
3060
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3061
+ if (args.size() != expectedCount) {
3062
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
3063
+ "pipelineExecute() expected "
3064
+ + fieldName
3065
+ + "."
3066
+ + functionName
3067
+ + " to include exactly "
3068
+ + expectedCount
3069
+ + " arguments.");
3070
+ }
3071
+ }
3072
+
3073
+ private Expression coerceExpressionValueNode(
3074
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value, String fieldName)
3075
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3076
+ return coerceExpressionValue(serializeValueNode(value), fieldName);
3077
+ }
3078
+
3079
+ private BooleanExpression coerceBooleanValueNode(
3080
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value, String fieldName)
3081
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3082
+ return coerceBooleanValue(serializeValueNode(value), fieldName);
3083
+ }
3084
+
3085
+ private String coerceStringValueNode(
3086
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value, String fieldName)
3087
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3088
+ return coerceStringValue(serializeValueNode(value), fieldName);
3089
+ }
3090
+
3091
+ private Object resolveValueOrExpressionNode(
3092
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value, String fieldName)
3093
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3094
+ return resolveValueOrExpression(serializeValueNode(value), fieldName);
3095
+ }
3096
+
3097
+ private Object resolveValueNode(
3098
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value, String fieldName)
3099
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3100
+ ResolvedValueBox rootBox = new ResolvedValueBox();
3101
+ java.util.ArrayDeque<ValueResolutionFrame> stack = new java.util.ArrayDeque<>();
3102
+ stack.push(new EnterValueResolutionFrame(value, fieldName, rootBox));
3103
+
3104
+ while (!stack.isEmpty()) {
3105
+ ValueResolutionFrame frame = stack.pop();
3106
+ if (frame instanceof EnterValueResolutionFrame) {
3107
+ EnterValueResolutionFrame enterFrame = (EnterValueResolutionFrame) frame;
3108
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode currentValue = enterFrame.value;
3109
+ String currentFieldName = enterFrame.fieldName;
3110
+
3111
+ if (currentValue
3112
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedPrimitiveValueNode) {
3113
+ enterFrame.box.value =
3114
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedPrimitiveValueNode) currentValue)
3115
+ .value;
3116
+ continue;
3117
+ }
3118
+
3119
+ if (currentValue
3120
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionValueNode) {
3121
+ ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionNode expression =
3122
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionValueNode) currentValue)
3123
+ .expression;
3124
+ if (expression
3125
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedConstantExpressionNode) {
3126
+ stack.push(
3127
+ new EnterValueResolutionFrame(
3128
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedConstantExpressionNode)
3129
+ expression)
3130
+ .value,
3131
+ currentFieldName,
3132
+ enterFrame.box));
3133
+ continue;
3134
+ }
3135
+ enterFrame.box.value = coerceExpression(expression, currentFieldName);
3136
+ continue;
3137
+ }
3138
+
3139
+ if (currentValue
3140
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedListValueNode) {
3141
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> values =
3142
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedListValueNode) currentValue)
3143
+ .values;
3144
+ List<ResolvedValueBox> childBoxes = new java.util.ArrayList<>(values.size());
3145
+ for (int i = 0; i < values.size(); i++) {
3146
+ childBoxes.add(new ResolvedValueBox());
3147
+ }
3148
+ stack.push(new ExitListValueResolutionFrame(enterFrame.box, childBoxes));
3149
+ for (int i = values.size() - 1; i >= 0; i--) {
3150
+ stack.push(
3151
+ new EnterValueResolutionFrame(
3152
+ values.get(i), currentFieldName + "[" + i + "]", childBoxes.get(i)));
3153
+ }
3154
+ continue;
3155
+ }
3156
+
3157
+ if (currentValue instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedMapValueNode) {
3158
+ Map<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> values =
3159
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedMapValueNode) currentValue).values;
3160
+ List<Map.Entry<String, ResolvedValueBox>> entries =
3161
+ new java.util.ArrayList<>(values.size());
3162
+ stack.push(new ExitMapValueResolutionFrame(enterFrame.box, entries));
3163
+ List<Map.Entry<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode>>
3164
+ pendingEntries = new java.util.ArrayList<>(values.entrySet());
3165
+ for (Map.Entry<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> entry :
3166
+ pendingEntries) {
3167
+ ResolvedValueBox childBox = new ResolvedValueBox();
3168
+ entries.add(new java.util.AbstractMap.SimpleEntry<>(entry.getKey(), childBox));
3169
+ }
3170
+ for (int i = pendingEntries.size() - 1; i >= 0; i--) {
3171
+ Map.Entry<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> entry =
3172
+ pendingEntries.get(i);
3173
+ stack.push(
3174
+ new EnterValueResolutionFrame(
3175
+ entry.getValue(),
3176
+ currentFieldName + "." + entry.getKey(),
3177
+ entries.get(i).getValue()));
3178
+ }
3179
+ continue;
3180
+ }
3181
+
3182
+ enterFrame.box.value = null;
3183
+ continue;
3184
+ }
3185
+
3186
+ if (frame instanceof ExitListValueResolutionFrame) {
3187
+ ExitListValueResolutionFrame exitFrame = (ExitListValueResolutionFrame) frame;
3188
+ List<Object> output = new java.util.ArrayList<>(exitFrame.childBoxes.size());
3189
+ for (ResolvedValueBox childBox : exitFrame.childBoxes) {
3190
+ output.add(childBox.value);
3191
+ }
3192
+ exitFrame.box.value = output;
3193
+ continue;
3194
+ }
3195
+
3196
+ ExitMapValueResolutionFrame exitFrame = (ExitMapValueResolutionFrame) frame;
3197
+ Map<String, Object> output = new java.util.LinkedHashMap<>();
3198
+ for (Map.Entry<String, ResolvedValueBox> entry : exitFrame.entries) {
3199
+ output.put(entry.getKey(), entry.getValue().value);
3200
+ }
3201
+ exitFrame.box.value = output;
3202
+ }
3203
+
3204
+ return rootBox.value;
3205
+ }
3206
+
3207
+ private boolean containsParsedExpression(
3208
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value) {
3209
+ java.util.ArrayDeque<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> stack =
3210
+ new java.util.ArrayDeque<>();
3211
+ stack.push(value);
3212
+
3213
+ while (!stack.isEmpty()) {
3214
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode currentValue = stack.pop();
3215
+ if (currentValue
3216
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionValueNode) {
3217
+ ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionNode expression =
3218
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionValueNode) currentValue)
3219
+ .expression;
3220
+ if (expression
3221
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedConstantExpressionNode) {
3222
+ stack.push(
3223
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedConstantExpressionNode) expression)
3224
+ .value);
3225
+ continue;
3226
+ }
3227
+ return true;
3228
+ }
3229
+
3230
+ if (currentValue instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedListValueNode) {
3231
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> values =
3232
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedListValueNode) currentValue).values;
3233
+ for (int i = values.size() - 1; i >= 0; i--) {
3234
+ stack.push(values.get(i));
3235
+ }
3236
+ continue;
3237
+ }
3238
+
3239
+ if (currentValue instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedMapValueNode) {
3240
+ for (ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode entry :
3241
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedMapValueNode) currentValue)
3242
+ .values.values()) {
3243
+ stack.push(entry);
3244
+ }
3245
+ }
3246
+ }
3247
+
3248
+ return false;
3249
+ }
3250
+
3251
+ private Object serializeExpressionNode(
3252
+ ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionNode value) {
3253
+ SerializedExpressionBox rootBox = new SerializedExpressionBox();
3254
+ ArrayDeque<SerializationFrame> stack = new ArrayDeque<>();
3255
+ stack.push(new EnterSerializedExpressionFrame(value, rootBox));
3256
+
3257
+ while (!stack.isEmpty()) {
3258
+ SerializationFrame frame = stack.pop();
3259
+ if (frame instanceof EnterSerializedExpressionFrame) {
3260
+ EnterSerializedExpressionFrame enterFrame = (EnterSerializedExpressionFrame) frame;
3261
+ ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionNode expression =
3262
+ enterFrame.value;
3263
+
3264
+ if (expression
3265
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedFieldExpressionNode) {
3266
+ Map<String, Object> output = new LinkedHashMap<>();
3267
+ output.put("__kind", "expression");
3268
+ output.put("exprType", "Field");
3269
+ output.put(
3270
+ "path",
3271
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedFieldExpressionNode) expression)
3272
+ .path);
3273
+ enterFrame.box.value = output;
3274
+ continue;
3275
+ }
3276
+
3277
+ if (expression
3278
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedConstantExpressionNode) {
3279
+ SerializedValueBox valueBox = new SerializedValueBox();
3280
+ stack.push(new ExitSerializedExpressionConstantFrame(enterFrame.box, valueBox));
3281
+ stack.push(
3282
+ new EnterSerializedValueFrame(
3283
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedConstantExpressionNode)
3284
+ expression)
3285
+ .value,
3286
+ valueBox));
3287
+ continue;
3288
+ }
3289
+
3290
+ ReactNativeFirebaseFirestorePipelineParser.ParsedFunctionExpressionNode function =
3291
+ (ReactNativeFirebaseFirestorePipelineParser.ParsedFunctionExpressionNode) expression;
3292
+ List<SerializedValueBox> argBoxes = new ArrayList<>(function.args.size());
3293
+ for (int i = 0; i < function.args.size(); i++) {
3294
+ argBoxes.add(new SerializedValueBox());
3295
+ }
3296
+ stack.push(
3297
+ new ExitSerializedExpressionFunctionFrame(enterFrame.box, function.name, argBoxes));
3298
+ for (int i = function.args.size() - 1; i >= 0; i--) {
3299
+ stack.push(new EnterSerializedValueFrame(function.args.get(i), argBoxes.get(i)));
3300
+ }
3301
+ continue;
3302
+ }
3303
+
3304
+ if (frame instanceof ExitSerializedExpressionFunctionFrame) {
3305
+ ExitSerializedExpressionFunctionFrame exitFrame =
3306
+ (ExitSerializedExpressionFunctionFrame) frame;
3307
+ Map<String, Object> output = new LinkedHashMap<>();
3308
+ output.put("__kind", "expression");
3309
+ output.put("exprType", "Function");
3310
+ output.put("name", exitFrame.name);
3311
+ List<Object> args = new ArrayList<>(exitFrame.argBoxes.size());
3312
+ for (SerializedValueBox argBox : exitFrame.argBoxes) {
3313
+ args.add(argBox.value);
3314
+ }
3315
+ output.put("args", args);
3316
+ exitFrame.box.value = output;
3317
+ continue;
3318
+ }
3319
+
3320
+ if (frame instanceof EnterSerializedValueFrame) {
3321
+ EnterSerializedValueFrame enterFrame = (EnterSerializedValueFrame) frame;
3322
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode currentValue = enterFrame.value;
3323
+
3324
+ if (currentValue
3325
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedPrimitiveValueNode) {
3326
+ enterFrame.box.value =
3327
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedPrimitiveValueNode) currentValue)
3328
+ .value;
3329
+ continue;
3330
+ }
3331
+
3332
+ if (currentValue
3333
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedListValueNode) {
3334
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> values =
3335
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedListValueNode) currentValue)
3336
+ .values;
3337
+ List<SerializedValueBox> childBoxes = new ArrayList<>(values.size());
3338
+ for (int i = 0; i < values.size(); i++) {
3339
+ childBoxes.add(new SerializedValueBox());
3340
+ }
3341
+ stack.push(new ExitSerializedValueListFrame(enterFrame.box, childBoxes));
3342
+ for (int i = values.size() - 1; i >= 0; i--) {
3343
+ stack.push(new EnterSerializedValueFrame(values.get(i), childBoxes.get(i)));
3344
+ }
3345
+ continue;
3346
+ }
3347
+
3348
+ if (currentValue instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedMapValueNode) {
3349
+ Map<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> values =
3350
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedMapValueNode) currentValue).values;
3351
+ List<Map.Entry<String, SerializedValueBox>> entries = new ArrayList<>(values.size());
3352
+ stack.push(new ExitSerializedValueMapFrame(enterFrame.box, entries));
3353
+ List<Map.Entry<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode>>
3354
+ pendingEntries = new ArrayList<>(values.entrySet());
3355
+ for (Map.Entry<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> entry :
3356
+ pendingEntries) {
3357
+ SerializedValueBox childBox = new SerializedValueBox();
3358
+ entries.add(new java.util.AbstractMap.SimpleEntry<>(entry.getKey(), childBox));
3359
+ }
3360
+ for (int i = pendingEntries.size() - 1; i >= 0; i--) {
3361
+ Map.Entry<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> entry =
3362
+ pendingEntries.get(i);
3363
+ stack.push(new EnterSerializedValueFrame(entry.getValue(), entries.get(i).getValue()));
3364
+ }
3365
+ continue;
3366
+ }
3367
+
3368
+ SerializedExpressionBox expressionBox = new SerializedExpressionBox();
3369
+ stack.push(new ExitSerializedValueExpressionFrame(enterFrame.box, expressionBox));
3370
+ stack.push(
3371
+ new EnterSerializedExpressionFrame(
3372
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionValueNode)
3373
+ currentValue)
3374
+ .expression,
3375
+ expressionBox));
3376
+ continue;
3377
+ }
3378
+
3379
+ if (frame instanceof ExitSerializedValueListFrame) {
3380
+ ExitSerializedValueListFrame exitFrame = (ExitSerializedValueListFrame) frame;
3381
+ List<Object> output = new ArrayList<>(exitFrame.childBoxes.size());
3382
+ for (SerializedValueBox childBox : exitFrame.childBoxes) {
3383
+ output.add(childBox.value);
3384
+ }
3385
+ exitFrame.box.value = output;
3386
+ continue;
3387
+ }
3388
+
3389
+ if (frame instanceof ExitSerializedValueMapFrame) {
3390
+ ExitSerializedValueMapFrame exitFrame = (ExitSerializedValueMapFrame) frame;
3391
+ Map<String, Object> output = new LinkedHashMap<>();
3392
+ for (Map.Entry<String, SerializedValueBox> entry : exitFrame.entries) {
3393
+ output.put(entry.getKey(), entry.getValue().value);
3394
+ }
3395
+ exitFrame.box.value = output;
3396
+ continue;
3397
+ }
3398
+
3399
+ if (frame instanceof ExitSerializedExpressionConstantFrame) {
3400
+ ExitSerializedExpressionConstantFrame exitFrame =
3401
+ (ExitSerializedExpressionConstantFrame) frame;
3402
+ Map<String, Object> output = new LinkedHashMap<>();
3403
+ output.put("__kind", "expression");
3404
+ output.put("exprType", "constant");
3405
+ output.put("value", exitFrame.valueBox.value);
3406
+ exitFrame.expressionBox.value = output;
3407
+ continue;
3408
+ }
3409
+
3410
+ ExitSerializedValueExpressionFrame exitFrame = (ExitSerializedValueExpressionFrame) frame;
3411
+ exitFrame.valueBox.value = exitFrame.expressionBox.value;
3412
+ }
3413
+
3414
+ return rootBox.value;
3415
+ }
3416
+
3417
+ private Object serializeValueNode(
3418
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value) {
3419
+ SerializedValueBox rootBox = new SerializedValueBox();
3420
+ ArrayDeque<SerializationFrame> stack = new ArrayDeque<>();
3421
+ stack.push(new EnterSerializedValueFrame(value, rootBox));
3422
+
3423
+ while (!stack.isEmpty()) {
3424
+ SerializationFrame frame = stack.pop();
3425
+ if (frame instanceof EnterSerializedValueFrame) {
3426
+ EnterSerializedValueFrame enterFrame = (EnterSerializedValueFrame) frame;
3427
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode currentValue = enterFrame.value;
3428
+
3429
+ if (currentValue
3430
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedPrimitiveValueNode) {
3431
+ enterFrame.box.value =
3432
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedPrimitiveValueNode) currentValue)
3433
+ .value;
3434
+ continue;
3435
+ }
3436
+
3437
+ if (currentValue
3438
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedListValueNode) {
3439
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> values =
3440
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedListValueNode) currentValue)
3441
+ .values;
3442
+ List<SerializedValueBox> childBoxes = new ArrayList<>(values.size());
3443
+ for (int i = 0; i < values.size(); i++) {
3444
+ childBoxes.add(new SerializedValueBox());
3445
+ }
3446
+ stack.push(new ExitSerializedValueListFrame(enterFrame.box, childBoxes));
3447
+ for (int i = values.size() - 1; i >= 0; i--) {
3448
+ stack.push(new EnterSerializedValueFrame(values.get(i), childBoxes.get(i)));
3449
+ }
3450
+ continue;
3451
+ }
3452
+
3453
+ if (currentValue instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedMapValueNode) {
3454
+ Map<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> values =
3455
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedMapValueNode) currentValue).values;
3456
+ List<Map.Entry<String, SerializedValueBox>> entries = new ArrayList<>(values.size());
3457
+ stack.push(new ExitSerializedValueMapFrame(enterFrame.box, entries));
3458
+ List<Map.Entry<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode>>
3459
+ pendingEntries = new ArrayList<>(values.entrySet());
3460
+ for (Map.Entry<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> entry :
3461
+ pendingEntries) {
3462
+ SerializedValueBox childBox = new SerializedValueBox();
3463
+ entries.add(new java.util.AbstractMap.SimpleEntry<>(entry.getKey(), childBox));
3464
+ }
3465
+ for (int i = pendingEntries.size() - 1; i >= 0; i--) {
3466
+ Map.Entry<String, ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> entry =
3467
+ pendingEntries.get(i);
3468
+ stack.push(new EnterSerializedValueFrame(entry.getValue(), entries.get(i).getValue()));
3469
+ }
3470
+ continue;
3471
+ }
3472
+
3473
+ SerializedExpressionBox expressionBox = new SerializedExpressionBox();
3474
+ stack.push(new ExitSerializedValueExpressionFrame(enterFrame.box, expressionBox));
3475
+ stack.push(
3476
+ new EnterSerializedExpressionFrame(
3477
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionValueNode)
3478
+ currentValue)
3479
+ .expression,
3480
+ expressionBox));
3481
+ continue;
3482
+ }
3483
+
3484
+ if (frame instanceof ExitSerializedValueListFrame) {
3485
+ ExitSerializedValueListFrame exitFrame = (ExitSerializedValueListFrame) frame;
3486
+ List<Object> output = new ArrayList<>(exitFrame.childBoxes.size());
3487
+ for (SerializedValueBox childBox : exitFrame.childBoxes) {
3488
+ output.add(childBox.value);
3489
+ }
3490
+ exitFrame.box.value = output;
3491
+ continue;
3492
+ }
3493
+
3494
+ if (frame instanceof ExitSerializedValueMapFrame) {
3495
+ ExitSerializedValueMapFrame exitFrame = (ExitSerializedValueMapFrame) frame;
3496
+ Map<String, Object> output = new LinkedHashMap<>();
3497
+ for (Map.Entry<String, SerializedValueBox> entry : exitFrame.entries) {
3498
+ output.put(entry.getKey(), entry.getValue().value);
3499
+ }
3500
+ exitFrame.box.value = output;
3501
+ continue;
3502
+ }
3503
+
3504
+ if (frame instanceof EnterSerializedExpressionFrame) {
3505
+ EnterSerializedExpressionFrame enterFrame = (EnterSerializedExpressionFrame) frame;
3506
+ ReactNativeFirebaseFirestorePipelineParser.ParsedExpressionNode expression =
3507
+ enterFrame.value;
3508
+
3509
+ if (expression
3510
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedFieldExpressionNode) {
3511
+ Map<String, Object> output = new LinkedHashMap<>();
3512
+ output.put("__kind", "expression");
3513
+ output.put("exprType", "Field");
3514
+ output.put(
3515
+ "path",
3516
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedFieldExpressionNode) expression)
3517
+ .path);
3518
+ enterFrame.box.value = output;
3519
+ continue;
3520
+ }
3521
+
3522
+ if (expression
3523
+ instanceof ReactNativeFirebaseFirestorePipelineParser.ParsedConstantExpressionNode) {
3524
+ SerializedValueBox valueBox = new SerializedValueBox();
3525
+ stack.push(new ExitSerializedExpressionConstantFrame(enterFrame.box, valueBox));
3526
+ stack.push(
3527
+ new EnterSerializedValueFrame(
3528
+ ((ReactNativeFirebaseFirestorePipelineParser.ParsedConstantExpressionNode)
3529
+ expression)
3530
+ .value,
3531
+ valueBox));
3532
+ continue;
3533
+ }
3534
+
3535
+ ReactNativeFirebaseFirestorePipelineParser.ParsedFunctionExpressionNode function =
3536
+ (ReactNativeFirebaseFirestorePipelineParser.ParsedFunctionExpressionNode) expression;
3537
+ List<SerializedValueBox> argBoxes = new ArrayList<>(function.args.size());
3538
+ for (int i = 0; i < function.args.size(); i++) {
3539
+ argBoxes.add(new SerializedValueBox());
3540
+ }
3541
+ stack.push(
3542
+ new ExitSerializedExpressionFunctionFrame(enterFrame.box, function.name, argBoxes));
3543
+ for (int i = function.args.size() - 1; i >= 0; i--) {
3544
+ stack.push(new EnterSerializedValueFrame(function.args.get(i), argBoxes.get(i)));
3545
+ }
3546
+ continue;
3547
+ }
3548
+
3549
+ if (frame instanceof ExitSerializedExpressionFunctionFrame) {
3550
+ ExitSerializedExpressionFunctionFrame exitFrame =
3551
+ (ExitSerializedExpressionFunctionFrame) frame;
3552
+ Map<String, Object> output = new LinkedHashMap<>();
3553
+ output.put("__kind", "expression");
3554
+ output.put("exprType", "Function");
3555
+ output.put("name", exitFrame.name);
3556
+ List<Object> args = new ArrayList<>(exitFrame.argBoxes.size());
3557
+ for (SerializedValueBox argBox : exitFrame.argBoxes) {
3558
+ args.add(argBox.value);
3559
+ }
3560
+ output.put("args", args);
3561
+ exitFrame.box.value = output;
3562
+ continue;
3563
+ }
3564
+
3565
+ if (frame instanceof ExitSerializedExpressionConstantFrame) {
3566
+ ExitSerializedExpressionConstantFrame exitFrame =
3567
+ (ExitSerializedExpressionConstantFrame) frame;
3568
+ Map<String, Object> output = new LinkedHashMap<>();
3569
+ output.put("__kind", "expression");
3570
+ output.put("exprType", "constant");
3571
+ output.put("value", exitFrame.valueBox.value);
3572
+ exitFrame.expressionBox.value = output;
3573
+ continue;
3574
+ }
3575
+
3576
+ ExitSerializedValueExpressionFrame exitFrame = (ExitSerializedValueExpressionFrame) frame;
3577
+ exitFrame.valueBox.value = exitFrame.expressionBox.value;
3578
+ }
3579
+
3580
+ return rootBox.value;
3581
+ }
3582
+
3583
+ private String canonicalizeExpressionFunctionName(String name) {
3584
+ if (name == null) {
3585
+ return "";
3586
+ }
3587
+ return name.toLowerCase(Locale.ROOT).replace("_", "").replace("-", "");
3588
+ }
3589
+
3590
+ private String normalizeExpressionFunctionName(String name) {
3591
+ if (name == null) {
3592
+ return name;
3593
+ }
3594
+
3595
+ switch (canonicalizeExpressionFunctionName(name)) {
3596
+ case "conditional":
3597
+ return "cond";
3598
+ case "logicalmaximum":
3599
+ return "logical_max";
3600
+ case "logicalminimum":
3601
+ return "logical_min";
3602
+ case "lower":
3603
+ case "tolower":
3604
+ return "to_lower";
3605
+ case "upper":
3606
+ case "toupper":
3607
+ return "to_upper";
3608
+ case "stringconcat":
3609
+ return "string_concat";
3610
+ case "timestamptruncate":
3611
+ return "timestamp_trunc";
3612
+ case "timestampsubtract":
3613
+ return "timestamp_sub";
3614
+ default:
3615
+ break;
3616
+ }
3617
+
3618
+ StringBuilder result = new StringBuilder(name.length() + 8);
3619
+ for (int i = 0; i < name.length(); i++) {
3620
+ char c = name.charAt(i);
3621
+ if (Character.isUpperCase(c)) {
3622
+ if (i > 0) {
3623
+ result.append('_');
3624
+ }
3625
+ result.append(Character.toLowerCase(c));
3626
+ } else {
3627
+ result.append(c);
3628
+ }
3629
+ }
3630
+ return result.toString();
3631
+ }
3632
+
3633
+ private Expression constantExpression(Object value)
3634
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3635
+ if (value == null) {
3636
+ return Expression.nullValue();
3637
+ }
3638
+ if (value instanceof String) {
3639
+ return Expression.constant((String) value);
3640
+ }
3641
+ if (value instanceof Number) {
3642
+ return Expression.constant((Number) value);
3643
+ }
3644
+ if (value instanceof Boolean) {
3645
+ return Expression.constant((Boolean) value);
3646
+ }
3647
+ if (value instanceof java.util.Date) {
3648
+ return Expression.constant((java.util.Date) value);
3649
+ }
3650
+ if (value instanceof Timestamp) {
3651
+ return Expression.constant((Timestamp) value);
3652
+ }
3653
+ if (value instanceof com.google.firebase.firestore.GeoPoint) {
3654
+ return Expression.constant((com.google.firebase.firestore.GeoPoint) value);
3655
+ }
3656
+ if (value instanceof com.google.firebase.firestore.Blob) {
3657
+ return Expression.constant((com.google.firebase.firestore.Blob) value);
3658
+ }
3659
+ if (value instanceof DocumentReference) {
3660
+ return Expression.constant((DocumentReference) value);
3661
+ }
3662
+ if (value instanceof com.google.firebase.firestore.VectorValue) {
3663
+ return Expression.constant((com.google.firebase.firestore.VectorValue) value);
3664
+ }
3665
+ if (value instanceof byte[]) {
3666
+ return Expression.constant((byte[]) value);
3667
+ }
3668
+
3669
+ if (value instanceof Map) {
3670
+ return Expression.Companion.toExprOrConstant$com_google_firebase_firebase_firestore(value);
3671
+ }
3672
+
3673
+ return Expression.Companion.toExprOrConstant$com_google_firebase_firebase_firestore(value);
3674
+ }
3675
+
3676
+ private BooleanExpression booleanExpressionFromParsedFunction(
3677
+ String functionName,
3678
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args,
3679
+ String fieldName)
3680
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3681
+ String normalizedName = functionName.toLowerCase(Locale.ROOT);
3682
+
3683
+ if ("and".equals(normalizedName) || "or".equals(normalizedName)) {
3684
+ if (args == null || args.isEmpty()) {
3685
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
3686
+ "pipelineExecute() expected " + fieldName + ".args to contain boolean expressions.");
3687
+ }
3688
+
3689
+ BooleanExpression[] expressions = new BooleanExpression[args.size()];
3690
+ for (int i = 0; i < args.size(); i++) {
3691
+ expressions[i] = coerceBooleanValueNode(args.get(i), fieldName + ".args[" + i + "]");
3692
+ }
3693
+
3694
+ BooleanExpression first = expressions[0];
3695
+ BooleanExpression[] rest = Arrays.copyOfRange(expressions, 1, expressions.length);
3696
+ return "and".equals(normalizedName)
3697
+ ? Expression.and(first, rest)
3698
+ : Expression.or(first, rest);
3699
+ }
3700
+
3701
+ if ("equal".equals(normalizedName)
3702
+ || "notequal".equals(normalizedName)
3703
+ || "greaterthan".equals(normalizedName)
3704
+ || "greaterthanorequal".equals(normalizedName)
3705
+ || "lessthan".equals(normalizedName)
3706
+ || "lessthanorequal".equals(normalizedName)
3707
+ || "arraycontains".equals(normalizedName)
3708
+ || "arraycontainsany".equals(normalizedName)
3709
+ || "arraycontainsall".equals(normalizedName)
3710
+ || "equalany".equals(normalizedName)
3711
+ || "notequalany".equals(normalizedName)) {
3712
+ if (args == null || args.size() < 2) {
3713
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
3714
+ "pipelineExecute() expected "
3715
+ + fieldName
3716
+ + ".args to include left and right operands.");
3717
+ }
3718
+
3719
+ Expression left = coerceExpressionValueNode(args.get(0), fieldName + ".args[0]");
3720
+
3721
+ if ("equal".equals(normalizedName)) {
3722
+ return applyParsedComparison(left::equal, args.get(1), fieldName + ".args[1]");
3723
+ }
3724
+ if ("notequal".equals(normalizedName)) {
3725
+ return applyParsedComparison(left::notEqual, args.get(1), fieldName + ".args[1]");
3726
+ }
3727
+ if ("greaterthan".equals(normalizedName)) {
3728
+ return applyParsedComparison(left::greaterThan, args.get(1), fieldName + ".args[1]");
3729
+ }
3730
+ if ("greaterthanorequal".equals(normalizedName)) {
3731
+ return applyParsedComparison(left::greaterThanOrEqual, args.get(1), fieldName + ".args[1]");
3732
+ }
3733
+ if ("lessthan".equals(normalizedName)) {
3734
+ return applyParsedComparison(left::lessThan, args.get(1), fieldName + ".args[1]");
3735
+ }
3736
+ if ("lessthanorequal".equals(normalizedName)) {
3737
+ return applyParsedComparison(left::lessThanOrEqual, args.get(1), fieldName + ".args[1]");
3738
+ }
3739
+ if ("arraycontains".equals(normalizedName)) {
3740
+ return applyParsedArrayContains(left, args.get(1), fieldName + ".args[1]");
3741
+ }
3742
+ if ("arraycontainsany".equals(normalizedName)) {
3743
+ return applyParsedArrayContainsAny(left, args.get(1), fieldName + ".args[1]");
3744
+ }
3745
+ if ("arraycontainsall".equals(normalizedName)) {
3746
+ return applyParsedArrayContainsAll(left, args.get(1), fieldName + ".args[1]");
3747
+ }
3748
+ if ("equalany".equals(normalizedName)) {
3749
+ if (!containsParsedExpression(args.get(1))) {
3750
+ Object right = resolveValueNode(args.get(1), fieldName + ".args[1]");
3751
+ if (right instanceof List) {
3752
+ return left.equalAny((List<?>) right);
3753
+ }
3754
+ }
3755
+ return left.equalAny(coerceExpressionValueNode(args.get(1), fieldName + ".args[1]"));
3756
+ }
3757
+ if ("notequalany".equals(normalizedName)) {
3758
+ if (!containsParsedExpression(args.get(1))) {
3759
+ Object right = resolveValueNode(args.get(1), fieldName + ".args[1]");
3760
+ if (right instanceof List) {
3761
+ return left.notEqualAny((List<?>) right);
3762
+ }
3763
+ }
3764
+ return left.notEqualAny(coerceExpressionValueNode(args.get(1), fieldName + ".args[1]"));
3765
+ }
3766
+ }
3767
+
3768
+ Expression[] expressions = new Expression[args.size()];
3769
+ for (int i = 0; i < args.size(); i++) {
3770
+ expressions[i] = coerceExpressionValueNode(args.get(i), fieldName + ".args[" + i + "]");
3771
+ }
3772
+ return BooleanExpression.rawFunction(
3773
+ normalizeExpressionFunctionName(functionName), expressions);
3774
+ }
3775
+
3776
+ private BooleanExpression applyParsedComparison(
3777
+ ComparisonFn fn,
3778
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value,
3779
+ String fieldName)
3780
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3781
+ if (containsParsedExpression(value)) {
3782
+ return fn.apply(coerceExpressionValueNode(value, fieldName));
3783
+ }
3784
+ return fn.apply(resolveValueNode(value, fieldName));
3785
+ }
3786
+
3787
+ private BooleanExpression applyParsedArrayContains(
3788
+ Expression expression,
3789
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value,
3790
+ String fieldName)
3791
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3792
+ if (containsParsedExpression(value)) {
3793
+ return expression.arrayContains(coerceExpressionValueNode(value, fieldName + ".value"));
3794
+ }
3795
+ return expression.arrayContains(resolveValueNode(value, fieldName));
3796
+ }
3797
+
3798
+ private BooleanExpression applyParsedArrayContainsAny(
3799
+ Expression expression,
3800
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value,
3801
+ String fieldName)
3802
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3803
+ if (!containsParsedExpression(value)) {
3804
+ Object resolved = resolveValueNode(value, fieldName);
3805
+ if (resolved instanceof List) {
3806
+ return expression.arrayContainsAny((List<?>) resolved);
3807
+ }
3808
+ }
3809
+ return expression.arrayContainsAny(coerceExpressionValueNode(value, fieldName + ".value"));
3810
+ }
3811
+
3812
+ private BooleanExpression applyParsedArrayContainsAll(
3813
+ Expression expression,
3814
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value,
3815
+ String fieldName)
3816
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3817
+ if (!containsParsedExpression(value)) {
3818
+ Object resolved = resolveValueNode(value, fieldName);
3819
+ if (resolved instanceof List) {
3820
+ return expression.arrayContainsAll((List<?>) resolved);
3821
+ }
3822
+ }
3823
+ return expression.arrayContainsAll(coerceExpressionValueNode(value, fieldName + ".value"));
3824
+ }
3825
+
3826
+ private AggregateFunction buildAggregateFunction(
3827
+ ReactNativeFirebaseFirestorePipelineParser.ParsedAggregateNode aggregate, String fieldName)
3828
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3829
+ String normalizedKind = aggregate.kind.toLowerCase(Locale.ROOT);
3830
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode expressionValue =
3831
+ aggregate.primaryValue;
3832
+ if (expressionValue == null && aggregate.args != null && !aggregate.args.isEmpty()) {
3833
+ expressionValue = aggregate.args.get(0);
3834
+ }
3835
+
3836
+ switch (normalizedKind) {
3837
+ case "countall":
3838
+ case "count_all":
3839
+ return AggregateFunction.countAll();
3840
+ case "count":
3841
+ if (expressionValue == null) {
3842
+ return AggregateFunction.countAll();
3843
+ }
3844
+ return aggregateWithParsedValue(
3845
+ expressionValue, AggregateFunction::count, AggregateFunction::count, fieldName);
3846
+ case "countif":
3847
+ case "count_if":
3848
+ if (expressionValue == null) {
3849
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
3850
+ "pipelineExecute() expected " + fieldName + ".expr for countIf aggregate.");
3851
+ }
3852
+ return AggregateFunction.countIf(
3853
+ coerceBooleanValueNode(expressionValue, fieldName + ".expr"));
3854
+ case "sum":
3855
+ return aggregateWithParsedValue(
3856
+ expressionValue, AggregateFunction::sum, AggregateFunction::sum, fieldName);
3857
+ case "avg":
3858
+ case "average":
3859
+ return aggregateWithParsedValue(
3860
+ expressionValue, AggregateFunction::average, AggregateFunction::average, fieldName);
3861
+ case "min":
3862
+ case "minimum":
3863
+ return aggregateWithParsedValue(
3864
+ expressionValue, AggregateFunction::minimum, AggregateFunction::minimum, fieldName);
3865
+ case "max":
3866
+ case "maximum":
3867
+ return aggregateWithParsedValue(
3868
+ expressionValue, AggregateFunction::maximum, AggregateFunction::maximum, fieldName);
3869
+ case "countdistinct":
3870
+ case "count_distinct":
3871
+ return aggregateWithParsedValue(
3872
+ expressionValue,
3873
+ AggregateFunction::countDistinct,
3874
+ AggregateFunction::countDistinct,
3875
+ fieldName);
3876
+ default:
3877
+ List<ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode> args =
3878
+ aggregate.args != null ? aggregate.args : new java.util.ArrayList<>();
3879
+ Expression[] expressions = new Expression[args.size()];
3880
+ for (int i = 0; i < args.size(); i++) {
3881
+ expressions[i] = coerceExpressionValueNode(args.get(i), fieldName + ".args[" + i + "]");
3882
+ }
3883
+ return AggregateFunction.rawAggregate(
3884
+ normalizeExpressionFunctionName(aggregate.kind), expressions);
3885
+ }
3886
+ }
3887
+
3888
+ private AggregateFunction aggregateWithParsedValue(
3889
+ ReactNativeFirebaseFirestorePipelineParser.ParsedValueNode value,
3890
+ AggregateWithString withString,
3891
+ AggregateWithExpression withExpression,
3892
+ String fieldName)
3893
+ throws ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException {
3894
+ if (value == null) {
3895
+ throw new ReactNativeFirebaseFirestorePipelineExecutor.PipelineValidationException(
3896
+ "pipelineExecute() expected " + fieldName + ".expr to be provided.");
3897
+ }
3898
+
3899
+ if (!containsParsedExpression(value)) {
3900
+ Object resolved = resolveValueNode(value, fieldName + ".expr");
3901
+ if (resolved instanceof String) {
3902
+ return withString.apply((String) resolved);
3903
+ }
3904
+ }
3905
+ return withExpression.apply(coerceExpressionValueNode(value, fieldName + ".expr"));
3906
+ }
3907
+
3908
+ private interface ComparisonFn {
3909
+ BooleanExpression apply(Object value);
3910
+ }
3911
+
3912
+ private interface AggregateWithString {
3913
+ AggregateFunction apply(String expression);
3914
+ }
3915
+
3916
+ private interface AggregateWithExpression {
3917
+ AggregateFunction apply(Expression expression);
3918
+ }
3919
+ }