Skip to content

Commit

Permalink
feat(cn-browse): Implement Indexing and Re-indexing Mechanisms for Ca…
Browse files Browse the repository at this point in the history
…ll-Numbers (#707)

Closes: MSEARCH-864
  • Loading branch information
psmagin authored Dec 5, 2024
1 parent 5cadf05 commit 24bae72
Show file tree
Hide file tree
Showing 26 changed files with 682 additions and 54 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
### Features
* Move Instance sub-entities population from database trigger to code ([MSEARCH-887](https://folio-org.atlassian.net/browse/MSEARCH-887))
* Call Numbers Browse: Implement Database Structure and Logic for Managing Call Numbers ([MSEARCH-862](https://folio-org.atlassian.net/browse/MSEARCH-862))
* Call Numbers Browse: Implement Call Number Browse Config ([MSEARCH-863](https://folio-org.atlassian.net/browse/MSEARCH-863))
* Call Numbers Browse: Implement Indexing and Re-indexing Mechanisms for Call-Numbers ([MSEARCH-864](https://folio-org.atlassian.net/browse/MSEARCH-864))

### Bug fixes
* Remove shelving order calculation for local call-number types
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.folio.search.model.entity;

import java.util.Set;
import org.folio.search.model.index.InstanceSubResource;

public record InstanceCallNumberEntityAgg(
String id,
String fullCallNumber,
String callNumber,
String callNumberPrefix,
String callNumberSuffix,
String callNumberTypeId,
String volume,
String enumeration,
String chronology,
String copyNumber,
Set<InstanceSubResource> instances) { }
16 changes: 16 additions & 0 deletions src/main/java/org/folio/search/model/index/CallNumberResource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.folio.search.model.index;

import java.util.Set;

public record CallNumberResource(
String id,
String fullCallNumber,
String callNumber,
String callNumberPrefix,
String callNumberSuffix,
String callNumberTypeId,
String volume,
String enumeration,
String chronology,
String copyNumber,
Set<InstanceSubResource> instances) { }
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ public class InstanceSubResource {
private Boolean shared;
private int count;
private List<String> typeId;
private List<String> locationId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public enum ReindexEntityType {
SUBJECT("subject", false, true),
CONTRIBUTOR("contributor", false, true),
CLASSIFICATION("classification", false, true),
CALL_NUMBER("call-number", false, true),
CALL_NUMBER("call_number", false, true),
ITEM("item", true, false),
HOLDINGS("holdings", true, false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum ResourceType {
INSTANCE("instance"),
INSTANCE_CONTRIBUTOR("contributor"),
INSTANCE_CLASSIFICATION("instance_classification"),
INSTANCE_CALL_NUMBER("instance_call_number"),
INSTANCE_SUBJECT("instance_subject"),
INSTITUTION("institution"),
ITEM("item"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,13 @@ public FolioIndexOperationResponse indexInstancesById(List<ResourceEvent> resour

private Map<String, List<SearchDocumentBody>> processIndexInstanceEvents(List<ResourceEvent> resourceEvents) {
var indexEvents = extractEventsForDataMove(resourceEvents);
preProcessEvents(indexEvents);
var fetchedInstances = Optional.ofNullable(consortiumTenantExecutor.execute(
() -> resourceFetchService.fetchInstancesByIds(indexEvents)))
.orElse(Collections.emptyList()).stream()
.filter(Objects::nonNull)
.toList();

preProcessEvents(fetchedInstances);
return searchDocumentConverter.convert(fetchedInstances);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
package org.folio.search.service.converter.preprocessor.extractor.impl;

import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static org.apache.commons.collections4.MapUtils.getMap;
import static org.apache.commons.collections4.MapUtils.getString;
import static org.folio.search.utils.CollectionUtils.subtract;
import static org.folio.search.utils.SearchConverterUtils.getNewAsMap;
import static org.folio.search.utils.SearchConverterUtils.getOldAsMap;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.extern.log4j.Log4j2;
import org.folio.search.domain.dto.ResourceEvent;
import org.folio.search.domain.dto.ResourceEventType;
import org.folio.search.domain.dto.TenantConfiguredFeature;
import org.folio.search.model.entity.CallNumberEntity;
import org.folio.search.model.entity.InstanceCallNumberEntity;
import org.folio.search.model.entity.InstanceCallNumberEntityAgg;
import org.folio.search.model.index.CallNumberResource;
import org.folio.search.model.types.ResourceType;
import org.folio.search.service.FeatureConfigService;
import org.folio.search.service.converter.preprocessor.extractor.ChildResourceExtractor;
import org.folio.search.service.reindex.jdbc.CallNumberRepository;
import org.folio.search.utils.CollectionUtils;
import org.folio.search.utils.JsonConverter;
import org.springframework.stereotype.Component;

Expand All @@ -35,35 +45,62 @@ public class CallNumberResourceExtractor extends ChildResourceExtractor {
public static final String ENUMERATION_FIELD = "enumeration";
public static final String COPY_NUMBER_FIELD = "copyNumber";

private final CallNumberRepository repository;
private final JsonConverter jsonConverter;
private final FeatureConfigService featureConfigService;

public CallNumberResourceExtractor(CallNumberRepository repository, JsonConverter jsonConverter,
FeatureConfigService featureConfigService) {
super(repository);
this.repository = repository;
this.jsonConverter = jsonConverter;
this.featureConfigService = featureConfigService;
}

@Override
public List<ResourceEvent> prepareEvents(ResourceEvent resource) {
return List.of();
if (!featureConfigService.isEnabled(TenantConfiguredFeature.BROWSE_CALL_NUMBERS)) {
return Collections.emptyList();
}
var oldCallNumbers = getCallNumberResources(getOldAsMap(resource));
var newCallNumbers = getCallNumberResources(getNewAsMap(resource));

if (oldCallNumbers.equals(newCallNumbers)) {
return emptyList();
}

var tenant = resource.getTenant();
var callNumbersForCreate = subtract(newCallNumbers, oldCallNumbers);
var callNumbersForDelete = subtract(oldCallNumbers, newCallNumbers);

var idsForCreate = toIds(callNumbersForCreate);
var idsForDelete = toIds(callNumbersForDelete);

List<String> idsForFetch = new ArrayList<>();
idsForFetch.addAll(idsForCreate);
idsForFetch.addAll(idsForDelete);

var entityAggList = repository.fetchByIds(idsForFetch);
var list = getResourceEventsForDeletion(idsForDelete, entityAggList, tenant);

var list1 = entityAggList.stream()
.map(entities -> toResourceEvent(entities, tenant))
.toList();
return CollectionUtils.mergeSafelyToList(list, list1);
}

@Override
public List<ResourceEvent> prepareEventsOnSharing(ResourceEvent resource) {
return List.of();
return emptyList();
}

@Override
public boolean hasChildResourceChanges(ResourceEvent event) {
public boolean hasChildResourceChanges(ResourceEvent resource) {
if (!featureConfigService.isEnabled(TenantConfiguredFeature.BROWSE_CALL_NUMBERS)) {
return false;
}
var oldAsMap = getOldAsMap(event);
var newAsMap = getNewAsMap(event);
var oldCallNumber = constructEntity(oldAsMap);
var newCallNumber = constructEntity(newAsMap);
var oldCallNumber = toCallNumberEntity(getOldAsMap(resource));
var newCallNumber = toCallNumberEntity(getNewAsMap(resource));
return !oldCallNumber.equals(newCallNumber);
}

Expand Down Expand Up @@ -93,6 +130,74 @@ protected Map<String, Object> constructEntity(Map<String, Object> entityProperti
if (!featureConfigService.isEnabled(TenantConfiguredFeature.BROWSE_CALL_NUMBERS)) {
return Collections.emptyMap();
}
return toCallNumberEntity(entityProperties)
.map(jsonConverter::convertToMap)
.orElse(Collections.emptyMap());
}

@Override
protected String childrenFieldName() {
return "";
}

@Override
protected Set<Map<String, Object>> getChildResources(Map<String, Object> event) {
return Set.of(event);
}

private Set<CallNumberEntity> getCallNumberResources(Map<String, Object> event) {
return toCallNumberEntity(event)
.map(Set::of)
.orElse(emptySet());
}

private List<String> toIds(Set<CallNumberEntity> subtract) {
return subtract.stream()
.map(CallNumberEntity::getId)
.collect(Collectors.toCollection(ArrayList::new));
}

private List<ResourceEvent> getResourceEventsForDeletion(List<String> idsForDelete,
List<InstanceCallNumberEntityAgg> entityAggList,
String tenant) {
var notFoundEntitiesForDelete = new ArrayList<>(idsForDelete);
var iterator = notFoundEntitiesForDelete.iterator();
while (iterator.hasNext()) {
var callNumber = iterator.next();
for (var agg : entityAggList) {
if (agg.id().equals(callNumber)) {
iterator.remove();
}
}
}

return notFoundEntitiesForDelete.stream()
.map(callNumberId -> toResourceDeleteEvent(callNumberId, tenant))
.toList();
}

private ResourceEvent toResourceDeleteEvent(String id, String tenant) {
return new ResourceEvent()
.id(id)
.tenant(tenant)
.resourceName(ResourceType.INSTANCE_CALL_NUMBER.getName())
.type(ResourceEventType.DELETE);
}

private ResourceEvent toResourceEvent(InstanceCallNumberEntityAgg source, String tenant) {
var id = source.id();
var resource = new CallNumberResource(id, source.fullCallNumber(), source.callNumber(),
source.callNumberPrefix(), source.callNumberSuffix(), source.callNumberTypeId(), source.volume(),
source.enumeration(), source.chronology(), source.copyNumber(), source.instances());
return new ResourceEvent()
.id(id)
.tenant(tenant)
.resourceName(ResourceType.INSTANCE_CALL_NUMBER.getName())
.type(ResourceEventType.UPDATE)
._new(jsonConverter.convertToMap(resource));
}

private Optional<CallNumberEntity> toCallNumberEntity(Map<String, Object> entityProperties) {
var callNumberComponents = getCallNumberComponents(entityProperties);
var callNumber = getString(callNumberComponents, CALL_NUMBER_FIELD);
if (callNumber != null) {
Expand All @@ -106,9 +211,9 @@ protected Map<String, Object> constructEntity(Map<String, Object> entityProperti
.enumeration(getString(entityProperties, ENUMERATION_FIELD))
.copyNumber(getString(entityProperties, COPY_NUMBER_FIELD))
.build();
return jsonConverter.convertToMap(callNumberEntity);
return Optional.of(callNumberEntity);
}
return Collections.emptyMap();
return Optional.empty();
}

@SuppressWarnings("unchecked")
Expand All @@ -117,14 +222,4 @@ private Map<String, Object> getCallNumberComponents(Map<String, Object> entityPr
Collections.<String, Object>emptyMap());
}

@Override
protected String childrenFieldName() {
return "";
}

@Override
protected Set<Map<String, Object>> getChildResources(Map<String, Object> event) {
return Set.of(event);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ public final class ReindexConstants {
ReindexEntityType.HOLDINGS, ResourceType.HOLDINGS,
ReindexEntityType.SUBJECT, ResourceType.INSTANCE_SUBJECT,
ReindexEntityType.CLASSIFICATION, ResourceType.INSTANCE_CLASSIFICATION,
ReindexEntityType.CONTRIBUTOR, ResourceType.INSTANCE_CONTRIBUTOR
ReindexEntityType.CONTRIBUTOR, ResourceType.INSTANCE_CONTRIBUTOR,
ReindexEntityType.CALL_NUMBER, ResourceType.INSTANCE_CALL_NUMBER
);

public static final String CALL_NUMBER_TABLE = "call_number";
Expand Down
Loading

0 comments on commit 24bae72

Please sign in to comment.