5050import java .io .File ;
5151import java .io .IOException ;
5252import java .nio .ByteBuffer ;
53+ import java .nio .file .Files ;
54+ import java .nio .file .StandardCopyOption ;
5355import java .util .*;
5456import java .util .concurrent .atomic .AtomicBoolean ;
5557import java .util .concurrent .atomic .AtomicInteger ;
@@ -250,7 +252,7 @@ public LSMVectorIndex(final DatabaseInternal database, final String name, final
250252
251253 // Create PQ file handler for Product Quantization (zero-disk-I/O search)
252254 // Note: PQ file uses direct I/O (not ArcadeDB pages) since it's loaded entirely into memory
253- this .pqFile = new LSMVectorIndexPQFile ( filePath );
255+ this .pqFile = createPQFileWithFallback ( mutable . getFilePath () );
254256
255257 LogManager .instance ()
256258 .log (this , Level .FINE , "Created LSMVectorIndex: indexName=%s, vectorFileId=%d, graphFileId=%d" , indexName ,
@@ -292,7 +294,7 @@ protected LSMVectorIndex(final DatabaseInternal database, final String name, fin
292294
293295 // Create PQ file handler (for zero-disk-I/O search)
294296 // PQ data will be loaded after schema loads metadata (see loadVectorsAfterSchemaLoad)
295- this .pqFile = new LSMVectorIndexPQFile ( filePath );
297+ this .pqFile = createPQFileWithFallback ( mutable . getFilePath () );
296298
297299 // Initialize compaction fields
298300 this .currentMutablePages = new AtomicInteger (mutable .getTotalPages ());
@@ -315,6 +317,39 @@ protected LSMVectorIndex(final DatabaseInternal database, final String name, fin
315317 // See loadVectorsAfterSchemaLoad() method which is called by LSMVectorIndexMutable.onAfterSchemaLoad()
316318 }
317319
320+ private LSMVectorIndexPQFile createPQFileWithFallback (final String primaryBasePath ) {
321+ // Use the component file path as canonical. If legacy PQ exists at a shorter base name, migrate it once.
322+ final LSMVectorIndexPQFile pq = new LSMVectorIndexPQFile (primaryBasePath );
323+
324+ // Derive a legacy base path by stripping the first extension (e.g., drop .4.262144.v0.lsmvecidx)
325+ String legacyBasePath = null ;
326+ final int dot = primaryBasePath .indexOf ('.' );
327+ if (dot > 0 ) {
328+ legacyBasePath = primaryBasePath .substring (0 , dot );
329+ }
330+
331+ if (!pq .exists () && legacyBasePath != null ) {
332+ final LSMVectorIndexPQFile legacyPQ = new LSMVectorIndexPQFile (legacyBasePath );
333+ if (legacyPQ .exists ()) {
334+ try {
335+ final var targetParent = pq .getFilePath ().getParent ();
336+ if (targetParent != null && !Files .exists (targetParent )) {
337+ Files .createDirectories (targetParent );
338+ }
339+ Files .move (legacyPQ .getFilePath (), pq .getFilePath (), StandardCopyOption .REPLACE_EXISTING );
340+ LogManager .instance ().log (this , Level .INFO ,
341+ "Migrated PQ file from legacy path %s to canonical %s" , legacyPQ .getFilePath (), pq .getFilePath ());
342+ } catch (final Exception e ) {
343+ LogManager .instance ().log (this , Level .WARNING ,
344+ "Failed to migrate PQ file from legacy path %s to canonical %s: %s" , legacyPQ .getFilePath (), pq .getFilePath (),
345+ e .getMessage ());
346+ }
347+ }
348+ }
349+
350+ return pq ;
351+ }
352+
318353 /**
319354 * Load vectors from pages after schema has loaded metadata.
320355 * Called by LSMVectorIndexMutable.onAfterSchemaLoad() after dimensions are set from schema.json.
0 commit comments