solr migration button

This commit is contained in:
Stefan Hardegger
2025-09-23 14:42:38 +02:00
parent 9472210d8b
commit 6ee2d67027
4 changed files with 272 additions and 11 deletions

View File

@@ -161,6 +161,58 @@ public class AdminSearchController {
}
}
/**
* Add libraryId field to Solr schema via Schema API.
* This is a prerequisite for library-aware indexing.
*/
@PostMapping("/solr/add-library-field")
public ResponseEntity<Map<String, Object>> addLibraryField() {
try {
logger.info("Starting Solr libraryId field addition");
if (!searchServiceAdapter.isSearchServiceAvailable()) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"error", "Solr is not available or healthy"
));
}
if (solrService == null) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"error", "Solr service not available"
));
}
// Add the libraryId field to the schema
try {
solrService.addLibraryIdField();
logger.info("libraryId field added successfully to schema");
return ResponseEntity.ok(Map.of(
"success", true,
"message", "libraryId field added successfully to both stories and authors cores",
"note", "You can now run the library schema migration"
));
} catch (Exception e) {
logger.error("Failed to add libraryId field to schema", e);
return ResponseEntity.internalServerError().body(Map.of(
"success", false,
"error", "Failed to add libraryId field to schema: " + e.getMessage(),
"details", "Check that Solr is accessible and schema is modifiable"
));
}
} catch (Exception e) {
logger.error("Error during libraryId field addition", e);
return ResponseEntity.internalServerError().body(Map.of(
"success", false,
"error", "libraryId field addition failed: " + e.getMessage()
));
}
}
/**
* Migrate to library-aware Solr schema.
* This endpoint handles the migration from non-library-aware to library-aware indexing.
@@ -185,13 +237,40 @@ public class AdminSearchController {
));
}
logger.info("Adding libraryId field to Solr schema");
// First, add the libraryId field to the schema via Schema API
try {
solrService.addLibraryIdField();
logger.info("libraryId field added successfully to schema");
} catch (Exception e) {
logger.error("Failed to add libraryId field to schema", e);
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"error", "Failed to add libraryId field to schema: " + e.getMessage(),
"details", "The schema must support the libraryId field before migration"
));
}
logger.info("Clearing existing Solr data for library schema migration");
// Note: This assumes the libraryId field has been added to the Solr schema
// Either manually or via schema restart with updated schema files
// Clear existing data that doesn't have libraryId
solrService.recreateIndices();
try {
solrService.recreateIndices();
} catch (Exception e) {
logger.warn("Could not recreate indices (expected in production): {}", e.getMessage());
// In production, just clear the data instead
try {
solrService.clearAllDocuments();
logger.info("Cleared all documents from Solr cores");
} catch (Exception clearError) {
logger.error("Failed to clear documents", clearError);
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"error", "Failed to clear existing data: " + clearError.getMessage()
));
}
}
// Get all data and reindex with library context
List<Story> allStories = storyService.findAllWithAssociations();

View File

@@ -379,6 +379,128 @@ public class SolrService {
}
}
/**
* Add libraryId field to Solr schema via Schema API
* This is required for library-aware indexing in production environments
*/
public void addLibraryIdField() throws Exception {
if (!isAvailable()) {
throw new IllegalStateException("Solr is not available");
}
try {
// Check if libraryId field already exists
if (hasLibraryIdField()) {
logger.info("libraryId field already exists in schema");
return;
}
logger.info("Adding libraryId field to Solr schema via Schema API");
// Add field to stories core
try {
var storiesRequest = new org.apache.solr.client.solrj.request.schema.SchemaRequest.AddField(
Map.of(
"name", "libraryId",
"type", "string",
"indexed", true,
"stored", true,
"required", false
)
);
var storiesResponse = storiesRequest.process(solrClient, properties.getCores().getStories());
logger.info("Added libraryId field to stories core: {}", storiesResponse.getStatus());
} catch (Exception e) {
if (e.getMessage() != null && e.getMessage().contains("already exists")) {
logger.info("libraryId field already exists in stories core");
} else {
throw e;
}
}
// Add field to authors core
try {
var authorsRequest = new org.apache.solr.client.solrj.request.schema.SchemaRequest.AddField(
Map.of(
"name", "libraryId",
"type", "string",
"indexed", true,
"stored", true,
"required", false
)
);
var authorsResponse = authorsRequest.process(solrClient, properties.getCores().getAuthors());
logger.info("Added libraryId field to authors core: {}", authorsResponse.getStatus());
} catch (Exception e) {
if (e.getMessage() != null && e.getMessage().contains("already exists")) {
logger.info("libraryId field already exists in authors core");
} else {
throw e;
}
}
logger.info("Successfully added libraryId field to both cores");
} catch (Exception e) {
logger.error("Failed to add libraryId field to schema", e);
throw new RuntimeException("Failed to add libraryId field to schema: " + e.getMessage(), e);
}
}
/**
* Check if libraryId field exists in the schema
*/
public boolean hasLibraryIdField() {
try {
var request = new org.apache.solr.client.solrj.request.schema.SchemaRequest.Field("libraryId");
request.process(solrClient, properties.getCores().getStories());
return true;
} catch (Exception e) {
// Field doesn't exist or other error
return false;
}
}
/**
* Clear all documents from both stories and authors cores
* Used for migration when core recreation isn't possible
*/
public void clearAllDocuments() throws Exception {
if (!isAvailable()) {
throw new IllegalStateException("Solr is not available");
}
try {
logger.info("Clearing all documents from Solr cores");
// Clear stories core
try {
solrClient.deleteByQuery(properties.getCores().getStories(), "*:*",
properties.getCommit().getCommitWithin());
logger.info("Cleared all documents from stories core");
} catch (Exception e) {
logger.error("Failed to clear stories core", e);
throw e;
}
// Clear authors core
try {
solrClient.deleteByQuery(properties.getCores().getAuthors(), "*:*",
properties.getCommit().getCommitWithin());
logger.info("Cleared all documents from authors core");
} catch (Exception e) {
logger.error("Failed to clear authors core", e);
throw e;
}
logger.info("Successfully cleared all documents from both cores");
} catch (Exception e) {
logger.error("Failed to clear all documents", e);
throw new RuntimeException("Failed to clear all documents: " + e.getMessage(), e);
}
}
// ===============================
// SEARCH OPERATIONS
// ===============================