15
15
* limitations under the License.
16
16
*/
17
17
18
- import { Query } from '../api' ;
19
- import { firestoreClientRunCountQuery } from '../core/firestore_client' ;
20
- import {
21
- AggregateField ,
22
- AggregateQuerySnapshot
23
- } from '../lite-api/aggregate_types' ;
18
+ import { AggregateField , AggregateSpec , Query } from '../api' ;
19
+ import { AggregateImpl } from '../core/aggregate' ;
20
+ import { firestoreClientRunAggregateQuery } from '../core/firestore_client' ;
21
+ import { count } from '../lite-api/aggregate' ;
22
+ import { AggregateQuerySnapshot } from '../lite-api/aggregate_types' ;
23
+ import { AggregateAlias } from '../model/aggregate_alias' ;
24
+ import { ObjectValue } from '../model/object_value' ;
24
25
import { cast } from '../util/input_validation' ;
26
+ import { mapToArray } from '../util/obj' ;
25
27
26
28
import { ensureFirestoreConfigured , Firestore } from './database' ;
27
29
import { ExpUserDataWriter } from './reference_impl' ;
28
30
29
- export { aggregateQuerySnapshotEqual } from '../lite-api/aggregate' ;
31
+ export {
32
+ aggregateQuerySnapshotEqual ,
33
+ count ,
34
+ sum ,
35
+ average ,
36
+ aggregateFieldEqual
37
+ } from '../lite-api/aggregate' ;
30
38
31
39
/**
32
40
* Calculates the number of documents in the result set of the given query,
@@ -52,8 +60,89 @@ export { aggregateQuerySnapshotEqual } from '../lite-api/aggregate';
52
60
export function getCountFromServer (
53
61
query : Query < unknown >
54
62
) : Promise < AggregateQuerySnapshot < { count : AggregateField < number > } > > {
63
+ const countQuerySpec : { count : AggregateField < number > } = {
64
+ count : count ( )
65
+ } ;
66
+
67
+ return getAggregateFromServer ( query , countQuerySpec ) ;
68
+ }
69
+
70
+ /**
71
+ * Calculates the specified aggregations over the documents in the result
72
+ * set of the given query, without actually downloading the documents.
73
+ *
74
+ * Using this function to perform aggregations is efficient because only the
75
+ * final aggregation values, not the documents' data, is downloaded. This
76
+ * function can even perform aggregations of the documents if the result set
77
+ * would be prohibitively large to download entirely (e.g. thousands of documents).
78
+ *
79
+ * The result received from the server is presented, unaltered, without
80
+ * considering any local state. That is, documents in the local cache are not
81
+ * taken into consideration, neither are local modifications not yet
82
+ * synchronized with the server. Previously-downloaded results, if any, are not
83
+ * used: every request using this source necessarily involves a round trip to
84
+ * the server.
85
+ *
86
+ * @param query The query whose result set to aggregate over.
87
+ * @param aggregateSpec An `AggregateSpec` object that specifies the aggregates
88
+ * to perform over the result set. The AggregateSpec specifies aliases for each
89
+ * aggregate, which can be used to retrieve the aggregate result.
90
+ * @example
91
+ * ```typescript
92
+ * const aggregateSnapshot = await getAggregateFromServer(query, {
93
+ * countOfDocs: count(),
94
+ * totalHours: sum('hours'),
95
+ * averageScore: average('score')
96
+ * });
97
+ *
98
+ * const countOfDocs: number = aggregateSnapshot.data().countOfDocs;
99
+ * const totalHours: number = aggregateSnapshot.data().totalHours;
100
+ * const averageScore: number | null = aggregateSnapshot.data().averageScore;
101
+ * ```
102
+ * @internal TODO (sum/avg) remove when public
103
+ */
104
+ export function getAggregateFromServer < T extends AggregateSpec > (
105
+ query : Query < unknown > ,
106
+ aggregateSpec : T
107
+ ) : Promise < AggregateQuerySnapshot < T > > {
55
108
const firestore = cast ( query . firestore , Firestore ) ;
56
109
const client = ensureFirestoreConfigured ( firestore ) ;
110
+
111
+ const internalAggregates = mapToArray ( aggregateSpec , ( aggregate , alias ) => {
112
+ return new AggregateImpl (
113
+ new AggregateAlias ( alias ) ,
114
+ aggregate . _aggregateType ,
115
+ aggregate . _internalFieldPath
116
+ ) ;
117
+ } ) ;
118
+
119
+ // Run the aggregation and convert the results
120
+ return firestoreClientRunAggregateQuery (
121
+ client ,
122
+ query . _query ,
123
+ internalAggregates
124
+ ) . then ( aggregateResult =>
125
+ convertToAggregateQuerySnapshot ( firestore , query , aggregateResult )
126
+ ) ;
127
+ }
128
+
129
+ /**
130
+ * Converts the core aggregration result to an `AggregateQuerySnapshot`
131
+ * that can be returned to the consumer.
132
+ * @param query
133
+ * @param aggregateResult Core aggregation result
134
+ * @internal
135
+ */
136
+ function convertToAggregateQuerySnapshot < T extends AggregateSpec > (
137
+ firestore : Firestore ,
138
+ query : Query < unknown > ,
139
+ aggregateResult : ObjectValue
140
+ ) : AggregateQuerySnapshot < T > {
57
141
const userDataWriter = new ExpUserDataWriter ( firestore ) ;
58
- return firestoreClientRunCountQuery ( client , query , userDataWriter ) ;
142
+ const querySnapshot = new AggregateQuerySnapshot < T > (
143
+ query ,
144
+ userDataWriter ,
145
+ aggregateResult
146
+ ) ;
147
+ return querySnapshot ;
59
148
}
0 commit comments