@@ -32,15 +32,18 @@ import com.google.firebase.firestore.pipeline.DatabaseSource
32
32
import com.google.firebase.firestore.pipeline.DistinctStage
33
33
import com.google.firebase.firestore.pipeline.DocumentsSource
34
34
import com.google.firebase.firestore.pipeline.Expr
35
+ import com.google.firebase.firestore.pipeline.Expr.Companion.field
35
36
import com.google.firebase.firestore.pipeline.ExprWithAlias
36
37
import com.google.firebase.firestore.pipeline.Field
37
38
import com.google.firebase.firestore.pipeline.FindNearestStage
38
39
import com.google.firebase.firestore.pipeline.FunctionExpr
39
40
import com.google.firebase.firestore.pipeline.GenericStage
41
+ import com.google.firebase.firestore.pipeline.InternalOptions
40
42
import com.google.firebase.firestore.pipeline.LimitStage
41
43
import com.google.firebase.firestore.pipeline.OffsetStage
42
44
import com.google.firebase.firestore.pipeline.Ordering
43
45
import com.google.firebase.firestore.pipeline.PipelineOptions
46
+ import com.google.firebase.firestore.pipeline.RealtimePipelineOptions
44
47
import com.google.firebase.firestore.pipeline.RemoveFieldsStage
45
48
import com.google.firebase.firestore.pipeline.ReplaceStage
46
49
import com.google.firebase.firestore.pipeline.SampleStage
@@ -55,12 +58,81 @@ import com.google.firestore.v1.ExecutePipelineRequest
55
58
import com.google.firestore.v1.StructuredPipeline
56
59
import com.google.firestore.v1.Value
57
60
58
- class Pipeline
61
+ open class AbstractPipeline
59
62
internal constructor (
60
63
internal val firestore: FirebaseFirestore ,
61
64
internal val userDataReader: UserDataReader ,
62
- private val stages: FluentIterable <Stage <* >>
65
+ internal val stages: FluentIterable <Stage <* >>
63
66
) {
67
+ private fun toStructuredPipelineProto (options : InternalOptions ? ): StructuredPipeline {
68
+ val builder = StructuredPipeline .newBuilder()
69
+ builder.pipeline = toPipelineProto()
70
+ options?.forEach(builder::putOptions)
71
+ return builder.build()
72
+ }
73
+
74
+ internal fun toPipelineProto (): com.google.firestore.v1.Pipeline =
75
+ com.google.firestore.v1.Pipeline .newBuilder()
76
+ .addAllStages(stages.map { it.toProtoStage(userDataReader) })
77
+ .build()
78
+
79
+ private fun toExecutePipelineRequest (options : InternalOptions ? ): ExecutePipelineRequest {
80
+ val database = firestore.databaseId
81
+ val builder = ExecutePipelineRequest .newBuilder()
82
+ builder.database = " projects/${database.projectId} /databases/${database.databaseId} "
83
+ builder.structuredPipeline = toStructuredPipelineProto(options)
84
+ return builder.build()
85
+ }
86
+
87
+ protected fun execute (options : InternalOptions ? ): Task <PipelineSnapshot > {
88
+ val request = toExecutePipelineRequest(options)
89
+ val observerTask = ObserverSnapshotTask ()
90
+ firestore.callClient { call -> call!! .executePipeline(request, observerTask) }
91
+ return observerTask.task
92
+ }
93
+
94
+ private inner class ObserverSnapshotTask : PipelineResultObserver {
95
+ private val userDataWriter =
96
+ UserDataWriter (firestore, DocumentSnapshot .ServerTimestampBehavior .DEFAULT )
97
+ private val taskCompletionSource = TaskCompletionSource <PipelineSnapshot >()
98
+ private val results: ImmutableList .Builder <PipelineResult > = ImmutableList .builder()
99
+ override fun onDocument (
100
+ key : DocumentKey ? ,
101
+ data : Map <String , Value >,
102
+ createTime : Timestamp ? ,
103
+ updateTime : Timestamp ?
104
+ ) {
105
+ results.add(
106
+ PipelineResult (
107
+ firestore,
108
+ userDataWriter,
109
+ if (key == null ) null else DocumentReference (key, firestore),
110
+ data,
111
+ createTime,
112
+ updateTime
113
+ )
114
+ )
115
+ }
116
+
117
+ override fun onComplete (executionTime : Timestamp ) {
118
+ taskCompletionSource.setResult(PipelineSnapshot (executionTime, results.build()))
119
+ }
120
+
121
+ override fun onError (exception : FirebaseFirestoreException ) {
122
+ taskCompletionSource.setException(exception)
123
+ }
124
+
125
+ val task: Task <PipelineSnapshot >
126
+ get() = taskCompletionSource.task
127
+ }
128
+ }
129
+
130
+ class Pipeline
131
+ private constructor (
132
+ firestore: FirebaseFirestore ,
133
+ userDataReader: UserDataReader ,
134
+ stages: FluentIterable <Stage <* >>
135
+ ) : AbstractPipeline (firestore, userDataReader, stages) {
64
136
internal constructor (
65
137
firestore: FirebaseFirestore ,
66
138
userDataReader: UserDataReader ,
@@ -71,37 +143,14 @@ internal constructor(
71
143
return Pipeline (firestore, userDataReader, stages.append(stage))
72
144
}
73
145
74
- fun execute (): Task <PipelineSnapshot > = execute(PipelineOptions . DEFAULT )
146
+ fun execute (): Task <PipelineSnapshot > = execute(null )
75
147
76
- fun execute (options : PipelineOptions ): Task <PipelineSnapshot > {
77
- val observerTask = ObserverSnapshotTask ()
78
- firestore.callClient { call -> call!! .executePipeline(toProto(options), observerTask) }
79
- return observerTask.task
80
- }
148
+ fun execute (options : RealtimePipelineOptions ): Task <PipelineSnapshot > = execute(options.options)
81
149
82
150
internal fun documentReference (key : DocumentKey ): DocumentReference {
83
151
return DocumentReference (key, firestore)
84
152
}
85
153
86
- private fun toProto (options : PipelineOptions ): ExecutePipelineRequest {
87
- val database = firestore.databaseId
88
- val builder = ExecutePipelineRequest .newBuilder()
89
- builder.database = " projects/${database.projectId} /databases/${database.databaseId} "
90
- builder.structuredPipeline = toStructuredPipelineProto()
91
- return builder.build()
92
- }
93
-
94
- private fun toStructuredPipelineProto (): StructuredPipeline {
95
- val builder = StructuredPipeline .newBuilder()
96
- builder.pipeline = toPipelineProto()
97
- return builder.build()
98
- }
99
-
100
- internal fun toPipelineProto (): com.google.firestore.v1.Pipeline =
101
- com.google.firestore.v1.Pipeline .newBuilder()
102
- .addAllStages(stages.map { it.toProtoStage(userDataReader) })
103
- .build()
104
-
105
154
/* *
106
155
* Adds a stage to the pipeline by specifying the stage name as an argument. This does not offer
107
156
* any type safety on the stage params and requires the caller to know the order (and optionally
@@ -153,9 +202,7 @@ internal constructor(
153
202
*/
154
203
fun removeFields (field : String , vararg additionalFields : String ): Pipeline =
155
204
append(
156
- RemoveFieldsStage (
157
- arrayOf(Expr .field(field), * additionalFields.map(Expr ::field).toTypedArray())
158
- )
205
+ RemoveFieldsStage (arrayOf(field(field), * additionalFields.map(Expr ::field).toTypedArray()))
159
206
)
160
207
161
208
/* *
@@ -178,11 +225,7 @@ internal constructor(
178
225
* @return A new [Pipeline] object with this stage appended to the stage list.
179
226
*/
180
227
fun select (selection : Selectable , vararg additionalSelections : Any ): Pipeline =
181
- append(
182
- SelectStage (
183
- arrayOf(selection, * additionalSelections.map(Selectable ::toSelectable).toTypedArray())
184
- )
185
- )
228
+ append(SelectStage .of(selection, * additionalSelections))
186
229
187
230
/* *
188
231
* Selects or creates a set of fields from the outputs of previous stages.
@@ -204,14 +247,7 @@ internal constructor(
204
247
* @return A new [Pipeline] object with this stage appended to the stage list.
205
248
*/
206
249
fun select (fieldName : String , vararg additionalSelections : Any ): Pipeline =
207
- append(
208
- SelectStage (
209
- arrayOf(
210
- Expr .field(fieldName),
211
- * additionalSelections.map(Selectable ::toSelectable).toTypedArray()
212
- )
213
- )
214
- )
250
+ append(SelectStage .of(fieldName, * additionalSelections))
215
251
216
252
/* *
217
253
* Sorts the documents from previous stages based on one or more [Ordering] criteria.
@@ -320,10 +356,7 @@ internal constructor(
320
356
fun distinct (groupField : String , vararg additionalGroups : Any ): Pipeline =
321
357
append(
322
358
DistinctStage (
323
- arrayOf(
324
- Expr .field(groupField),
325
- * additionalGroups.map(Selectable ::toSelectable).toTypedArray()
326
- )
359
+ arrayOf(field(groupField), * additionalGroups.map(Selectable ::toSelectable).toTypedArray())
327
360
)
328
361
)
329
362
@@ -453,7 +486,7 @@ internal constructor(
453
486
* @param field The [String] specifying the field name containing the nested map.
454
487
* @return A new [Pipeline] object with this stage appended to the stage list.
455
488
*/
456
- fun replace (field : String ): Pipeline = replace(Expr . field(field))
489
+ fun replace (field : String ): Pipeline = replace(field(field))
457
490
458
491
/* *
459
492
* Fully overwrites all fields in a document with those coming from a nested map.
@@ -514,8 +547,7 @@ internal constructor(
514
547
* @param alias The name of field to store emitted element of array.
515
548
* @return A new [Pipeline] object with this stage appended to the stage list.
516
549
*/
517
- fun unnest (arrayField : String , alias : String ): Pipeline =
518
- unnest(Expr .field(arrayField).alias(alias))
550
+ fun unnest (arrayField : String , alias : String ): Pipeline = unnest(field(arrayField).alias(alias))
519
551
520
552
/* *
521
553
* Takes a specified array from the input documents and outputs a document for each element with
@@ -550,41 +582,6 @@ internal constructor(
550
582
* @return A new [Pipeline] object with this stage appended to the stage list.
551
583
*/
552
584
fun unnest (unnestStage : UnnestStage ): Pipeline = append(unnestStage)
553
-
554
- private inner class ObserverSnapshotTask : PipelineResultObserver {
555
- private val userDataWriter =
556
- UserDataWriter (firestore, DocumentSnapshot .ServerTimestampBehavior .DEFAULT )
557
- private val taskCompletionSource = TaskCompletionSource <PipelineSnapshot >()
558
- private val results: ImmutableList .Builder <PipelineResult > = ImmutableList .builder()
559
- override fun onDocument (
560
- key : DocumentKey ? ,
561
- data : Map <String , Value >,
562
- createTime : Timestamp ? ,
563
- updateTime : Timestamp ?
564
- ) {
565
- results.add(
566
- PipelineResult (
567
- firestore,
568
- userDataWriter,
569
- if (key == null ) null else DocumentReference (key, firestore),
570
- data,
571
- createTime,
572
- updateTime
573
- )
574
- )
575
- }
576
-
577
- override fun onComplete (executionTime : Timestamp ) {
578
- taskCompletionSource.setResult(PipelineSnapshot (executionTime, results.build()))
579
- }
580
-
581
- override fun onError (exception : FirebaseFirestoreException ) {
582
- taskCompletionSource.setException(exception)
583
- }
584
-
585
- val task: Task <PipelineSnapshot >
586
- get() = taskCompletionSource.task
587
- }
588
585
}
589
586
590
587
/* * Start of a Firestore Pipeline */
@@ -644,7 +641,7 @@ class PipelineSource internal constructor(private val firestore: FirebaseFiresto
644
641
* Set the pipeline's source to the collection specified by CollectionSource.
645
642
*
646
643
* @param stage A [CollectionSource] that will be the source of this pipeline.
647
- * @return Pipeline with documents from target collection.
644
+ * @return A new [ Pipeline] object with documents from target collection.
648
645
* @throws [IllegalArgumentException] Thrown if the [stage] provided targets a different project
649
646
* or database than the pipeline.
650
647
*/
@@ -659,6 +656,7 @@ class PipelineSource internal constructor(private val firestore: FirebaseFiresto
659
656
* Set the pipeline's source to the collection group with the given id.
660
657
*
661
658
* @param collectionId The id of a collection group that will be the source of this pipeline.
659
+ * @return A new [Pipeline] object with documents from target collection group.
662
660
*/
663
661
fun collectionGroup (collectionId : String ): Pipeline =
664
662
pipeline(CollectionGroupSource .of((collectionId)))
@@ -710,6 +708,87 @@ class PipelineSource internal constructor(private val firestore: FirebaseFiresto
710
708
}
711
709
}
712
710
711
+ class RealtimePipelineSource internal constructor(private val firestore : FirebaseFirestore ) {
712
+
713
+ /* *
714
+ * Set the pipeline's source to the collection specified by the given path.
715
+ *
716
+ * @param path A path to a collection that will be the source of this pipeline.
717
+ * @return A new [RealtimePipeline] object with documents from target collection.
718
+ */
719
+ fun collection (path : String ): RealtimePipeline = collection(CollectionSource .of(path))
720
+
721
+ /* *
722
+ * Set the pipeline's source to the collection specified by the given [CollectionReference].
723
+ *
724
+ * @param ref A [CollectionReference] for a collection that will be the source of this pipeline.
725
+ * @return A new [RealtimePipeline] object with documents from target collection.
726
+ * @throws [IllegalArgumentException] Thrown if the [ref] provided targets a different project or
727
+ * database than the pipeline.
728
+ */
729
+ fun collection (ref : CollectionReference ): RealtimePipeline = collection(CollectionSource .of(ref))
730
+
731
+ /* *
732
+ * Set the pipeline's source to the collection specified by CollectionSource.
733
+ *
734
+ * @param stage A [CollectionSource] that will be the source of this pipeline.
735
+ * @return A new [RealtimePipeline] object with documents from target collection.
736
+ * @throws [IllegalArgumentException] Thrown if the [stage] provided targets a different project
737
+ * or database than the pipeline.
738
+ */
739
+ fun collection (stage : CollectionSource ): RealtimePipeline {
740
+ if (stage.firestore != null && stage.firestore.databaseId != firestore.databaseId) {
741
+ throw IllegalArgumentException (" Provided collection is from a different Firestore instance." )
742
+ }
743
+ return RealtimePipeline (firestore, firestore.userDataReader, stage)
744
+ }
745
+
746
+ /* *
747
+ * Set the pipeline's source to the collection group with the given id.
748
+ *
749
+ * @param collectionId The id of a collection group that will be the source of this pipeline.
750
+ * @return A new [RealtimePipeline] object with documents from target collection group.
751
+ */
752
+ fun collectionGroup (collectionId : String ): RealtimePipeline =
753
+ pipeline(CollectionGroupSource .of((collectionId)))
754
+
755
+ fun pipeline (stage : CollectionGroupSource ): RealtimePipeline =
756
+ RealtimePipeline (firestore, firestore.userDataReader, stage)
757
+ }
758
+
759
+ class RealtimePipeline
760
+ internal constructor (
761
+ firestore: FirebaseFirestore ,
762
+ userDataReader: UserDataReader ,
763
+ stages: FluentIterable <Stage <* >>
764
+ ) : AbstractPipeline (firestore, userDataReader, stages) {
765
+ internal constructor (
766
+ firestore: FirebaseFirestore ,
767
+ userDataReader: UserDataReader ,
768
+ stage: Stage <* >
769
+ ) : this (firestore, userDataReader, FluentIterable .of(stage))
770
+
771
+ private fun append (stage : Stage <* >): RealtimePipeline {
772
+ return RealtimePipeline (firestore, userDataReader, stages.append(stage))
773
+ }
774
+
775
+ fun execute (): Task <PipelineSnapshot > = execute(null )
776
+
777
+ fun execute (options : PipelineOptions ): Task <PipelineSnapshot > = execute(options.options)
778
+
779
+ fun limit (limit : Int ): RealtimePipeline = append(LimitStage (limit))
780
+
781
+ fun offset (offset : Int ): RealtimePipeline = append(OffsetStage (offset))
782
+
783
+ fun select (selection : Selectable , vararg additionalSelections : Any ): RealtimePipeline =
784
+ append(SelectStage .of(selection, * additionalSelections))
785
+
786
+ fun select (fieldName : String , vararg additionalSelections : Any ): RealtimePipeline =
787
+ append(SelectStage .of(fieldName, * additionalSelections))
788
+
789
+ fun where (condition : BooleanExpr ): RealtimePipeline = append(WhereStage (condition))
790
+ }
791
+
713
792
/* *
714
793
*/
715
794
class PipelineSnapshot
0 commit comments