Skip to content

Commit 5e1d84e

Browse files
committed
Fixups
1 parent 4e255dc commit 5e1d84e

File tree

6 files changed

+353
-148
lines changed

6 files changed

+353
-148
lines changed

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/testutil/IntegrationTestUtil.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,8 @@ public static void checkOnlineAndOfflineResultsMatch(Query query, String... expe
568568
*/
569569
public static void checkQueryAndPipelineResultsMatch(Query query, String... expectedDocs) {
570570
QuerySnapshot docsFromQuery = waitFor(query.get(Source.SERVER));
571-
PipelineSnapshot docsFromPipeline = waitFor(query.pipeline().execute());
571+
PipelineSnapshot docsFromPipeline =
572+
waitFor(query.getFirestore().pipeline().createFrom(query).execute());
572573

573574
assertEquals(querySnapshotToIds(docsFromQuery), pipelineSnapshotToIds(docsFromPipeline));
574575
List<String> expected = asList(expectedDocs);

firebase-firestore/src/main/java/com/google/firebase/firestore/FirebaseFirestore.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,11 @@ static void setClientLanguage(@NonNull String languageToken) {
884884
FirestoreChannel.setClientLanguage(languageToken);
885885
}
886886

887+
/**
888+
* Build a new Pipeline
889+
*
890+
* @return {@code PipelineSource} for this Firestore instance.
891+
*/
887892
@NonNull
888893
public PipelineSource pipeline() {
889894
clientProvider.ensureConfigured();

firebase-firestore/src/main/java/com/google/firebase/firestore/Pipeline.kt

Lines changed: 163 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import com.google.android.gms.tasks.Task
1818
import com.google.android.gms.tasks.TaskCompletionSource
1919
import com.google.common.collect.FluentIterable
2020
import com.google.common.collect.ImmutableList
21+
import com.google.firebase.Timestamp
2122
import com.google.firebase.firestore.model.DocumentKey
22-
import com.google.firebase.firestore.model.SnapshotVersion
2323
import com.google.firebase.firestore.model.Values
2424
import com.google.firebase.firestore.pipeline.AddFieldsStage
2525
import com.google.firebase.firestore.pipeline.AggregateStage
@@ -32,7 +32,6 @@ import com.google.firebase.firestore.pipeline.DistinctStage
3232
import com.google.firebase.firestore.pipeline.DocumentsSource
3333
import com.google.firebase.firestore.pipeline.Expr
3434
import com.google.firebase.firestore.pipeline.Field
35-
import com.google.firebase.firestore.pipeline.FindNearestOptions
3635
import com.google.firebase.firestore.pipeline.FindNearestStage
3736
import com.google.firebase.firestore.pipeline.GenericArg
3837
import com.google.firebase.firestore.pipeline.GenericStage
@@ -47,7 +46,6 @@ import com.google.firebase.firestore.pipeline.Selectable
4746
import com.google.firebase.firestore.pipeline.SortStage
4847
import com.google.firebase.firestore.pipeline.Stage
4948
import com.google.firebase.firestore.pipeline.UnionStage
50-
import com.google.firebase.firestore.pipeline.UnnestOptions
5149
import com.google.firebase.firestore.pipeline.UnnestStage
5250
import com.google.firebase.firestore.pipeline.WhereStage
5351
import com.google.firebase.firestore.util.Preconditions
@@ -59,15 +57,15 @@ class Pipeline
5957
internal constructor(
6058
internal val firestore: FirebaseFirestore,
6159
internal val userDataReader: UserDataReader,
62-
private val stages: FluentIterable<Stage>
60+
private val stages: FluentIterable<Stage<*>>
6361
) {
6462
internal constructor(
6563
firestore: FirebaseFirestore,
6664
userDataReader: UserDataReader,
67-
stage: Stage
65+
stage: Stage<*>
6866
) : this(firestore, userDataReader, FluentIterable.of(stage))
6967

70-
private fun append(stage: Stage): Pipeline {
68+
private fun append(stage: Stage<*>): Pipeline {
7169
return Pipeline(firestore, userDataReader, stages.append(stage))
7270
}
7371

@@ -144,15 +142,28 @@ internal constructor(
144142
fun findNearest(
145143
property: Expr,
146144
vector: DoubleArray,
147-
distanceMeasure: FindNearestStage.DistanceMeasure
148-
) = append(FindNearestStage(property, vector, distanceMeasure, FindNearestOptions.DEFAULT))
145+
distanceMeasure: FindNearestStage.DistanceMeasure,
146+
) = append(FindNearestStage.of(property, vector, distanceMeasure))
149147

150148
fun findNearest(
151-
property: Expr,
149+
propertyField: String,
152150
vector: DoubleArray,
153151
distanceMeasure: FindNearestStage.DistanceMeasure,
154-
options: FindNearestOptions
155-
) = append(FindNearestStage(property, vector, distanceMeasure, options))
152+
) = append(FindNearestStage.of(propertyField, vector, distanceMeasure))
153+
154+
fun findNearest(
155+
property: Expr,
156+
vector: Expr,
157+
distanceMeasure: FindNearestStage.DistanceMeasure,
158+
) = append(FindNearestStage.of(property, vector, distanceMeasure))
159+
160+
fun findNearest(
161+
propertyField: String,
162+
vector: Expr,
163+
distanceMeasure: FindNearestStage.DistanceMeasure,
164+
) = append(FindNearestStage.of(propertyField, vector, distanceMeasure))
165+
166+
fun findNearest(stage: FindNearestStage) = append(stage)
156167

157168
fun replace(field: String): Pipeline = replace(Field.of(field))
158169

@@ -165,34 +176,36 @@ internal constructor(
165176

166177
fun union(other: Pipeline): Pipeline = append(UnionStage(other))
167178

168-
fun unnest(field: String, alias: String): Pipeline = unnest(Field.of(field).alias(alias))
169-
170-
fun unnest(field: String, alias: String, options: UnnestOptions): Pipeline =
171-
unnest(Field.of(field).alias(alias), options)
179+
fun unnest(field: String, alias: String): Pipeline = unnest(UnnestStage.withField(field, alias))
172180

173181
fun unnest(selectable: Selectable): Pipeline = append(UnnestStage(selectable))
174182

175-
fun unnest(selectable: Selectable, options: UnnestOptions): Pipeline =
176-
append(UnnestStage(selectable))
183+
fun unnest(stage: UnnestStage): Pipeline = append(stage)
177184

178185
private inner class ObserverSnapshotTask : PipelineResultObserver {
179186
private val userDataWriter =
180187
UserDataWriter(firestore, DocumentSnapshot.ServerTimestampBehavior.DEFAULT)
181188
private val taskCompletionSource = TaskCompletionSource<PipelineSnapshot>()
182189
private val results: ImmutableList.Builder<PipelineResult> = ImmutableList.builder()
183-
override fun onDocument(key: DocumentKey?, data: Map<String, Value>, version: SnapshotVersion) {
190+
override fun onDocument(
191+
key: DocumentKey?,
192+
data: Map<String, Value>,
193+
createTime: Timestamp?,
194+
updateTime: Timestamp?
195+
) {
184196
results.add(
185197
PipelineResult(
186198
firestore,
187199
userDataWriter,
188200
if (key == null) null else DocumentReference(key, firestore),
189201
data,
190-
version
202+
createTime,
203+
updateTime
191204
)
192205
)
193206
}
194207

195-
override fun onComplete(executionTime: SnapshotVersion) {
208+
override fun onComplete(executionTime: Timestamp) {
196209
taskCompletionSource.setResult(PipelineSnapshot(executionTime, results.build()))
197210
}
198211

@@ -205,24 +218,58 @@ internal constructor(
205218
}
206219
}
207220

221+
/** Start of a Firestore Pipeline */
208222
class PipelineSource internal constructor(private val firestore: FirebaseFirestore) {
223+
224+
/**
225+
* Convert the given Query into an equivalent Pipeline.
226+
*
227+
* @param query A Query to be converted into a Pipeline.
228+
* @return Pipeline that is equivalent to [query]
229+
* @throws [IllegalArgumentException] Thrown if the [query] provided targets a different project
230+
* or database than the pipeline.
231+
*/
209232
fun createFrom(query: Query): Pipeline {
210233
if (query.firestore.databaseId != firestore.databaseId) {
211234
throw IllegalArgumentException("Provided query is from a different Firestore instance.")
212235
}
213236
return query.query.toPipeline(firestore, firestore.userDataReader)
214237
}
215238

216-
fun createFrom(query: AggregateQuery): Pipeline =
217-
createFrom(query.query)
239+
/**
240+
* Convert the given Aggregate Query into an equivalent Pipeline.
241+
*
242+
* @param aggregateQuery An Aggregate Query to be converted into a Pipeline.
243+
* @return Pipeline that is equivalent to [aggregateQuery]
244+
* @throws [IllegalArgumentException] Thrown if the [aggregateQuery] provided targets a different
245+
* project or database than the pipeline.
246+
*/
247+
fun createFrom(aggregateQuery: AggregateQuery): Pipeline =
248+
createFrom(aggregateQuery.query)
218249
.aggregate(
219-
*query.aggregateFields.map(AggregateField::toPipeline).toTypedArray<AggregateWithAlias>()
250+
*aggregateQuery.aggregateFields
251+
.map(AggregateField::toPipeline)
252+
.toTypedArray<AggregateWithAlias>()
220253
)
221254

255+
/**
256+
* Set the pipeline's source to the collection specified by the given path.
257+
*
258+
* @param path A path to a collection that will be the source of this pipeline.
259+
* @return Pipeline with documents from target collection.
260+
*/
222261
fun collection(path: String): Pipeline =
223262
// Validate path by converting to CollectionReference
224263
collection(firestore.collection(path))
225264

265+
/**
266+
* Set the pipeline's source to the collection specified by the given CollectionReference.
267+
*
268+
* @param ref A CollectionReference for a collection that will be the source of this pipeline.
269+
* @return Pipeline with documents from target collection.
270+
* @throws [IllegalArgumentException] Thrown if the [ref] provided targets a different project or
271+
* database than the pipeline.
272+
*/
226273
fun collection(ref: CollectionReference): Pipeline {
227274
if (ref.firestore.databaseId != firestore.databaseId) {
228275
throw IllegalArgumentException(
@@ -232,6 +279,11 @@ class PipelineSource internal constructor(private val firestore: FirebaseFiresto
232279
return Pipeline(firestore, firestore.userDataReader, CollectionSource(ref.path))
233280
}
234281

282+
/**
283+
* Set the pipeline's source to the collection group with the given id.
284+
*
285+
* @param collectionid The id of a collection group that will be the source of this pipeline.
286+
*/
235287
fun collectionGroup(collectionId: String): Pipeline {
236288
Preconditions.checkNotNull(collectionId, "Provided collection ID must not be null.")
237289
require(!collectionId.contains("/")) {
@@ -240,12 +292,33 @@ class PipelineSource internal constructor(private val firestore: FirebaseFiresto
240292
return Pipeline(firestore, firestore.userDataReader, CollectionGroupSource(collectionId))
241293
}
242294

295+
/**
296+
* Set the pipeline's source to be all documents in this database.
297+
*
298+
* @return Pipeline with all documents in this database.
299+
*/
243300
fun database(): Pipeline = Pipeline(firestore, firestore.userDataReader, DatabaseSource())
244301

302+
/**
303+
* Set the pipeline's source to the documents specified by the given paths.
304+
*
305+
* @param documents Paths specifying the individual documents that will be the source of this
306+
* pipeline.
307+
* @return Pipeline with [documents].
308+
*/
245309
fun documents(vararg documents: String): Pipeline =
246310
// Validate document path by converting to DocumentReference
247311
documents(*documents.map(firestore::document).toTypedArray())
248312

313+
/**
314+
* Set the pipeline's source to the documents specified by the given DocumentReferences.
315+
*
316+
* @param documents DocumentReferences specifying the individual documents that will be the source
317+
* of this pipeline.
318+
* @return Pipeline with [documents].
319+
* @throws [IllegalArgumentException] Thrown if the [documents] provided targets a different
320+
* project or database than the pipeline.
321+
*/
249322
fun documents(vararg documents: DocumentReference): Pipeline {
250323
val databaseId = firestore.databaseId
251324
for (document in documents) {
@@ -263,29 +336,70 @@ class PipelineSource internal constructor(private val firestore: FirebaseFiresto
263336
}
264337
}
265338

339+
/**
340+
*/
266341
class PipelineSnapshot
267-
internal constructor(
268-
private val executionTime: SnapshotVersion,
269-
val results: List<PipelineResult>
270-
) : Iterable<PipelineResult> {
342+
internal constructor(executionTime: Timestamp, results: List<PipelineResult>) :
343+
Iterable<PipelineResult> {
344+
345+
/** The time at which the pipeline producing this result is executed. */
346+
val executionTime: Timestamp = executionTime
347+
348+
/** List of all the results */
349+
val results: List<PipelineResult> = results
350+
271351
override fun iterator() = results.iterator()
272352
}
273353

274354
class PipelineResult
275355
internal constructor(
276356
private val firestore: FirebaseFirestore,
277357
private val userDataWriter: UserDataWriter,
278-
val ref: DocumentReference?,
358+
ref: DocumentReference?,
279359
private val fields: Map<String, Value>,
280-
private val version: SnapshotVersion,
360+
createTime: Timestamp?,
361+
updateTime: Timestamp?,
281362
) {
282363

364+
/** The time the document was created. Null if this result is not a document. */
365+
val createTime: Timestamp? = createTime
366+
367+
/**
368+
* The time the document was last updated (at the time the snapshot was generated). Null if this
369+
* result is not a document.
370+
*/
371+
val updateTime: Timestamp? = updateTime
372+
373+
/**
374+
* The reference to the document, if the query returns the `__name__` field for a document. The
375+
* name field will be returned by default if querying a document.
376+
*
377+
* The `__name__` field will not be returned if the query projects away this field. For example:
378+
* ```
379+
* // this query does not select the `__name__` field as part of the select stage,
380+
* // so the __name__ field will not be in the output docs from this stage
381+
* db.pipeline().collection("books").select("title", "desc")
382+
* ```
383+
*
384+
* The `__name__` field will not be returned from queries with aggregate or distinct stages.
385+
*
386+
* @return [DocumentReference] Reference to the document, if applicable.
387+
*/
388+
val ref: DocumentReference? = ref
389+
283390
/**
284391
* Returns the ID of the document represented by this result. Returns null if this result is not
285392
* corresponding to a Firestore document.
393+
*
394+
* @return ID of document, if applicable.
286395
*/
287396
fun getId(): String? = ref?.id
288397

398+
/**
399+
* Retrieves all fields in the result as an object map.
400+
*
401+
* @return Map of field names to objects.
402+
*/
289403
fun getData(): Map<String, Any?> = userDataWriter.convertObject(fields)
290404

291405
private fun extractNestedValue(fieldPath: FieldPath): Value? {
@@ -307,15 +421,32 @@ internal constructor(
307421
return value
308422
}
309423

424+
/**
425+
* Retrieves the field specified by [field].
426+
*
427+
* @param field The field path (e.g. "foo" or "foo.bar") to a specific field.
428+
* @return The data at the specified field location or null if no such field exists.
429+
*/
310430
fun get(field: String): Any? = get(FieldPath.fromDotSeparatedPath(field))
311431

432+
/**
433+
* Retrieves the field specified by [fieldPath].
434+
*
435+
* @param fieldPath The field path to a specific field.
436+
* @return The data at the specified field location or null if no such field exists.
437+
*/
312438
fun get(fieldPath: FieldPath): Any? = userDataWriter.convertValue(extractNestedValue(fieldPath))
313439

314-
override fun toString() = "PipelineResult{ref=$ref, version=$version}, data=${getData()}"
440+
override fun toString() = "PipelineResult{ref=$ref, updateTime=$updateTime}, data=${getData()}"
315441
}
316442

317443
internal interface PipelineResultObserver {
318-
fun onDocument(key: DocumentKey?, data: Map<String, Value>, version: SnapshotVersion)
319-
fun onComplete(executionTime: SnapshotVersion)
444+
fun onDocument(
445+
key: DocumentKey?,
446+
data: Map<String, Value>,
447+
createTime: Timestamp?,
448+
updateTime: Timestamp?
449+
)
450+
fun onComplete(executionTime: Timestamp)
320451
fun onError(exception: FirebaseFirestoreException)
321452
}

0 commit comments

Comments
 (0)