From 49ff6ac0191dc0a0a0daaaa0398ac5627c995b49 Mon Sep 17 00:00:00 2001 From: Kodi Date: Tue, 6 Oct 2026 17:20:44 -0700 Subject: [PATCH] DIS-2941: Editing Volume Numbers in a Series Does Not Save Added new variable "userDefinedVolume" to track whether or not volume information should be overwritten during indexing. Gets set to true if user edits the volume field on an entry Gets set back to false if the user defined value is equal to the value found during indexing Update GroupedWorkIndexer.java: - When checking if we should add series member with volume, check if we are looking at an existing series member with a user defined volume in addition to checking if the volume exists The following needs to be true to add a new series member with volume (to avoid duplicates from user-defined members): - The volume does not exist in the current series members - The series member grouped work permanent ID equals the grouped work permanent id being indexed and does not have a user defined volume OR the grouped work permanent IDs don't match If we go to updating an existing member instead of adding a new one, we will change the userDefinedVolume variable to false if the existing volumes contains the current volume being indexed For deletion, we will no longer remove series members that have a user-defined volume value Update release notes Tested on my local instance of Aspen on individual grouped works and series Will need larger testing batches (nightly index before and after view) to ensure desired series indexing functionality remains --- .../reindexer/GroupedWorkIndexer.java | 12 ++++++---- .../reindexer/SeriesMember.java | 22 ++++++++++++++++--- .../reindexer/SeriesMemberVolume.java | 8 ++++++- code/web/release_notes/26.10.00/DIS-2941.yaml | 12 ++++++++++ .../version_updates/26.10.00.php | 9 ++++++++ code/web/sys/Series/SeriesMember.php | 11 ++++++++++ 6 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 code/web/release_notes/26.10.00/DIS-2941.yaml diff --git a/code/reindexer/src/org/aspen_discovery/reindexer/GroupedWorkIndexer.java b/code/reindexer/src/org/aspen_discovery/reindexer/GroupedWorkIndexer.java index c945befbc2..1e1363bdaa 100644 --- a/code/reindexer/src/org/aspen_discovery/reindexer/GroupedWorkIndexer.java +++ b/code/reindexer/src/org/aspen_discovery/reindexer/GroupedWorkIndexer.java @@ -373,7 +373,7 @@ public GroupedWorkIndexer(String serverName, Connection dbConn, Ini configIni, b getV2SeriesStmt = dbConn.prepareStatement("SELECT * from series where seriesPermanentId = ?", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); checkIfSeriesMemberExistsStmt = dbConn.prepareStatement("SELECT * from series_member where seriesId = ? AND groupedWorkPermanentId = ?", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - getSeriesMemberStmt = dbConn.prepareStatement("SELECT sm.id as seriesMemberId, sm.seriesId, s.seriesPermanentId, s.version, s.groupedWorkSeriesTitle, s.author, s.seriesLanguage, s.isIndexed, sm.volume, sm.priorityScore, sm.deleted, sm.userAdded FROM series_member AS sm LEFT JOIN series AS s ON sm.seriesId = s.id WHERE groupedWorkPermanentId = ?", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + getSeriesMemberStmt = dbConn.prepareStatement("SELECT sm.id as seriesMemberId, sm.seriesId, s.seriesPermanentId, s.version, s.groupedWorkSeriesTitle, s.author, s.seriesLanguage, s.isIndexed, sm.volume, sm.priorityScore, sm.deleted, sm.userAdded, sm.groupedWorkPermanentId, sm.userDefinedVolume FROM series_member AS sm LEFT JOIN series AS s ON sm.seriesId = s.id WHERE groupedWorkPermanentId = ?", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); addSeriesStmt = dbConn.prepareStatement("INSERT INTO series (displayName, audience, created, dateUpdated, author, groupedWorkSeriesTitle, version) VALUES (?, ?, ?, ?, ?, ?, 1)", PreparedStatement.RETURN_GENERATED_KEYS); addSeriesV2Stmt = dbConn.prepareStatement("INSERT INTO series (displayName, audience, created, dateUpdated, author, groupedWorkSeriesTitle, seriesPermanentId, seriesLanguage, version) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 2)", PreparedStatement.RETURN_GENERATED_KEYS); addSeriesMemberStmt = dbConn.prepareStatement("INSERT INTO series_member (seriesId, isPlaceholder, groupedWorkPermanentId, volume, pubDate, displayName, author, description, weight, priorityScore) VALUES (?, 0, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id)", PreparedStatement.RETURN_GENERATED_KEYS); @@ -1596,7 +1596,7 @@ private void updateSeriesDataForWork(AbstractGroupedWorkSolr groupedWork) { key = seriesMember.getSeriesPermanentId(); } if (seriesMembersInDb.containsKey(key)) { - seriesMembersInDb.get(key).addVolume(seriesMemberRS.getString("volume"), seriesMemberRS.getBoolean("deleted"), seriesMemberRS.getBoolean("userAdded")); + seriesMembersInDb.get(key).addVolume(seriesMemberRS.getString("volume"), seriesMemberRS.getBoolean("deleted"), seriesMemberRS.getBoolean("userAdded"), seriesMemberRS.getBoolean("userDefinedVolume")); }else{ seriesMembersInDb.put(key, seriesMember); } @@ -1907,10 +1907,14 @@ private void addMemberToSeries(long seriesId, SeriesInfo seriesInfo, AbstractGro private void updateSeriesMembers(long seriesId, SeriesInfo seriesInfo, SeriesMember seriesMember, AbstractGroupedWorkSolr groupedWork, long timeNow) { for (String volume : seriesInfo.getVolumes()) { Set existingVolumes = seriesMember.getVolumes(); - if (!existingVolumes.contains(volume)) { + boolean isSameWorkWithUserDefinedVolume = seriesMember.getMemberGroupedWorkPermanentId().equals(groupedWork.id) && seriesMember.hasUserDefinedVolume(); + if (!existingVolumes.contains(volume) && !isSameWorkWithUserDefinedVolume) { addSeriesMemberWithVolume(seriesId, seriesInfo, volume, groupedWork, timeNow, 0); }else{ seriesMember.setVolumeFoundInIndex(volume); + if (existingVolumes.contains(volume)){ //if volume info matches, remove user-defined volume tag + seriesMember.setUserDefinedVolume(false); + } //Update priority score as needed if (!Objects.equals(seriesInfo.getPriorityScore(), seriesMember.getPriorityScore())) { try { @@ -1936,7 +1940,7 @@ private void updateSeriesMembers(long seriesId, SeriesInfo seriesInfo, SeriesMem //Delete any series members that no longer exist (with the volume) boolean valuesWereDeleted = false; for (SeriesMemberVolume volume : seriesMember.getSeriesVolumes()) { - if (!volume.isFoundInIndex() && !volume.isUserAdded()) { + if (!volume.isFoundInIndex() && !volume.isUserAdded() && !volume.isUserDefinedVolume()) { deleteSeriesMember(seriesMember.getSeriesId(), groupedWork.getId(), volume.getVolume()); valuesWereDeleted = true; } diff --git a/code/reindexer/src/org/aspen_discovery/reindexer/SeriesMember.java b/code/reindexer/src/org/aspen_discovery/reindexer/SeriesMember.java index 2fe29eef9c..eb16e687f6 100644 --- a/code/reindexer/src/org/aspen_discovery/reindexer/SeriesMember.java +++ b/code/reindexer/src/org/aspen_discovery/reindexer/SeriesMember.java @@ -15,6 +15,7 @@ public class SeriesMember { private final String seriesPermanentId; private final String groupedWorkSeriesTitle; private String author; + private String groupedWorkPermanentId; @SuppressWarnings("FieldCanBeLocal") private final String seriesLanguage; //A list of volumes found in the database with a flag for if it was found in the current index @@ -24,6 +25,7 @@ public class SeriesMember { private int version; private boolean foundInCurrentIndex = false; private boolean deleted; + private boolean userDefinedVolume; /** * Constructor for use when loading series that are already linked to a Grouped Work via a Series Member @@ -32,13 +34,15 @@ public SeriesMember(ResultSet seriesMemberRS) throws SQLException { this.seriesId = seriesMemberRS.getLong("seriesId"); this.seriesPermanentId = seriesMemberRS.getString("seriesPermanentId"); this.groupedWorkSeriesTitle = seriesMemberRS.getString("groupedWorkSeriesTitle"); + this.groupedWorkPermanentId = seriesMemberRS.getString("groupedWorkPermanentId"); this.author = seriesMemberRS.getString("author"); this.seriesLanguage = seriesMemberRS.getString("seriesLanguage"); - this.volumes.put(seriesMemberRS.getString("volume"), new SeriesMemberVolume(seriesMemberRS.getString("volume"), seriesMemberRS.getBoolean("deleted"), seriesMemberRS.getBoolean("userAdded"))); + this.volumes.put(seriesMemberRS.getString("volume"), new SeriesMemberVolume(seriesMemberRS.getString("volume"), seriesMemberRS.getBoolean("deleted"), seriesMemberRS.getBoolean("userAdded"), seriesMemberRS.getBoolean("userDefinedVolume"))); this.priorityScore = seriesMemberRS.getInt("priorityScore"); this.isIndexed = seriesMemberRS.getBoolean("isIndexed"); this.version = seriesMemberRS.getInt("version"); this.deleted = seriesMemberRS.getBoolean("deleted"); + this.userDefinedVolume = seriesMemberRS.getBoolean("userDefinedVolume"); } /** @@ -85,6 +89,14 @@ public String getSeriesPermanentId() { return seriesPermanentId; } + public String getMemberGroupedWorkPermanentId() { + return groupedWorkPermanentId; + } + + public boolean hasUserDefinedVolume() { + return userDefinedVolume; + } + private String normalizedSeriesName; public String getNormalizedSeriesName() { if (normalizedSeriesName == null) { @@ -135,6 +147,10 @@ public void setVolumeFoundInIndex(String volumeId) { } } + public void setUserDefinedVolume(boolean userDefinedVolume) { + this.userDefinedVolume = userDefinedVolume; + } + public boolean isIndexed() { return isIndexed; } @@ -151,8 +167,8 @@ public void setVersion(int version) { this.version = version; } - public void addVolume(String volume, boolean deleted, boolean userAdded) { - this.volumes.put(volume, new SeriesMemberVolume(volume, deleted, userAdded)); + public void addVolume(String volume, boolean deleted, boolean userAdded, boolean userDefinedVolume) { + this.volumes.put(volume, new SeriesMemberVolume(volume, deleted, userAdded, userDefinedVolume)); } public boolean isDeleted() { diff --git a/code/reindexer/src/org/aspen_discovery/reindexer/SeriesMemberVolume.java b/code/reindexer/src/org/aspen_discovery/reindexer/SeriesMemberVolume.java index 81e4b5c26f..ff93cc661f 100644 --- a/code/reindexer/src/org/aspen_discovery/reindexer/SeriesMemberVolume.java +++ b/code/reindexer/src/org/aspen_discovery/reindexer/SeriesMemberVolume.java @@ -5,15 +5,17 @@ public class SeriesMemberVolume { private boolean deleted; private boolean userAdded; private boolean foundInIndex; + private boolean userDefinedVolume; public SeriesMemberVolume() { } - public SeriesMemberVolume(String volume, boolean deleted, boolean userAdded) { + public SeriesMemberVolume(String volume, boolean deleted, boolean userAdded, boolean userDefinedVolume) { this.volume = volume; this.deleted = deleted; this.userAdded = userAdded; + this.userDefinedVolume = userDefinedVolume; } public String getVolume() { @@ -36,6 +38,10 @@ public boolean isUserAdded() { return userAdded; } + public boolean isUserDefinedVolume() { + return userDefinedVolume; + } + public void setUserAdded(boolean userAdded) { this.userAdded = userAdded; } diff --git a/code/web/release_notes/26.10.00/DIS-2941.yaml b/code/web/release_notes/26.10.00/DIS-2941.yaml new file mode 100644 index 0000000000..9c5ddf5142 --- /dev/null +++ b/code/web/release_notes/26.10.00/DIS-2941.yaml @@ -0,0 +1,12 @@ +Jira: DIS-2941 +Type: Bug +Developer: Kodi Lein (KL) +ContributingOrg: Grove For Libraries +Sponsor: HMCPL +GroveRelease: 26.10.00 +CommunityRelease: 26.11.00 +Component: Series +Description: + - Fix issue where edited volume information for V2 series gets overwritten during indexing. +NewSettings: +NewPermissions: \ No newline at end of file diff --git a/code/web/sys/DBMaintenance/version_updates/26.10.00.php b/code/web/sys/DBMaintenance/version_updates/26.10.00.php index 7a4b8b806b..78e50cc49e 100644 --- a/code/web/sys/DBMaintenance/version_updates/26.10.00.php +++ b/code/web/sys/DBMaintenance/version_updates/26.10.00.php @@ -41,6 +41,15 @@ function getUpdates26_10_00(): array { //add_oauth2_user_consents_table //kodi + 'series_member_user_defined_volume' => [ + 'title' => 'User Defined Volumes for Series Members (V2 Series)', + 'description' => 'Add column for storing if a series member has a user-defined volume value.', + 'continueOnError' => false, + 'sql' => [ + "ALTER TABLE series_member ADD COLUMN userDefinedVolume TINYINT(1) NOT NULL DEFAULT 0", + ], + ], + //series_member_user_defined_volume //yanjun diff --git a/code/web/sys/Series/SeriesMember.php b/code/web/sys/Series/SeriesMember.php index a37b24c8a9..be400da478 100644 --- a/code/web/sys/Series/SeriesMember.php +++ b/code/web/sys/Series/SeriesMember.php @@ -15,6 +15,7 @@ class SeriesMember extends DataObject { public $weight; public $cover; public $userAdded; + public $userDefinedVolume; public $excluded; public $deleted; /** @noinspection PhpUnused */ @@ -105,6 +106,12 @@ static function getObjectStructure(string $context = ''): array { 'label' => 'User Added', 'readOnly' => true, ], + 'userDefinedVolume' => [ + 'property' => 'userDefinedVolume', + 'type' => 'hidden', + 'label' => 'User Defined Volume', + 'readOnly' => true, + ], 'excluded' => [ 'property' => 'excluded', 'type' => 'checkbox', @@ -120,6 +127,9 @@ static function getObjectStructure(string $context = ''): array { public function update(string $context = '') : int|bool { if ($this->groupedWorkPermanentId) { + if (in_array('volume', $this->_changedFields, true)) { + $this->userDefinedVolume = true; + } $this->isPlaceholder = false; } else { $this->isPlaceholder = true; @@ -142,6 +152,7 @@ public function getNumericColumnNames(): array { 'pubDate', 'isPlaceholder', 'userAdded', + 'userDefinedVolume', 'excluded' ]; }