Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/google/cloud/firestore_v1/client.py: 48%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

62 statements  

1# Copyright 2017 Google LLC All rights reserved. 

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"""Client for interacting with the Google Cloud Firestore API. 

16 

17This is the base from which all interactions with the API occur. 

18 

19In the hierarchy of API concepts 

20 

21* a :class:`~google.cloud.firestore_v1.client.Client` owns a 

22 :class:`~google.cloud.firestore_v1.collection.CollectionReference` 

23* a :class:`~google.cloud.firestore_v1.client.Client` owns a 

24 :class:`~google.cloud.firestore_v1.document.DocumentReference` 

25""" 

26from __future__ import annotations 

27 

28from typing import TYPE_CHECKING, Any, Generator, Iterable, List, Optional, Union 

29 

30from google.api_core import gapic_v1 

31from google.api_core import retry as retries 

32 

33from google.cloud.firestore_v1.base_client import ( 

34 _CLIENT_INFO, 

35 BaseClient, 

36 _parse_batch_get, 

37 _path_helper, 

38) 

39 

40# Types needed only for Type Hints 

41from google.cloud.firestore_v1.base_document import DocumentSnapshot 

42from google.cloud.firestore_v1.batch import WriteBatch 

43from google.cloud.firestore_v1.collection import CollectionReference 

44from google.cloud.firestore_v1.document import DocumentReference 

45from google.cloud.firestore_v1.field_path import FieldPath 

46from google.cloud.firestore_v1.query import CollectionGroup 

47from google.cloud.firestore_v1.services.firestore import client as firestore_client 

48from google.cloud.firestore_v1.services.firestore.transports import ( 

49 grpc as firestore_grpc_transport, 

50) 

51from google.cloud.firestore_v1.transaction import Transaction 

52 

53if TYPE_CHECKING: # pragma: NO COVER 

54 from google.cloud.firestore_v1.bulk_writer import BulkWriter 

55 import datetime 

56 

57 

58class Client(BaseClient): 

59 """Client for interacting with Google Cloud Firestore API. 

60 

61 .. note:: 

62 

63 Since the Cloud Firestore API requires the gRPC transport, no 

64 ``_http`` argument is accepted by this class. 

65 

66 Args: 

67 project (Optional[str]): The project which the client acts on behalf 

68 of. If not passed, falls back to the default inferred 

69 from the environment. 

70 credentials (Optional[~google.auth.credentials.Credentials]): The 

71 OAuth2 Credentials to use for this client. If not passed, falls 

72 back to the default inferred from the environment. 

73 database (Optional[str]): The database name that the client targets. 

74 If not passed, falls back to :attr:`DEFAULT_DATABASE`. 

75 client_info (Optional[google.api_core.gapic_v1.client_info.ClientInfo]): 

76 The client info used to send a user-agent string along with API 

77 requests. If ``None``, then default info will be used. Generally, 

78 you only need to set this if you're developing your own library 

79 or partner tool. 

80 client_options (Union[dict, google.api_core.client_options.ClientOptions]): 

81 Client options used to set user options on the client. API Endpoint 

82 should be set through client_options. 

83 """ 

84 

85 def __init__( 

86 self, 

87 project=None, 

88 credentials=None, 

89 database=None, 

90 client_info=_CLIENT_INFO, 

91 client_options=None, 

92 ) -> None: 

93 super(Client, self).__init__( 

94 project=project, 

95 credentials=credentials, 

96 database=database, 

97 client_info=client_info, 

98 client_options=client_options, 

99 ) 

100 

101 @property 

102 def _firestore_api(self): 

103 """Lazy-loading getter GAPIC Firestore API. 

104 Returns: 

105 :class:`~google.cloud.gapic.firestore.v1`.firestore_client.FirestoreClient: 

106 The GAPIC client with the credentials of the current client. 

107 """ 

108 return self._firestore_api_helper( 

109 firestore_grpc_transport.FirestoreGrpcTransport, 

110 firestore_client.FirestoreClient, 

111 firestore_client, 

112 ) 

113 

114 def collection(self, *collection_path: str) -> CollectionReference: 

115 """Get a reference to a collection. 

116 

117 For a top-level collection: 

118 

119 .. code-block:: python 

120 

121 >>> client.collection('top') 

122 

123 For a sub-collection: 

124 

125 .. code-block:: python 

126 

127 >>> client.collection('mydocs/doc/subcol') 

128 >>> # is the same as 

129 >>> client.collection('mydocs', 'doc', 'subcol') 

130 

131 Sub-collections can be nested deeper in a similar fashion. 

132 

133 Args: 

134 collection_path: Can either be 

135 

136 * A single ``/``-delimited path to a collection 

137 * A tuple of collection path segments 

138 

139 Returns: 

140 :class:`~google.cloud.firestore_v1.collection.CollectionReference`: 

141 A reference to a collection in the Firestore database. 

142 """ 

