Skip to content

Commit 11c854d

Browse files
authored
Use parent path for watch on queries. (#7752)
Avoids hard-coding the root document, to enable queries on nested collections. Closes #7515. Supersedes #7613.
1 parent 3618354 commit 11c854d

3 files changed

Lines changed: 84 additions & 17 deletions

File tree

firestore/google/cloud/firestore_v1/watch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,9 +351,9 @@ def for_document(
351351
def for_query(
352352
cls, query, snapshot_callback, snapshot_class_instance, reference_class_instance
353353
):
354+
parent_path, _ = query._parent._parent_info()
354355
query_target = firestore_pb2.Target.QueryTarget(
355-
parent=query._client._database_string + "/documents",
356-
structured_query=query._to_protobuf(),
356+
parent=parent_path, structured_query=query._to_protobuf()
357357
)
358358

359359
return cls(

firestore/tests/unit/v1/test_cross_language.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,8 @@ def test_listen_testprotos(test_proto): # pragma: NO COVER
236236
def callback(keys, applied_changes, read_time):
237237
snapshots.append((keys, applied_changes, read_time))
238238

239-
query = DummyQuery(client=client)
239+
collection = DummyCollection(client=client)
240+
query = DummyQuery(parent=collection)
240241
watch = Watch.for_query(
241242
query, callback, DocumentSnapshot, DocumentReference
242243
)
@@ -374,11 +375,24 @@ def stop(self):
374375
self.is_active = False
375376

376377

378+
class DummyCollection(object):
379+
def __init__(self, client, parent=None):
380+
self._client = client
381+
self._parent = parent
382+
383+
def _parent_info(self):
384+
return "{}/documents".format(self._client._database_string), None
385+
386+
377387
class DummyQuery(object): # pragma: NO COVER
378-
def __init__(self, **kw):
379-
self._client = kw["client"]
388+
def __init__(self, parent):
389+
self._parent = parent
380390
self._comparator = lambda x, y: 1
381391

392+
@property
393+
def _client(self):
394+
return self._parent._client
395+
382396
def _to_protobuf(self):
383397
from google.cloud.firestore_v1.proto import query_pb2
384398

firestore/tests/unit/v1/test_watch.py

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,44 @@ def test_for_query(self):
227227
snapshot_callback = self._snapshot_callback
228228
snapshot_class_instance = DummyDocumentSnapshot
229229
document_reference_class_instance = DummyDocumentReference
230+
client = DummyFirestore()
231+
parent = DummyCollection(client)
230232
modulename = "google.cloud.firestore_v1.watch"
231233
pb2 = DummyPb2()
232234
with mock.patch("%s.firestore_pb2" % modulename, pb2):
233235
with mock.patch("%s.Watch.ResumableBidiRpc" % modulename, DummyRpc):
234236
with mock.patch(
235237
"%s.Watch.BackgroundConsumer" % modulename, DummyBackgroundConsumer
236238
):
237-
query = DummyQuery()
239+
query = DummyQuery(parent=parent)
240+
inst = Watch.for_query(
241+
query,
242+
snapshot_callback,
243+
snapshot_class_instance,
244+
document_reference_class_instance,
245+
)
246+
self.assertTrue(inst._consumer.started)
247+
self.assertTrue(inst._rpc.callbacks, [inst._on_rpc_done])
248+
self.assertEqual(inst._targets["query"], "dummy query target")
249+
250+
def test_for_query_nested(self):
251+
from google.cloud.firestore_v1.watch import Watch
252+
253+
snapshot_callback = self._snapshot_callback
254+
snapshot_class_instance = DummyDocumentSnapshot
255+
document_reference_class_instance = DummyDocumentReference
256+
client = DummyFirestore()
257+
root = DummyCollection(client)
258+
grandparent = DummyDocument("document", parent=root)
259+
parent = DummyCollection(client, parent=grandparent)
260+
modulename = "google.cloud.firestore_v1.watch"
261+
pb2 = DummyPb2()
262+
with mock.patch("%s.firestore_pb2" % modulename, pb2):
263+
with mock.patch("%s.Watch.ResumableBidiRpc" % modulename, DummyRpc):
264+
with mock.patch(
265+
"%s.Watch.BackgroundConsumer" % modulename, DummyBackgroundConsumer
266+
):
267+
query = DummyQuery(parent=parent)
238268
inst = Watch.for_query(
239269
query,
240270
snapshot_callback,
@@ -693,18 +723,41 @@ def __init__(self, *document_path, **kw):
693723
self.__dict__.update(kw)
694724

695725

696-
class DummyQuery(object): # pragma: NO COVER
697-
def __init__(self, **kw):
698-
if "client" not in kw:
699-
self._client = DummyFirestore()
700-
else:
701-
self._client = kw["client"]
726+
class DummyDocument(object):
727+
def __init__(self, name, parent):
728+
self._name = name
729+
self._parent = parent
702730

703-
if "comparator" not in kw:
704-
# don't really do the comparison, just return 0 (equal) for all
705-
self._comparator = lambda x, y: 1
706-
else:
707-
self._comparator = kw["comparator"]
731+
@property
732+
def _document_path(self):
733+
return "{}/documents/{}".format(
734+
self._parent._client._database_string, self._name
735+
)
736+
737+
738+
class DummyCollection(object):
739+
def __init__(self, client, parent=None):
740+
self._client = client
741+
self._parent = parent
742+
743+
def _parent_info(self):
744+
if self._parent is None:
745+
return "{}/documents".format(self._client._database_string), None
746+
return self._parent._document_path, None
747+
748+
749+
def _compare(x, y): # pragma: NO COVER
750+
return 1
751+
752+
753+
class DummyQuery(object):
754+
def __init__(self, parent):
755+
self._comparator = _compare
756+
self._parent = parent
757+
758+
@property
759+
def _client(self):
760+
return self._parent._client
708761

709762
def _to_protobuf(self):
710763
return ""

0 commit comments

Comments
 (0)