|
36 | 36 | ) |
37 | 37 | from nitric.utils import new_default_channel, _dict_from_struct, _struct_from_dict |
38 | 38 |
|
| 39 | +NIL_DOC_ID = "" |
| 40 | + |
39 | 41 |
|
40 | 42 | class CollectionDepthException(Exception): |
41 | 43 | """The max depth of document sub-collections has been exceeded.""" |
@@ -147,6 +149,23 @@ def doc(self, doc_id: str) -> DocumentRef: |
147 | 149 | """Return a reference to a document in the collection.""" |
148 | 150 | return DocumentRef(_documents=self._documents, parent=self, id=doc_id) |
149 | 151 |
|
| 152 | + def collection(self, name: str) -> CollectionGroupRef: |
| 153 | + """ |
| 154 | + Return a reference to a sub-collection of this document. |
| 155 | +
|
| 156 | + This is currently only supported to one level of depth. |
| 157 | + e.g. Documents().collection('a').collection('b').doc('c') is valid, |
| 158 | + Documents().collection('a').doc('b').collection('c').collection('d') is invalid (1 level too deep). |
| 159 | + """ |
| 160 | + current_depth = self.sub_collection_depth() |
| 161 | + if current_depth >= MAX_SUB_COLLECTION_DEPTH: |
| 162 | + # Collection nesting is only supported to a maximum depth. |
| 163 | + raise CollectionDepthException( |
| 164 | + f"sub-collections supported to a depth of {MAX_SUB_COLLECTION_DEPTH}, " |
| 165 | + f"attempted to create new collection with depth {current_depth + 1}" |
| 166 | + ) |
| 167 | + return CollectionGroupRef(_documents=self._documents, name=name, parent=self) |
| 168 | + |
150 | 169 | def query( |
151 | 170 | self, |
152 | 171 | paging_token: Any = None, |
@@ -174,6 +193,66 @@ def is_sub_collection(self): |
174 | 193 | return self.parent is not None |
175 | 194 |
|
176 | 195 |
|
| 196 | +@dataclass(frozen=True, order=True) |
| 197 | +class CollectionGroupRef: |
| 198 | + """A reference to a collection group.""" |
| 199 | + |
| 200 | + _documents: Documents |
| 201 | + name: str |
| 202 | + parent: Union[CollectionRef, None] = field(default_factory=lambda: None) |
| 203 | + |
| 204 | + def query( |
| 205 | + self, |
| 206 | + paging_token: Any = None, |
| 207 | + limit: int = 0, |
| 208 | + expressions: Union[Expression, List[Expression]] = None, |
| 209 | + ) -> QueryBuilder: |
| 210 | + """Return a query builder scoped to this collection.""" |
| 211 | + return QueryBuilder( |
| 212 | + documents=self._documents, |
| 213 | + collection=self.to_collection_ref(), |
| 214 | + paging_token=paging_token, |
| 215 | + limit=limit, |
| 216 | + expressions=[expressions] if isinstance(expressions, Expression) else expressions, |
| 217 | + ) |
| 218 | + |
| 219 | + def sub_collection_depth(self) -> int: |
| 220 | + """Return the depth of this collection group, which is a count of the parents above this collection.""" |
| 221 | + if not self.is_sub_collection(): |
| 222 | + return 0 |
| 223 | + else: |
| 224 | + return self.parent.sub_collection_depth() + 1 |
| 225 | + |
| 226 | + def is_sub_collection(self): |
| 227 | + """Return True if this collection is a sub-collection of a document in another collection.""" |
| 228 | + return self.parent is not None |
| 229 | + |
| 230 | + def to_collection_ref(self): |
| 231 | + """Return this collection group as a collection ref""" |
| 232 | + return CollectionRef( |
| 233 | + self._documents, |
| 234 | + self.name, |
| 235 | + DocumentRef( |
| 236 | + self._documents, |
| 237 | + self.parent, |
| 238 | + NIL_DOC_ID, |
| 239 | + ), |
| 240 | + ) |
| 241 | + |
| 242 | + @staticmethod |
| 243 | + def from_collection_ref(collectionRef: CollectionRef, documents: Documents) -> CollectionGroupRef: |
| 244 | + """Return a collection ref as a collection group""" |
| 245 | + if collectionRef.parent is not None: |
| 246 | + return CollectionGroupRef( |
| 247 | + documents, |
| 248 | + collectionRef.name, |
| 249 | + CollectionGroupRef.from_collection_ref( |
| 250 | + collectionRef.parent, |
| 251 | + documents, |
| 252 | + ), |
| 253 | + ) |
| 254 | + |
| 255 | + |
177 | 256 | class Operator(Enum): |
178 | 257 | """Valid query expression operators.""" |
179 | 258 |
|
|
0 commit comments