143 return CollectionReference(*_path_helper(collection_path), client=self) 

144 

145 def collection_group(self, collection_id: str) -> CollectionGroup: 

146 """ 

147 Creates and returns a new Query that includes all documents in the 

148 database that are contained in a collection or subcollection with the 

149 given collection_id. 

150 

151 .. code-block:: python 

152 

153 >>> query = client.collection_group('mygroup') 

154 

155 Args: 

156 collection_id (str) Identifies the collections to query over. 

157 

158 Every collection or subcollection with this ID as the last segment of its 

159 path will be included. Cannot contain a slash. 

160 

161 Returns: 

162 :class:`~google.cloud.firestore_v1.query.CollectionGroup`: 

163 The created Query. 

164 """ 

165 return CollectionGroup(self._get_collection_reference(collection_id)) 

166 

167 def document(self, *document_path: str) -> DocumentReference: 

168 """Get a reference to a document in a collection. 

169 

170 For a top-level document: 

171 

172 .. code-block:: python 

173 

174 >>> client.document('collek/shun') 

175 >>> # is the same as 

176 >>> client.document('collek', 'shun') 

177 

178 For a document in a sub-collection: 

179 

180 .. code-block:: python 

181 

182 >>> client.document('mydocs/doc/subcol/child') 

183 >>> # is the same as 

184 >>> client.document('mydocs', 'doc', 'subcol', 'child') 

185 

186 Documents in sub-collections can be nested deeper in a similar fashion. 

187 

188 Args: 

189 document_path): Can either be 

190 

191 * A single ``/``-delimited path to a document 

192 * A tuple of document path segments 

193 

194 Returns: 

195 :class:`~google.cloud.firestore_v1.document.DocumentReference`: 

196 A reference to a document in a collection. 

197 """ 

198 return DocumentReference( 

199 *self._document_path_helper(*document_path), client=self 

200 ) 

201 

202 def get_all( 

203 self, 

204 references: list, 

205 field_paths: Iterable[str] | None = None, 

206 transaction: Transaction | None = None, 

207 retry: retries.Retry | object | None = gapic_v1.method.DEFAULT, 

208 timeout: float | None = None, 

209 *, 

210 read_time: datetime.datetime | None = None, 

211 ) -> Generator[DocumentSnapshot, Any, None]: 

212 """Retrieve a batch of documents. 

213 

214 .. note:: 

215 

216 Documents returned by this method are not guaranteed to be 

217 returned in the same order that they are given in ``references``. 

218 

219 .. note:: 

220 

221 If multiple ``references`` refer to the same document, the server 

222 will only return one result. 

223 

224 See :meth:`~google.cloud.firestore_v1.client.Client.field_path` for 

225 more information on **field paths**. 

226 

227 If a ``transaction`` is used and it already has write operations 

228 added, this method cannot be used (i.e. read-after-write is not 

229 allowed). 

230 

231 Args: 

232 references (List[.DocumentReference, ...]): Iterable of document 

233 references to be retrieved. 

234 field_paths (Optional[Iterable[str, ...]]): An iterable of field 

235 paths (``.``-delimited list of field names) to use as a 

236 projection of document fields in the returned results. If 

237 no value is provided, all fields will be returned. 

238 transaction (Optional[:class:`~google.cloud.firestore_v1.transaction.Transaction`]): 

239 An existing transaction that these ``references`` will be 

240 retrieved in. 

241 retry (google.api_core.retry.Retry): Designation of what errors, if any, 

242 should be retried. Defaults to a system-specified policy. 

243 timeout (float): The timeout for this request. Defaults to a 

244 system-specified value. 

245 read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given 

246 time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery 

247 is enabled, can additionally be a whole minute timestamp within the past 7 days. If no 

248 timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC. 

249 

250 Yields: 

251 .DocumentSnapshot: The next document snapshot that fulfills the 

252 query, or :data:`None` if the document does not exist. 

253 """ 

254 request, reference_map, kwargs = self._prep_get_all( 

255 references, field_paths, transaction, retry, timeout, read_time 

256 ) 

257 

258 response_iterator = self._firestore_api.batch_get_documents( 

259 request=request, 

260 metadata=self._rpc_metadata, 

261 **kwargs, 

262 ) 

263 

264 for get_doc_response in response_iterator: 

265 yield _parse_batch_get(get_doc_response, reference_map, self) 

266 

267 def collections( 

268 self, 

269 retry: retries.Retry | object | None = gapic_v1.method.DEFAULT, 

270 timeout: float | None = None, 

271 *, 

272 read_time: datetime.datetime | None = None, 

273 ) -> Generator[Any, Any, None]: 

