Skip to content

Commit 010322f

Browse files
committed
refactor fmd change check, use in permissions as well
1 parent 2392a55 commit 010322f

3 files changed

Lines changed: 98 additions & 100 deletions

File tree

src/main/java/edu/harvard/iq/dataverse/FileMetadata.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,15 @@
6767
*/
6868
@Table(indexes = {@Index(columnList="datafile_id"), @Index(columnList="datasetversion_id")} )
6969
@NamedNativeQuery(
70-
name = "FileMetadata.compareFileMetadata",
70+
name = "FileMetadata.getDatafilesWithChangedMetadata",
7171
query = "WITH fm_categories AS (" +
7272
" SELECT fmd.filemetadatas_id, " +
7373
" STRING_AGG(dfc.name, ',' ORDER BY dfc.name) AS categories " +
7474
" FROM FileMetadata_DataFileCategory fmd " +
7575
" JOIN DataFileCategory dfc ON fmd.filecategories_id = dfc.id " +
7676
" GROUP BY fmd.filemetadatas_id " +
7777
") " +
78-
"SELECT fm1.id " +
78+
"SELECT fm1.datafile_id " +
7979
"FROM FileMetadata fm1 " +
8080
"LEFT JOIN FileMetadata fm2 ON fm1.datafile_id = fm2.datafile_id " +
8181
" AND fm2.datasetversion_id = ?1 " +
@@ -93,11 +93,11 @@
9393
" ) " +
9494
" ) " +
9595
" )",
96-
resultSetMapping = "IdToLongMapping"
96+
resultSetMapping = "IdToIntegerMapping"
9797
)
9898
/* When this mapping was to Long.class, Postgres was still returning an Integer, causing indexing failures - see #11776 */
9999
@SqlResultSetMapping(
100-
name = "IdToLongMapping",
100+
name = "IdToIntegerMapping",
101101
columns = @ColumnResult(name = "id", type = Integer.class)
102102
)
103103
@Entity

