1515 BucketAlreadyExistsException ,
1616 BucketDoesNotExistException ,
1717 CollectionAlreadyExistsException ,
18- DocumentExistsException ,
1918 DocumentNotFoundException ,
2019 QueryIndexAlreadyExistsException ,
2120 ScopeAlreadyExistsException ,
2221)
2322from couchbase .management .buckets import CreateBucketSettings
24- from couchbase .management .collections import CollectionSpec
2523from couchbase .management .options import CreatePrimaryQueryIndexOptions
2624from couchbase .options import ClusterOptions
25+ from couchbase .subdocument import upsert
2726from opentelemetry .trace import get_tracer
2827
2928from cbltest .api .error import CblTestError
@@ -86,7 +85,7 @@ def create_collections(self, bucket: str, scope: str, names: list[str]) -> None:
8685 ):
8786 try :
8887 if name != "_default" :
89- c .create_collection (CollectionSpec ( name , scope ) )
88+ c .create_collection (scope_name = scope , collection_name = name )
9089 except CollectionAlreadyExistsException :
9190 pass
9291
@@ -258,37 +257,6 @@ def run_query(
258257
259258 return list (dict (result ) for result in query_obj .execute ())
260259
261- def add_document (
262- self , bucket_name : str , document_id : str , document_data : dict
263- ) -> None :
264- """
265- Adds a document to the specified bucket in the Couchbase cluster.
266-
267- :param bucket_name: The name of the bucket where the document will be added
268- :param document_id: The unique ID of the document
269- :param document_data: The content of the document as a dictionary
270- :raises DocumentExistsException: If a document with the same ID already exists
271- """
272- with self .__tracer .start_as_current_span ("add_document" ) as span :
273- try :
274- bucket = self .__cluster .bucket (bucket_name )
275- collection = bucket .default_collection ()
276-
277- collection .insert (document_id , document_data )
278- span .set_attribute ("couchbase.bucket" , bucket_name )
279- span .set_attribute ("couchbase.document_id" , document_id )
280-
281- print (
282- f"Document '{ document_id } ' successfully added to bucket '{ bucket_name } '."
283- )
284- except DocumentExistsException :
285- print (
286- f"Document with ID '{ document_id } ' already exists in bucket '{ bucket_name } '."
287- )
288- except Exception as e :
289- print (f"An error occurred while adding document '{ document_id } ': { e } " )
290- raise
291-
292260 def upsert_document (
293261 self ,
294262 bucket : str ,
@@ -324,6 +292,151 @@ def upsert_document(
324292 f"Failed to insert document '{ doc_id } ' into { bucket } .{ scope } .{ collection } : { e } "
325293 )
326294
295+ def delete_document (
296+ self ,
297+ bucket : str ,
298+ doc_id : str ,
299+ scope : str = "_default" ,
300+ collection : str = "_default" ,
301+ ) -> None :
302+ """
303+ Deletes a document from the specified bucket.scope.collection.
304+ """
305+ with self .__tracer .start_as_current_span (
306+ "delete_document" ,
307+ attributes = {
308+ "cbl.bucket.name" : bucket ,
309+ "cbl.scope.name" : scope ,
310+ "cbl.collection.name" : collection ,
311+ "cbl.document.id" : doc_id ,
312+ },
313+ ):
314+ try :
315+ bucket_obj = _try_n_times (10 , 1 , False , self .__cluster .bucket , bucket )
316+ coll = bucket_obj .scope (scope ).collection (collection )
317+ coll .remove (doc_id )
318+ except DocumentNotFoundException :
319+ pass
320+ except Exception as e :
321+ raise CblTestError (
322+ f"Failed to delete document '{ doc_id } ' from { bucket } .{ scope } .{ collection } : { e } "
323+ )
324+
325+ def get_document (
326+ self ,
327+ bucket : str ,
328+ doc_id : str ,
329+ scope : str = "_default" ,
330+ collection : str = "_default" ,
331+ ) -> dict | None :
332+ """
333+ Gets a document from the specified bucket.scope.collection.
334+
335+ :param bucket: The bucket name.
336+ :param doc_id: The document ID.
337+ :param scope: The scope name.
338+ :param collection: The collection name.
339+ :return: The document content as a dictionary, or None if not found.
340+ """
341+ with self .__tracer .start_as_current_span (
342+ "get_document" ,
343+ attributes = {
344+ "cbl.bucket.name" : bucket ,
345+ "cbl.scope.name" : scope ,
346+ "cbl.collection.name" : collection ,
347+ "cbl.document.id" : doc_id ,
348+ },
349+ ):
350+ try :
351+ bucket_obj = _try_n_times (10 , 1 , False , self .__cluster .bucket , bucket )
352+ coll = bucket_obj .scope (scope ).collection (collection )
353+ result = coll .get (doc_id )
354+ return result .content_as [dict ] if result else None
355+ except DocumentNotFoundException :
356+ return None
357+ except Exception as e :
358+ raise CblTestError (
359+ f"Failed to get document '{ doc_id } ' from { bucket } .{ scope } .{ collection } : { e } "
360+ )
361+
362+ def upsert_document_xattr (
363+ self ,
364+ bucket : str ,
365+ doc_id : str ,
366+ xattr_key : str ,
367+ xattr_value : str ,
368+ scope : str = "_default" ,
369+ collection : str = "_default" ,
370+ ) -> None :
371+ """
372+ Upserts an xattr on a document using subdocument operations
373+
374+ :param bucket: The bucket containing the document
375+ :param doc_id: The ID of the document to update
376+ :param xattr_key: The xattr key to upsert
377+ :param xattr_value: The value to set for the xattr
378+ :param scope: The scope containing the document (default '_default')
379+ :param collection: The collection containing the document (default '_default')
380+ """
381+ with self .__tracer .start_as_current_span (
382+ "upsert_document_xattr" ,
383+ attributes = {
384+ "cbl.bucket" : bucket ,
385+ "cbl.scope" : scope ,
386+ "cbl.collection" : collection ,
387+ "cbl.document.id" : doc_id ,
388+ "cbl.xattr.key" : xattr_key ,
389+ },
390+ ):
391+ try :
392+ col = self .__cluster .bucket (bucket ).scope (scope ).collection (collection )
393+ col .mutate_in (
394+ doc_id ,
395+ [upsert (xattr_key , xattr_value , xattr = True , create_parents = True )],
396+ )
397+ except Exception as e :
398+ raise CblTestError (
399+ f"Failed to upsert xattr '{ xattr_key } ' on document '{ doc_id } ' in { bucket } .{ scope } .{ collection } : { e } "
400+ )
401+
402+ def delete_document_xattr (
403+ self ,
404+ bucket : str ,
405+ doc_id : str ,
406+ xattr_key : str ,
407+ scope : str = "_default" ,
408+ collection : str = "_default" ,
409+ ) -> None :
410+ """
411+ Deletes an xattr from a document using subdocument operations
412+
413+ :param bucket: The bucket containing the document
414+ :param doc_id: The ID of the document
415+ :param xattr_key: The xattr key to delete
416+ :param scope: The scope containing the document (default '_default')
417+ :param collection: The collection containing the document (default '_default')
418+ """
419+ with self .__tracer .start_as_current_span (
420+ "delete_document_xattr" ,
421+ attributes = {
422+ "cbl.bucket" : bucket ,
423+ "cbl.scope" : scope ,
424+ "cbl.collection" : collection ,
425+ "cbl.document.id" : doc_id ,
426+ "cbl.xattr.key" : xattr_key ,
427+ },
428+ ):
429+ try :
430+ from couchbase .subdocument import remove
431+
432+ col = self .__cluster .bucket (bucket ).scope (scope ).collection (collection )
433+ col .mutate_in (
434+ doc_id ,
435+ [remove (xattr_key , xattr = True )],
436+ )
437+ except Exception :
438+ pass
439+
327440 def start_xdcr (self , target : "CouchbaseServer" , bucket_name : str ) -> None :
328441 """
329442 Starts an XDCR replication from this cluster to the target cluster
@@ -479,4 +592,4 @@ def stop_xcdr(self, target: "CouchbaseServer", bucket_name: str) -> None:
479592 resp = session .delete (
480593 f"http://{ self .__hostname } :8091/controller/cancelXDCR/{ encoded } " ,
481594 )
482- resp .raise_for_status ()
595+ resp .raise_for_status ()
0 commit comments