Skip to content

Commit 375df71

Browse files
committed
add snippets for joins and explain
1 parent 4a6ce82 commit 375df71

1 file changed

Lines changed: 346 additions & 0 deletions

File tree

snippets/firestore/pipeline.py

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Pipeline snippets for Cloud Firestore."""
16+
17+
# pylint: disable=invalid-name
18+
19+
20+
def load_test_data(db):
21+
# [START pipeline_join_test_data]
22+
# Load set of cities.
23+
cities = db.collection("cities")
24+
25+
cities.document("SF").set(
26+
{
27+
"name": "San Francisco",
28+
"state": "CA",
29+
"country": "USA",
30+
}
31+
)
32+
cities.document("LA").set(
33+
{
34+
"name": "Los Angeles",
35+
"state": "CA",
36+
"country": "USA",
37+
}
38+
)
39+
cities.document("DC").set(
40+
{
41+
"name": "Washington, D.C.",
42+
"state": None,
43+
"country": "USA",
44+
}
45+
)
46+
cities.document("TOK").set(
47+
{
48+
"name": "Tokyo",
49+
"state": None,
50+
"country": "Japan",
51+
}
52+
)
53+
54+
# Load restaurants in various cities.
55+
sf_restaurants = db.collection("cities").document("SF").collection("restaurants")
56+
la_restaurants = db.collection("cities").document("LA").collection("restaurants")
57+
dc_restaurants = db.collection("cities").document("DC").collection("restaurants")
58+
59+
rest1 = sf_restaurants.add(
60+
{
61+
"name": "Golden Gate Pizza",
62+
"type": "pizza",
63+
"owner_id": "Mario Rossi",
64+
}
65+
)[1]
66+
rest2 = sf_restaurants.add(
67+
{
68+
"name": "Bay Area Burger",
69+
"type": "burger",
70+
"owner_id": "Sarah Jenkins",
71+
}
72+
)[1]
73+
rest3 = sf_restaurants.add(
74+
{
75+
"name": "Sunset Taco",
76+
"type": "mexican",
77+
"owner_id": "Edward",
78+
}
79+
)[1]
80+
81+
rest4 = la_restaurants.add(
82+
{
83+
"name": "Hollywood Sushi",
84+
"type": "sushi",
85+
"owner_id": "Ken Kenji",
86+
}
87+
)[1]
88+
rest5 = la_restaurants.add(
89+
{
90+
"name": "Venice Pizza",
91+
"type": "pizza",
92+
"owner_id": "Luigi Romano",
93+
}
94+
)[1]
95+
96+
rest6 = dc_restaurants.add(
97+
{
98+
"name": "Capitol Tacos",
99+
"type": "mexican",
100+
"owner_id": "Maria Garcia",
101+
}
102+
)[1]
103+
rest7 = dc_restaurants.add(
104+
{
105+
"name": "Georgetown Coffee",
106+
"type": "cafe",
107+
"owner_id": "David Kim",
108+
}
109+
)[1]
110+
111+
# Load collection of reviews.
112+
reviews = db.collection("reviews")
113+
114+
reviews.add({"restaurant": rest1, "rating": 5, "reviewer_id": "Alice"})
115+
reviews.add({"restaurant": rest1, "rating": 4, "reviewer_id": "Bob"})
116+
reviews.add({"restaurant": rest2, "rating": 4, "reviewer_id": "Charlie"})
117+
reviews.add({"restaurant": rest3, "rating": 5, "reviewer_id": "Diana"})
118+
reviews.add({"restaurant": rest3, "rating": 4, "reviewer_id": "Edward"})
119+
reviews.add({"restaurant": rest3, "rating": 4, "reviewer_id": "Fiona"})
120+
# rest4 has 0 reviews
121+
reviews.add({"restaurant": rest5, "rating": 3, "reviewer_id": "George"})
122+
reviews.add({"restaurant": rest6, "rating": 5, "reviewer_id": "Hannah"})
123+
reviews.add({"restaurant": rest6, "rating": 4, "reviewer_id": "Ian"})
124+
reviews.add({"restaurant": rest7, "rating": 5, "reviewer_id": "Julia"})
125+
# [END pipeline_join_test_data]
126+
127+
128+
def pipeline_join_lookup(db):
129+
from google.cloud.firestore_v1.pipeline_expressions import Field, Variable
130+
131+
# [START pipeline_join_lookup]
132+
results = (
133+
db.pipeline()
134+
.collection_group("reviews")
135+
.define(Field.of("restaurant").as_("restaurant_name"))
136+
.add_fields(
137+
db.pipeline()
138+
.collection_group("restaurants")
139+
.where(Field.of("__name__").equal(Variable("restaurant_name")))
140+
.select("name", "type")
141+
.to_scalar_expression()
142+
.as_("restaurant")
143+
)
144+
.execute()
145+
)
146+
# [END pipeline_join_lookup]
147+
return results
148+
149+
150+
def pipeline_join_array(db):
151+
from google.cloud.firestore_v1.pipeline_expressions import Field, Variable
152+
153+
# [START pipeline_join_array]
154+
results = (
155+
db.pipeline()
156+
.collection_group("restaurants")
157+
.where(Field.of("type").equal("pizza"))
158+
.define(Field.of("__name__").as_("restaurant_name"))
159+
.select(
160+
Field.of("name"),
161+
db.pipeline()
162+
.collection_group("reviews")
163+
.where(Field.of("restaurant").equal(Variable("restaurant_name")))
164+
.select("rating", "reviewer_id")
165+
.to_array_expression()
166+
.as_("reviews"),
167+
)
168+
.execute()
169+
)
170+
# [END pipeline_join_array]
171+
return results
172+
173+
174+
def pipeline_join_aggregate(db):
175+
from google.cloud.firestore_v1.pipeline_expressions import Field, Variable
176+
177+
# [START pipeline_join_aggregate]
178+
results = (
179+
db.pipeline()
180+
.collection_group("restaurants")
181+
.where(Field.of("type").equal("pizza"))
182+
.define(Field.of("__name__").as_("restaurant_name"))
183+
.select(
184+
Field.of("name"),
185+
db.pipeline()
186+
.collection_group("reviews")
187+
.where(Field.of("restaurant").equal(Variable("restaurant_name")))
188+
.aggregate(Field.of("rating").average().as_("avg_rating"))
189+
.to_scalar_expression()
190+
.as_("avg_rating"),
191+
)
192+
.execute()
193+
)
194+
# [END pipeline_join_aggregate]
195+
return results
196+
197+
198+
def pipeline_join_limit(db):
199+
from google.cloud.firestore_v1.pipeline_expressions import Field, Variable
200+
201+
# [START pipeline_join_limit]
202+
results = (
203+
db.pipeline()
204+
.collection_group("restaurants")
205+
.define(Field.of("__name__").as_("restaurant_name"))
206+
.select(
207+
Field.of("name"),
208+
db.pipeline()
209+
.collection_group("reviews")
210+
.where(Field.of("restaurant").equal(Variable("restaurant_name")))
211+
.sort(Field.of("rating").descending())
212+
.limit(2)
213+
.select("rating", "reviewer_id")
214+
.to_array_expression()
215+
.as_("top_reviews"),
216+
)
217+
.execute()
218+
)
219+
# [END pipeline_join_limit]
220+
return results
221+
222+
223+
def pipeline_join_subcollection(db):
224+
from google.cloud.firestore_v1.pipeline_source import PipelineSource
225+
226+
# [START pipeline_join_subcollection]
227+
results = (
228+
db.pipeline()
229+
.collection("cities")
230+
.add_fields(
231+
PipelineSource.subcollection("restaurants")
232+
.to_array_expression()
233+
.length()
234+
.as_("restaurant_count")
235+
)
236+
.execute()
237+
)
238+
# [END pipeline_join_subcollection]
239+
return results
240+
241+
242+
def pipeline_join_multi_field(db):
243+
from google.cloud.firestore_v1.pipeline_expressions import Count, Field, Variable
244+
245+
# [START pipeline_join_multi_field]
246+
results = (
247+
db.pipeline()
248+
.collection_group("restaurants")
249+
.define(
250+
Field.of("owner_id").as_("owner_id"),
251+
Field.of("__name__").as_("__name__"),
252+
)
253+
.where(
254+
db.pipeline()
255+
.collection_group("reviews")
256+
.where(Field.of("restaurant").equal(Variable("__name__")))
257+
.where(Field.of("author").equal(Variable("owner_id")))
258+
.aggregate(Count().as_("c"))
259+
.to_scalar_expression()
260+
.greater_than(0)
261+
)
262+
.execute()
263+
)
264+
# [END pipeline_join_multi_field]
265+
return results
266+
267+
268+
def pipeline_join_anti(db):
269+
from google.cloud.firestore_v1.pipeline_expressions import Count, Field, Variable
270+
271+
# [START pipeline_join_anti]
272+
results = (
273+
db.pipeline()
274+
.collection_group("restaurants")
275+
.define(Field.of("__name__").as_("restaurant_name"))
276+
.where(
277+
db.pipeline()
278+
.collection_group("reviews")
279+
.where(Field.of("restaurant").equal(Variable("restaurant_name")))
280+
.aggregate(Count().as_("review_count"))
281+
.to_scalar_expression()
282+
.equal(0)
283+
)
284+
.execute()
285+
)
286+
# [END pipeline_join_anti]
287+
return results
288+
289+
290+
def pipeline_join_unnest(db):
291+
from google.cloud.firestore_v1.pipeline_expressions import Field, Variable
292+
293+
# [START pipeline_join_unnest]
294+
results = (
295+
db.pipeline()
296+
.collection_group("restaurants")
297+
.where(Field.of("type").equal("pizza"))
298+
.define(Field.of("__name__").as_("restaurant_name"))
299+
.unnest(
300+
db.pipeline()
301+
.collection_group("reviews")
302+
.where(Field.of("restaurant").equal(Variable("restaurant_name")))
303+
.select("rating", "reviewer_id")
304+
.to_array_expression(),
305+
alias="review",
306+
)
307+
.execute()
308+
)
309+
# [END pipeline_join_unnest]
310+
return results
311+
312+
313+
def pipeline_join_uncorrelated(db):
314+
from google.cloud.firestore_v1.pipeline_expressions import Field
315+
316+
# [START pipeline_join_uncorrelated]
317+
results = (
318+
db.pipeline()
319+
.collection("reviews")
320+
# Average review rating is 4.3
321+
.where(
322+
Field.of("rating").greater_than(
323+
db.pipeline()
324+
.collection("reviews")
325+
.aggregate(Field.of("rating").average().as_("avg"))
326+
.to_scalar_expression()
327+
)
328+
)
329+
.select("rating", "reviewer_id")
330+
.execute()
331+
)
332+
# [END pipeline_join_uncorrelated]
333+
return results
334+
335+
336+
def pipeline_force_table_scan(db):
337+
# [START pipeline_force_table_scan]
338+
# Force Planner to only do a Full-Table Scan
339+
results = (
340+
db.pipeline()
341+
.collection_group("customers")
342+
.limit(100)
343+
.execute()
344+
)
345+
# [END pipeline_force_table_scan]
346+
return results

0 commit comments

Comments
 (0)