274 """List top-level collections of the client's database. 

275 

276 Args: 

277 retry (google.api_core.retry.Retry): Designation of what errors, if any, 

278 should be retried. Defaults to a system-specified policy. 

279 timeout (float): The timeout for this request. Defaults to a 

280 system-specified value. 

281 read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given 

282 time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery 

283 is enabled, can additionally be a whole minute timestamp within the past 7 days. If no 

284 timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC. 

285 

286 Returns: 

287 Sequence[:class:`~google.cloud.firestore_v1.collection.CollectionReference`]: 

288 iterator of subcollections of the current document. 

289 """ 

290 request, kwargs = self._prep_collections(retry, timeout, read_time) 

291 

292 iterator = self._firestore_api.list_collection_ids( 

293 request=request, 

294 metadata=self._rpc_metadata, 

295 **kwargs, 

296 ) 

297 

298 for collection_id in iterator: 

299 yield self.collection(collection_id) 

300 

301 def recursive_delete( 

302 self, 

303 reference: Union[CollectionReference, DocumentReference], 

304 *, 

305 bulk_writer: Optional["BulkWriter"] = None, 

306 chunk_size: int = 5000, 

307 ) -> int: 

308 """Deletes documents and their subcollections, regardless of collection 

309 name. 

310 

311 Passing a CollectionReference leads to each document in the collection 

312 getting deleted, as well as all of their descendents. 

313 

314 Passing a DocumentReference deletes that one document and all of its 

315 descendents. 

316 

317 Args: 

318 reference (Union[ 

319 :class:`@google.cloud.firestore_v1.collection.CollectionReference`, 

320 :class:`@google.cloud.firestore_v1.document.DocumentReference`, 

321 ]) 

322 The reference to be deleted. 

323 

324 bulk_writer (Optional[:class:`@google.cloud.firestore_v1.bulk_writer.BulkWriter`]) 

325 The BulkWriter used to delete all matching documents. Supply this 

326 if you want to override the default throttling behavior. 

327 

328 """ 

329 if bulk_writer is None: 

330 bulk_writer = self.bulk_writer() 

331 

332 return self._recursive_delete( 

333 reference, 

334 bulk_writer, 

335 chunk_size=chunk_size, 

336 ) 

337 

338 def _recursive_delete( 

339 self, 

340 reference: Union[CollectionReference, DocumentReference], 

341 bulk_writer: "BulkWriter", 

342 *, 

343 chunk_size: int = 5000, 

344 depth: int = 0, 

345 ) -> int: 

346 """Recursion helper for `recursive_delete.""" 

347 

348 num_deleted: int = 0 

349 

350 if isinstance(reference, CollectionReference): 

351 chunk: List[DocumentSnapshot] 

352 for chunk in ( 

353 reference.recursive() 

354 .select([FieldPath.document_id()]) 

355 ._chunkify(chunk_size) 

356 ): 

357 doc_snap: DocumentSnapshot 

358 for doc_snap in chunk: 

359 num_deleted += 1 

360 bulk_writer.delete(doc_snap.reference) 

361 

362 elif isinstance(reference, DocumentReference): 

363 col_ref: CollectionReference 

364 for col_ref in reference.collections(): 

365 num_deleted += self._recursive_delete( 

366 col_ref, 

367 bulk_writer, 

368 chunk_size=chunk_size, 

369 depth=depth + 1, 

370 ) 

371 num_deleted += 1 

372 bulk_writer.delete(reference) 

373 

374 else: 

375 raise TypeError( 

376 f"Unexpected type for reference: {reference.__class__.__name__}" 

377 ) 

378 

379 if depth == 0: 

380 bulk_writer.close() 

381 

382 return num_deleted 

383 

384 def batch(self) -> WriteBatch: 

385 """Get a batch instance from this client. 

386 

387 Returns: 

388 :class:`~google.cloud.firestore_v1.batch.WriteBatch`: 

389 A "write" batch to be used for accumulating document changes and 

390 sending the changes all at once. 

391 """ 

392 return WriteBatch(self) 

393 

394 def transaction(self, **kwargs) -> Transaction: 

395 """Get a transaction that uses this client. 

396 

397 See :class:`~google.cloud.firestore_v1.transaction.Transaction` for 

398 more information on transactions and the constructor arguments. 

399 

400 Args: 

401 kwargs (Dict[str, Any]): The keyword arguments (other than 

402 ``client``) to pass along to the 

403 :class:`~google.cloud.firestore_v1.transaction.Transaction` 

404 constructor. 

405 

406 Returns: 

407 :class:`~google.cloud.firestore_v1.transaction.Transaction`: 

408 A transaction attached to this client. 

409 """ 

410 return Transaction(self, **kwargs)