From cb8302add8fc33645fa51dfbe109f51d5d0ba84a Mon Sep 17 00:00:00 2001 From: Manish Madan Date: Wed, 5 Nov 2025 19:38:10 +0530 Subject: [PATCH] Fixes shared conversation on cloud version (#2135) * (fix:shared) conv as id, not dbref * (chore) script to migrate dbref to id * (chore): ruff fix --------- Co-authored-by: GH Action - Upstream Sync --- application/api/user/sharing/routes.py | 32 ++--- ...grate_conversation_id_dbref_to_objectid.py | 114 ++++++++++++++++++ 2 files changed, 124 insertions(+), 22 deletions(-) create mode 100644 scripts/migrate_conversation_id_dbref_to_objectid.py diff --git a/application/api/user/sharing/routes.py b/application/api/user/sharing/routes.py index 8221206c..fac8bda0 100644 --- a/application/api/user/sharing/routes.py +++ b/application/api/user/sharing/routes.py @@ -13,7 +13,6 @@ from application.api.user.base import ( agents_collection, attachments_collection, conversations_collection, - db, shared_conversations_collections, ) from application.utils import check_required_fields @@ -97,9 +96,7 @@ class ShareConversation(Resource): api_uuid = pre_existing_api_document["key"] pre_existing = shared_conversations_collections.find_one( { - "conversation_id": DBRef( - "conversations", ObjectId(conversation_id) - ), + "conversation_id": ObjectId(conversation_id), "isPromptable": is_promptable, "first_n_queries": current_n_queries, "user": user, @@ -120,10 +117,7 @@ class ShareConversation(Resource): shared_conversations_collections.insert_one( { "uuid": explicit_binary, - "conversation_id": { - "$ref": "conversations", - "$id": ObjectId(conversation_id), - }, + "conversation_id": ObjectId(conversation_id), "isPromptable": is_promptable, "first_n_queries": current_n_queries, "user": user, @@ -154,10 +148,7 @@ class ShareConversation(Resource): shared_conversations_collections.insert_one( { "uuid": explicit_binary, - "conversation_id": { - "$ref": "conversations", - "$id": ObjectId(conversation_id), - }, + "conversation_id": ObjectId(conversation_id), "isPromptable": is_promptable, "first_n_queries": current_n_queries, "user": user, @@ -175,9 +166,7 @@ class ShareConversation(Resource): ) pre_existing = shared_conversations_collections.find_one( { - "conversation_id": DBRef( - "conversations", ObjectId(conversation_id) - ), + "conversation_id": ObjectId(conversation_id), "isPromptable": is_promptable, "first_n_queries": current_n_queries, "user": user, @@ -197,10 +186,7 @@ class ShareConversation(Resource): shared_conversations_collections.insert_one( { "uuid": explicit_binary, - "conversation_id": { - "$ref": "conversations", - "$id": ObjectId(conversation_id), - }, + "conversation_id": ObjectId(conversation_id), "isPromptable": is_promptable, "first_n_queries": current_n_queries, "user": user, @@ -233,10 +219,12 @@ class GetPubliclySharedConversations(Resource): if ( shared and "conversation_id" in shared - and isinstance(shared["conversation_id"], DBRef) ): - conversation_ref = shared["conversation_id"] - conversation = db.dereference(conversation_ref) + # conversation_id is now stored as an ObjectId, not a DBRef + conversation_id = shared["conversation_id"] + conversation = conversations_collection.find_one( + {"_id": conversation_id} + ) if conversation is None: return make_response( jsonify( diff --git a/scripts/migrate_conversation_id_dbref_to_objectid.py b/scripts/migrate_conversation_id_dbref_to_objectid.py new file mode 100644 index 00000000..8ffedd11 --- /dev/null +++ b/scripts/migrate_conversation_id_dbref_to_objectid.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +""" +Migration script to convert conversation_id from DBRef to ObjectId in shared_conversations collection. +""" + +import pymongo +import logging +from tqdm import tqdm +from bson.dbref import DBRef +from bson.objectid import ObjectId + +# Configure logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger() + +# Configuration +MONGO_URI = "mongodb://localhost:27017/" +DB_NAME = "docsgpt" + +def backup_collection(collection, backup_collection_name): + """Backup collection before migration.""" + logger.info(f"Backing up collection {collection.name} to {backup_collection_name}") + collection.aggregate([{"$out": backup_collection_name}]) + logger.info("Backup completed") + +def migrate_conversation_id_dbref_to_objectid(): + """Migrate conversation_id from DBRef to ObjectId.""" + client = pymongo.MongoClient(MONGO_URI) + db = client[DB_NAME] + shared_conversations_collection = db["shared_conversations"] + + try: + # Backup collection before migration + backup_collection(shared_conversations_collection, "shared_conversations_backup") + + # Find all documents and filter for DBRef conversation_id in Python + all_documents = list(shared_conversations_collection.find({})) + documents_with_dbref = [] + + for doc in all_documents: + conversation_id_field = doc.get("conversation_id") + if isinstance(conversation_id_field, DBRef): + documents_with_dbref.append(doc) + + if not documents_with_dbref: + logger.info("No documents with DBRef conversation_id found. Migration not needed.") + return + + logger.info(f"Found {len(documents_with_dbref)} documents with DBRef conversation_id") + + # Process each document + migrated_count = 0 + error_count = 0 + + for doc in tqdm(documents_with_dbref, desc="Migrating conversation_id"): + try: + conversation_id_field = doc.get("conversation_id") + + # Extract the ObjectId from the DBRef + dbref_id = conversation_id_field.id + + if dbref_id and ObjectId.is_valid(dbref_id): + # Update the document to use direct ObjectId + result = shared_conversations_collection.update_one( + {"_id": doc["_id"]}, + {"$set": {"conversation_id": dbref_id}} + ) + + if result.modified_count > 0: + migrated_count += 1 + logger.debug(f"Successfully migrated document {doc['_id']}") + else: + error_count += 1 + logger.warning(f"Failed to update document {doc['_id']}") + else: + error_count += 1 + logger.warning(f"Invalid ObjectId in DBRef for document {doc['_id']}: {dbref_id}") + + except Exception as e: + error_count += 1 + logger.error(f"Error migrating document {doc['_id']}: {e}") + + # Final verification + all_docs_after = list(shared_conversations_collection.find({})) + remaining_dbref = 0 + for doc in all_docs_after: + if isinstance(doc.get("conversation_id"), DBRef): + remaining_dbref += 1 + + logger.info("Migration completed:") + logger.info(f" - Total documents processed: {len(documents_with_dbref)}") + logger.info(f" - Successfully migrated: {migrated_count}") + logger.info(f" - Errors encountered: {error_count}") + logger.info(f" - Remaining DBRef documents: {remaining_dbref}") + + if remaining_dbref == 0: + logger.info("✅ Migration successful: All DBRef conversation_id fields have been converted to ObjectId") + else: + logger.warning(f"⚠️ Migration incomplete: {remaining_dbref} DBRef documents still exist") + + except Exception as e: + logger.error(f"Migration failed: {e}") + raise + finally: + client.close() + +if __name__ == "__main__": + try: + logger.info("Starting conversation_id DBRef to ObjectId migration...") + migrate_conversation_id_dbref_to_objectid() + logger.info("Migration completed successfully!") + except Exception as e: + logger.error(f"Migration failed due to error: {e}") + logger.warning("Please verify database state or restore from backups if necessary.")