src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -602,10 +602,22 @@ private void doIndexDataset(Dataset dataset, boolean doNormalSolrDocCleanUp) thr
602602
writeDebugInfo(debug, dataset);
603603
}
604604
if (doNormalSolrDocCleanUp) {
605+
List<String> solrIdsOfPermissionDocsToDelete = new ArrayList<>();
605606
try {
606607
solrIdsOfDocsToDelete = findFilesOfParentDataset(dataset.getId());
607608
logger.fine("Existing file docs: " + String.join(", ", solrIdsOfDocsToDelete));
608609
if (!solrIdsOfDocsToDelete.isEmpty()) {
610+
if (!latestVersion.isDraft()) {
611+
// For draft datasets after a published version, we're not reindexing the files unless their metadata changes
612+
// Therefore, to make sure their
613+
for (String fileDocId : solrIdsOfDocsToDelete) {
614+
if (!fileDocId.endsWith(draftSuffix)) {
615+
solrIdsOfPermissionDocsToDelete.add(fileDocId + draftSuffix + discoverabilityPermissionSuffix);
616+
}
617+
}
618+
619+
logger.fine("Existing permission docs: " + String.join(", ", solrIdsOfPermissionDocsToDelete));
620+
}
609621
// We keep the latest version's docs unless it is deaccessioned and there is no
610622
// published/released version
611623
// So skip the loop removing those docs from the delete list except in that case
@@ -649,7 +661,7 @@ private void doIndexDataset(Dataset dataset, boolean doNormalSolrDocCleanUp) thr
649661
logger.fine("Solr docs to delete: " + String.join(", ", solrIdsOfDocsToDelete));
650662

651663
if (!solrIdsOfDocsToDelete.isEmpty()) {
652-
List<String> solrIdsOfPermissionDocsToDelete = new ArrayList<>();
664+
653665
for (String file : solrIdsOfDocsToDelete) {
654666
// Also remove associated permission docs
655667
solrIdsOfPermissionDocsToDelete.add(file + discoverabilityPermissionSuffix);
@@ -1416,7 +1428,7 @@ public SolrInputDocuments toSolrDocs(IndexableDataset indexableDataset, Set<Long
14161428
long maxSize = maxFTIndexingSize != null ? maxFTIndexingSize.longValue() : Long.MAX_VALUE;
14171429

14181430
List<String> filesIndexed = new ArrayList<>();
1419-
final List<Long> changedFileMetadataIds = new ArrayList<>();
1431+
final List<Long> changedFileIds = new ArrayList<>();
14201432
if (datasetVersion != null) {
14211433
List<FileMetadata> fileMetadatas = datasetVersion.getFileMetadatas();
14221434
List<FileMetadata> rfm = new ArrayList<>();
@@ -1427,42 +1439,17 @@ public SolrInputDocuments toSolrDocs(IndexableDataset indexableDataset, Set<Long
14271439
fileMap.put(released.getDataFile().getId(), released);
14281440
}
14291441

1430-
Query query = em.createNamedQuery("FileMetadata.compareFileMetadata", Long.class);
1431-
query.setParameter(1, dataset.getReleasedVersion().getId());
1432-
query.setParameter(2, datasetVersion.getId());
1433-
1434-
/*
1435-
* When the query was configured to return Long, it was returning Integer. The query has been changed to return Integer now. The code here is robust if that changes in the future.
1436-
*/
1437-
List<Object> queryResults = query.getResultList();
1438-
for (Object result : queryResults) {
1439-
if (result != null) {
1440-
// Ensure we're adding Long objects to the list
1441-
if (result instanceof Integer intResult) {
1442-
logger.finest("Converted Integer result to Long: " + result);
1443-
changedFileMetadataIds.add(Long.valueOf(intResult));
1444-
} else if (result instanceof Long longResult) {
1445-
// Already a Long, add directly
1446-
logger.finest("Added existing Long to list: " + result);
1447-
changedFileMetadataIds.add(longResult);
1448-
} else {
1449-
// If it's not a Long, convert it to one via String
1450-
try {
1451-
changedFileMetadataIds.add(Long.valueOf(result.toString()));
1452-
logger.finest("Converted non-Long result to Long: " + result + " of type " + result.getClass().getName());
1453-
} catch (NumberFormatException e) {
1454-
logger.warning("Could not convert query result to Long: " + result);
1455-
}
1456-
}
1457-
}
1458-
}
1442+
solrIndexService.populateChangedFileIds(
1443+
dataset.getReleasedVersion().getId(),
1444+
datasetVersion.getId(),
1445+
changedFileIds);
14591446
logger.fine(
14601447
"We are indexing a draft version of a dataset that has a released version. We'll be checking file metadatas if they are exact clones of the released versions.");
14611448
} else if (datasetVersion.isDraft()) {
14621449
// Add all file metadata ids to changedFileMetadataIds
1463-
changedFileMetadataIds.addAll(
1450+
changedFileIds.addAll(
14641451
fileMetadatas.stream()
1465-
.map(FileMetadata::getId)
1452+
.map(fm -> fm.getDataFile().getId())
14661453
.collect(Collectors.toList())
14671454
);
14681455
}
@@ -1526,7 +1513,7 @@ public SolrInputDocuments toSolrDocs(IndexableDataset indexableDataset, Set<Long
15261513
}
15271514
boolean indexThisFile = false;
15281515

1529-
if (indexThisMetadata && (isReleasedVersion || changedFileMetadataIds.contains(fileMetadata.getId()))) {
1516+
if (indexThisMetadata && (isReleasedVersion || changedFileIds.contains(datafile.getId()))) {
15301517
indexThisFile = true;
15311518
} else if (indexThisMetadata) {
15321519
// Draft version, file is not new or all file metadata matches the released version

src/main/java/edu/harvard/iq/dataverse/search/SolrIndexServiceBean.java

Lines changed: 73 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import jakarta.json.JsonObjectBuilder;
3737
import jakarta.persistence.EntityManager;
3838
import jakarta.persistence.PersistenceContext;
39+
import jakarta.persistence.Query;
3940

4041
import org.apache.solr.client.solrj.SolrServerException;
4142
import org.apache.solr.common.SolrInputDocument;
@@ -410,41 +411,100 @@ public void indexDatasetBatchInNewTransaction(List<Long> datasetIds, final int[]
410411
indexPermissionsForOneDvObject(dataset);
411412

412413
// Process files for this dataset
413-
for (DatasetVersion version : datasetVersionsToBuildCardsFor(dataset)) {
414-
processDatasetVersionFiles(version, fileCounter, fileQueryMin, versions.size()>1);
414+
List<DatasetVersion> versions = datasetVersionsToBuildCardsFor(dataset);
415+
final List<Long> changedFileIds = new ArrayList<>();
416+
if(versions.size()>1) {
417+
Long releasedVersionId = versions.get(versions.get(0).isReleased() ? 0 : 1).getId();
418+
Long draftVersionId = versions.get(versions.get(0).isReleased() ? 1 : 0).getId();
419+
420+
populateChangedFileIds(
421+
releasedVersionId,
422+
draftVersionId,
423+
changedFileIds
424+
);
425+
}
426+
for (DatasetVersion version : versions) {
427+
processDatasetVersionFiles(version, fileCounter, fileQueryMin, (versions.size()>1 && version.isDraft()) ? changedFileIds : null);
415428
}
416429
}
417430
}
418431
}
419432

420433
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
421434
public void indexDatasetFilesInNewTransaction(List<DatasetVersion> versions, final int[] fileCounter, int fileQueryMin) {
435+
final List<Long> changedFileIds = new ArrayList<>();
436+
if(versions.size()>1) {
437+
Long releasedVersionId = versions.get(versions.get(0).isReleased() ? 0 : 1).getId();
438+
Long draftVersionId = versions.get(versions.get(0).isReleased() ? 1 : 0).getId();
439+
440+
populateChangedFileIds(
441+
releasedVersionId,
442+
draftVersionId,
443+
changedFileIds
444+
);
445+
}
422446
for (DatasetVersion version : versions) {
423447
// The version object is detached, but its fileMetadatas collection is already loaded.
424448
// We only need its ID and state, which are available.
425-
processDatasetVersionFiles(version, fileCounter, fileQueryMin, versions.size()>1);
449+
processDatasetVersionFiles(version, fileCounter, fileQueryMin, (versions.size()>1 && version.isDraft()) ? changedFileIds : null);
426450
}
427451
}
428452

453+
/**
454+
* Retrieves the IDs of file metadatas that have changed between the released version
455+
* and the draft version of a dataset.
456+
*
457+
* @param releasedVersionId the ID of the released dataset version
458+
* @param draftVersionId the ID of the draft dataset version
459+
* @param changedFileMetadataIds the list to populate with changed file metadata IDs
460+
*/
461+
protected void populateChangedFileIds(Long releasedVersionId, Long draftVersionId, List<Long> changedFileIds) {
462+
Query query = em.createNamedQuery("FileMetadata.getDatafilesWithChangedMetadata", Long.class);
463+
query.setParameter(1, releasedVersionId);
464+
query.setParameter(2, draftVersionId);
465+
466+
/*
467+
* When the query was configured to return Long, it was returning Integer.
468+
* The query has been changed to return Integer now. The code here is robust
469+
* if that changes in the future.
470+
*/
471+
List<Object> queryResults = query.getResultList();
472+
for (Object result : queryResults) {
473+
if (result != null) {
474+
// Ensure we're adding Long objects to the list
475+
if (result instanceof Integer intResult) {
476+
logger.finest("Converted Integer result to Long: " + result);
477+
changedFileIds.add(Long.valueOf(intResult));
478+
} else if (result instanceof Long longResult) {
479+
// Already a Long, add directly
480+
logger.finest("Added existing Long to list: " + result);
481+
changedFileIds.add(longResult);
482+
} else {
483+
// If it's not a Long, convert it to one via String
484+
try {
485+
changedFileIds.add(Long.valueOf(result.toString()));
486+
logger.finest("Converted non-Long result to Long: " + result + " of type " + result.getClass().getName());
487+
} catch (NumberFormatException e) {
488+
logger.warning("Could not convert query result to Long: " + result);
489+
}
490+
}
491+
}
492+
}
493+
}
494+
429495
private void processDatasetVersionFiles(DatasetVersion version,
430-
final int[] fileCounter, int fileQueryMin, boolean isReleased) {
496+
final int[] fileCounter, int fileQueryMin, List<Long> changedFileIds) {
431497
List<String> cachedPerms = searchPermissionsService.findDatasetVersionPerms(version);
432498
String solrIdEnd = getDatasetOrDataFileSolrEnding(version.getVersionState());
433499
Long versionId = version.getId();
434500
List<DataFileProxy> filesToReindexAsBatch = new ArrayList<>();
435501

436502
// If the version is draft and there is a released version,
437-
// we only need perm docs for the files with filemetadata changes == those with _draft solr docs already
438-
Set<Long> fileIdsToReindex = null;
439-
if (version.getVersionState().equals(DatasetVersion.VersionState.DRAFT) && isReleased) {
440-
fileIdsToReindex = getFileIdsWithSolrDocs(versionId);
441-
logger.fine("Found " + fileIdsToReindex.size() + " files with draft Solr docs for version " + versionId);
442-
}
503+
// we only need perm docs for the files with filemetadata changes == those in changedFileMetadataIds
443504

444505
// Process files in batches of 100
445506
int batchSize = 100;
446507

447-
final Set<Long> finalFileIdsToReindex = fileIdsToReindex;
448508
if (dataFileService.findCountByDatasetVersionId(version.getId()).intValue() > fileQueryMin) {
449509
// For large datasets, use a more efficient SQL query
450510
// ToDo - only get the ones in finalFileIdsToReindex
@@ -453,7 +513,7 @@ private void processDatasetVersionFiles(DatasetVersion version,
453513
// Process files in batches to avoid memory issues
454514
fileStream.forEach(fileInfo -> {
455515
// Only add files that need reindexing
456-
if (finalFileIdsToReindex == null || finalFileIdsToReindex.contains(fileInfo.getFileId())) {
516+
if (changedFileIds == null || changedFileIds.contains(fileInfo.getFileId())) {
457517
filesToReindexAsBatch.add(fileInfo);
458518
fileCounter[0]++;
459519

@@ -470,7 +530,7 @@ private void processDatasetVersionFiles(DatasetVersion version,
470530
for (FileMetadata fmd : version.getFileMetadatas()) {
471531
// Only add files that need reindexing
472532
DataFileProxy fileProxy = new DataFileProxy(fmd);
473-
if (finalFileIdsToReindex == null || finalFileIdsToReindex.contains(fileProxy.getFileId())) {
533+
if (changedFileIds == null || changedFileIds.contains(fileProxy.getFileId())) {
474534
filesToReindexAsBatch.add(fileProxy);
475535
fileCounter[0]++;
476536

@@ -510,55 +570,6 @@ private void reindexFilesInBatches(List<DataFileProxy> filesToReindexAsBatch, Li
510570
}
511571
}
512572

513-
/**
514-
* Queries Solr to find file IDs that have draft documents for the given dataset version.
515-
* This is used to optimize permission reindexing by only processing files that have
516-
* metadata changes in the draft version.
517-
*
518-
* @param datasetVersionId The ID of the dataset version
519-
* @return A set of file IDs that have Solr documents associated with this version
520-
*/
521-
private Set<Long> getFileIdsWithSolrDocs(Long datasetVersionId) {
522-
Set<Long> fileIds = new HashSet<>();
523-
524-
try {
525-
SolrQuery solrQuery = new SolrQuery();
526-
527-
// Query for files in this specific version with draft suffix
528-
solrQuery.setQuery("*:*");
529-
solrQuery.addFilterQuery(SearchFields.TYPE + ":" + SearchConstants.FILES);
530-
solrQuery.addFilterQuery(SearchFields.DATASET_VERSION_ID + ":" + datasetVersionId);
531-
532-
// Only return the entity ID field
533-
solrQuery.setFields(SearchFields.ENTITY_ID);
534-
535-
// We want all matching documents
536-
solrQuery.setRows(Integer.MAX_VALUE);
537-
538-
logger.fine("Solr query to find draft files: " + solrQuery);
539-
540-
QueryResponse queryResponse = solrClientService.getSolrClient().query(solrQuery);
541-
SolrDocumentList docs = queryResponse.getResults();
542-
543-
for (SolrDocument doc : docs) {
544-
Long entityId = (Long) doc.getFieldValue(SearchFields.ENTITY_ID);
545-
if (entityId != null) {
546-
fileIds.add(entityId);
547-
}
548-
}
549-
550-
logger.fine("Found " + fileIds.size() + " files with draft Solr docs for version " + datasetVersionId);
551-
552-
} catch (SolrServerException | IOException ex) {
553-
logger.log(Level.WARNING, "Error querying Solr for draft file IDs for version " + datasetVersionId +
554-
". Will reindex all files as fallback.", ex);
555-
// Return null to indicate we should process all files
556-
return null;
557-
}
558-
559-
return fileIds;
560-
}
561-
562573
public IndexResponse deleteMultipleSolrIds(List<String> solrIdsToDelete) {
563574
if (solrIdsToDelete.isEmpty()) {
564575
return new IndexResponse("nothing to delete");

0 commit comments

Comments
 (0)