Ver Fonte

fix: handle manually deleted Qdrant collections properly

Fixed two issues with collection management when collections are
manually deleted from Qdrant server:

1. Collection existence verification: When reusing a collection name
   from database records, now verifies the collection still exists in
   Qdrant before reusing it. If deleted externally, cleans up database
   records and assigns a fresh iterator.

2. Iterator numbering: Changed iterator to start at 0 instead of 1.
   - Empty category now returns iterator 0
   - Results in collection names like: {custom_id}-{category}_0
   - Previously started at _1, now properly starts at _0

This ensures that after manually deleting collections and forcing
re-sync, new collections start with proper numbering (_0, _1, _2...)
instead of continuing from stale database state (_4, _5, etc).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Fszontagh há 8 meses atrás
pai
commit
4805e0b4f3
2 ficheiros alterados com 33 adições e 15 exclusões
  1. 30 12
      src/services/QdrantEmbeddingWorkflow.ts
  2. 3 3
      src/services/QdrantService.ts

+ 30 - 12
src/services/QdrantEmbeddingWorkflow.ts

@@ -95,18 +95,36 @@ export class QdrantEmbeddingWorkflow {
       let collectionName: string;
       let collectionName: string;
 
 
       if (existingEmbedding && existingEmbedding.collection_name) {
       if (existingEmbedding && existingEmbedding.collection_name) {
-        // Reuse existing collection for this URL
-        collectionName = existingEmbedding.collection_name;
-        logger.debug(`Reusing existing collection ${collectionName} for content ${contentId}`);
-
-        // Delete all existing points in this collection for this content_id
-        // This ensures we don't have duplicate/stale chunks
-        await this.qdrantService.deletePointsByContentId(collectionName, contentId);
-
-        // Delete all existing embedding records for this content
-        // We'll create new ones for each chunk
-        for (const emb of existingEmbeddings) {
-          this.database.deleteShopEmbedding(emb.id);
+        // Check if the collection still exists in Qdrant
+        const collectionExists = await this.qdrantService.collectionExists(existingEmbedding.collection_name);
+
+        if (collectionExists) {
+          // Reuse existing collection for this URL
+          collectionName = existingEmbedding.collection_name;
+          logger.debug(`Reusing existing collection ${collectionName} for content ${contentId}`);
+
+          // Delete all existing points in this collection for this content_id
+          // This ensures we don't have duplicate/stale chunks
+          await this.qdrantService.deletePointsByContentId(collectionName, contentId);
+
+          // Delete all existing embedding records for this content
+          // We'll create new ones for each chunk
+          for (const emb of existingEmbeddings) {
+            this.database.deleteShopEmbedding(emb.id);
+          }
+        } else {
+          // Collection was deleted externally, clean up database and get new iterator
+          logger.info(`Collection ${existingEmbedding.collection_name} no longer exists in Qdrant, assigning new collection`);
+
+          // Delete all existing embedding records for this content
+          for (const emb of existingEmbeddings) {
+            this.database.deleteShopEmbedding(emb.id);
+          }
+
+          // Get next iterator for this category (one collection per URL)
+          const iterator = await this.qdrantService.getNextIterator(shop.custom_id, contentType);
+          collectionName = this.qdrantService.generateCollectionName(shop.custom_id, contentType, iterator);
+          logger.info(`Assigned new collection ${collectionName} for content ${contentId}`);
         }
         }
       } else {
       } else {
         // Get next iterator for this category (one collection per URL)
         // Get next iterator for this category (one collection per URL)

+ 3 - 3
src/services/QdrantService.ts

@@ -368,15 +368,15 @@ export class QdrantService {
    * Get next iterator number for a category
    * Get next iterator number for a category
    */
    */
   async getNextIterator(shopCustomId: string, category: string): Promise<number> {
   async getNextIterator(shopCustomId: string, category: string): Promise<number> {
-    if (!this.enabled) return 1;
+    if (!this.enabled) return 0;
 
 
     try {
     try {
       const collections = await this.getCollectionsForCategory(shopCustomId, category);
       const collections = await this.getCollectionsForCategory(shopCustomId, category);
 
 
-      if (collections.length === 0) return 1;
+      if (collections.length === 0) return 0;
 
 
       // Find the highest iterator number
       // Find the highest iterator number
-      let maxIterator = 0;
+      let maxIterator = -1;
       for (const collectionName of collections) {
       for (const collectionName of collections) {
         const parsed = this.parseCollectionName(collectionName);
         const parsed = this.parseCollectionName(collectionName);
         if (parsed && parsed.iterator > maxIterator) {
         if (parsed && parsed.iterator > maxIterator) {