Skip to content

Commit

Permalink
feat(cn-browse): support showing instance title in browse results list
Browse files Browse the repository at this point in the history
Closes: MSEARCH-948
  • Loading branch information
psmagin committed Jan 27, 2025
1 parent 575beae commit c68142b
Show file tree
Hide file tree
Showing 15 changed files with 233 additions and 214 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* Call Numbers Browse: Implement Indexing and Re-indexing Mechanisms for Call-Numbers ([MSEARCH-864](https://folio-org.atlassian.net/browse/MSEARCH-864))
* Call Numbers Browse: Implement Browsing Endpoint for Call-Numbers ([MSEARCH-865](https://folio-org.atlassian.net/browse/MSEARCH-865))
* Call Numbers Browse: Support aliases for callNumberTypeId filters ([MSEARCH-942](https://folio-org.atlassian.net/browse/MSEARCH-942))
* Call Numbers Browse: Support showing instance title in browse results list ([MSEARCH-948](https://folio-org.atlassian.net/browse/MSEARCH-948))

### Bug fixes
* Remove shelving order calculation for local call-number types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ public class InstanceSubResource {
private Boolean shared;
private int count;
private List<String> typeId;
private List<String> locationId;
private String locationId;
private List<String> instanceId;
private String instanceTitle;
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
public abstract class AbstractShelvingOrderBrowseServiceBySearchAfter<T, R>
extends AbstractBrowseServiceBySearchAfter<T, R> {

private final ConsortiumSearchHelper consortiumSearchHelper;
protected final ConsortiumSearchHelper consortiumSearchHelper;
private final BrowseConfigServiceDecorator configService;

protected AbstractShelvingOrderBrowseServiceBySearchAfter(ConsortiumSearchHelper consortiumSearchHelper,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

import static org.folio.search.utils.SearchUtils.CALL_NUMBER_TYPE_ID_FIELD;

import java.util.List;
import java.util.Set;
import java.util.function.Function;
import lombok.extern.log4j.Log4j2;
import org.folio.search.domain.dto.BrowseType;
import org.folio.search.domain.dto.CallNumberBrowseItem;
import org.folio.search.model.BrowseResult;
import org.folio.search.model.SearchResult;
import org.folio.search.model.index.CallNumberResource;
import org.folio.search.model.index.InstanceSubResource;
import org.folio.search.model.service.BrowseContext;
import org.folio.search.service.consortium.BrowseConfigServiceDecorator;
import org.folio.search.service.consortium.ConsortiumSearchHelper;
Expand Down Expand Up @@ -61,8 +65,27 @@ protected BrowseResult<CallNumberBrowseItem> mapToBrowseResult(BrowseContext ctx
.chronology(resource.chronology())
.enumeration(resource.enumeration())
.copyNumber(resource.copyNumber())
.instanceTitle(getInstanceTitle(ctx, resource, CallNumberResource::instances))
.isAnchor(isAnchor ? true : null)
.totalRecords(getTotalRecords(ctx, resource, CallNumberResource::instances)));
}

private String getInstanceTitle(BrowseContext ctx, CallNumberResource resource,
Function<CallNumberResource, Set<InstanceSubResource>> func) {
var instanceSubResources = consortiumSearchHelper.filterSubResourcesForConsortium(ctx, resource, func);
if (instanceSubResources.size() == 1) {
return instanceSubResources.iterator().next().getInstanceTitle();
}
return null;
}

@Override
protected Integer getTotalRecords(BrowseContext ctx, CallNumberResource resource,
Function<CallNumberResource, Set<InstanceSubResource>> func) {
return consortiumSearchHelper.filterSubResourcesForConsortium(ctx, resource, func)
.stream()
.map(InstanceSubResource::getInstanceId)
.map(List::size)
.reduce(0, Integer::sum);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
import static org.opensearch.index.query.QueryBuilders.termQuery;

import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.folio.search.model.index.CallNumberResource;
import org.folio.search.model.index.InstanceSubResource;
import org.folio.search.model.service.BrowseContext;
import org.folio.search.model.types.ResourceType;
Expand All @@ -34,6 +34,7 @@ public class ConsortiumSearchHelper {
private static final Logger logger = LoggerFactory.getLogger(ConsortiumSearchHelper.class);
private static final String BROWSE_SHARED_FILTER_KEY = "instances.shared";
private static final String BROWSE_TENANT_FILTER_KEY = "instances.tenantId";
private static final String BROWSE_LOCATION_FILTER_KEY = "instances.locationId";

private final FolioExecutionContext folioExecutionContext;
private final ConsortiumTenantService consortiumTenantService;
Expand Down Expand Up @@ -138,22 +139,29 @@ public <T> Set<InstanceSubResource> filterSubResourcesForConsortium(
}
return subResources.stream()
.filter(subResourcesFilter)
.filter(instanceSubResource -> filterForCallNumbers(context, resource, instanceSubResource))
.collect(Collectors.toSet());
}

public static Optional<TermQueryBuilder> getBrowseFilter(BrowseContext context, String filterKey) {
protected Optional<TermQueryBuilder> getBrowseFilter(BrowseContext context, String filterKey) {
return context.getFilters().stream()
.map(filter -> getTermFilterForKey(filter, filterKey))
.filter(Objects::nonNull)
.findFirst();
}

public static List<Object> getBrowseFilterValues(BrowseContext context, String filterKey) {
return context.getFilters().stream()
.flatMap(filter -> getTermFiltersForKey(filter, filterKey))
.filter(Objects::nonNull)
.map(TermQueryBuilder::value)
.toList();
private <T> boolean filterForCallNumbers(BrowseContext context, T resource, InstanceSubResource instanceSubResource) {
if (resource instanceof CallNumberResource) {
var locationIds = context.getFilters().stream()
.map(filter -> getTermFilterForKey(filter, BROWSE_LOCATION_FILTER_KEY))
.filter(Objects::nonNull)
.map(TermQueryBuilder::value)
.map(String::valueOf)
.filter(StringUtils::isNotBlank)
.toList();
return locationIds.isEmpty() || locationIds.contains(instanceSubResource.getLocationId());
}
return true;
}

private BoolQueryBuilder prepareBoolQueryForActiveAffiliation(QueryBuilder query) {
Expand Down Expand Up @@ -224,15 +232,6 @@ private static TermQueryBuilder getTermFilterForKey(QueryBuilder filter, String
: null;
}

private static Stream<TermQueryBuilder> getTermFiltersForKey(QueryBuilder filter, String filterKey) {
if (filter instanceof TermQueryBuilder termFilter && termFilter.fieldName().equals(filterKey)) {
return Stream.of(termFilter);
} else if (filter instanceof BoolQueryBuilder boolFilter) {
return boolFilter.should().stream().map(shouldFilter -> getTermFilterForKey(shouldFilter, filterKey));
}
return null;
}

private boolean sharedFilterValue(TermQueryBuilder sharedQuery) {
return sharedQuery.value() instanceof Boolean boolValue && boolValue
|| sharedQuery.value() instanceof String stringValue && Boolean.parseBoolean(stringValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,21 @@ public class CallNumberRepository extends UploadRangeRepository implements Insta
SELECT c.*,
json_agg(
json_build_object(
'count', sub.instance_count,
'instanceId', sub.instance_ids,
'tenantId', sub.tenant_id,
'shared', sub.shared,
'locationId', sub.location_ids
'locationId', sub.location_id
)
) AS instances
FROM (SELECT ins.call_number_id,
ins.tenant_id,
i.shared,
array_agg(DISTINCT ins.location_id) FILTER (WHERE ins.location_id IS NOT NULL) AS location_ids,
count(DISTINCT ins.instance_id) AS instance_count
ins.location_id,
array_agg(DISTINCT i.id) AS instance_ids
FROM %1$s.instance_call_number ins
INNER JOIN %1$s.instance i ON i.id = ins.instance_id
WHERE %2$s
GROUP BY ins.call_number_id, ins.tenant_id, i.shared) sub
GROUP BY ins.call_number_id, ins.tenant_id, i.shared, ins.location_id) sub
JOIN %1$s.call_number c ON c.id = sub.call_number_id
WHERE %3$s
GROUP BY c.id;
Expand Down Expand Up @@ -108,12 +108,12 @@ WITH cte AS (
c.last_updated_date,
json_agg(
CASE
WHEN sub.instance_count IS NULL THEN NULL
WHEN sub.instance_ids IS NULL THEN NULL
ELSE json_build_object(
'count', sub.instance_count,
'instanceId', sub.instance_ids,
'tenantId', sub.tenant_id,
'shared', sub.shared,
'locationId', sub.location_ids
'locationId', sub.location_id
)
END
) AS instances
Expand All @@ -123,15 +123,16 @@ LEFT JOIN (
cte.id,
ins.tenant_id,
i.shared,
array_agg(DISTINCT ins.location_id) FILTER (WHERE ins.location_id IS NOT NULL) AS location_ids,
count(DISTINCT ins.instance_id) AS instance_count
ins.location_id,
array_agg(DISTINCT i.id) AS instance_ids
FROM %1$s.instance_call_number ins
INNER JOIN cte ON ins.call_number_id = cte.id
INNER JOIN %1$s.instance i ON i.id = ins.instance_id
GROUP BY
cte.id,
ins.tenant_id,
i.shared
i.shared,
ins.location_id
) sub ON c.id = sub.id
GROUP BY
c.id,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package org.folio.search.service.setter.callnumber;

import static org.folio.search.utils.LogUtils.collectionToLogMsg;
import static org.opensearch.index.query.QueryBuilders.idsQuery;

import java.util.Collection;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections4.MapUtils;
import org.folio.search.model.SimpleResourceRequest;
import org.folio.search.model.index.CallNumberResource;
import org.folio.search.model.index.InstanceSubResource;
import org.folio.search.model.types.ResourceType;
import org.folio.search.repository.SearchRepository;
import org.folio.search.service.consortium.TenantProvider;
import org.folio.search.service.setter.SearchResponsePostProcessor;
import org.folio.spring.FolioExecutionContext;
import org.opensearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Component;

@Log4j2
@Component
@RequiredArgsConstructor
public final class CallNumberSearchResponsePostProcessor implements SearchResponsePostProcessor<CallNumberResource> {

private static final String INSTANCE_TITLE_FIELD = "plain_title";

private final SearchRepository searchRepository;
private final FolioExecutionContext context;
private final TenantProvider tenantProvider;

@Override
public Class<CallNumberResource> getGeneric() {
return CallNumberResource.class;
}

@Override
public void process(List<CallNumberResource> res) {
log.debug("process:: by [res: {}]", collectionToLogMsg(res, true));

if (res == null || res.isEmpty()) {
return;
}
var subResources = res.stream()
.flatMap(resource -> resource.instances().stream())
.toList();

countAndSetNumberOfLinkedInstances(subResources);
}

private void countAndSetNumberOfLinkedInstances(List<InstanceSubResource> authorities) {
var ids = authorities.stream()
.map(InstanceSubResource::getInstanceId)
.filter(instanceIds -> instanceIds.size() == 1)
.flatMap(Collection::stream)
.distinct()
.toList();
var queries = buildQuery(ids);

var resourceRequest = SimpleResourceRequest.of(ResourceType.INSTANCE,
tenantProvider.getTenant(context.getTenantId()));
var searchHits = searchRepository.search(resourceRequest, queries).getHits().getHits();

for (var searchHit : searchHits) {
var instanceId = searchHit.getId();
var instanceTitle = MapUtils.getString(searchHit.getSourceAsMap(), INSTANCE_TITLE_FIELD);
for (var authority : authorities) {
if (authority.getInstanceId().size() == 1 && authority.getInstanceId().get(0).equals(instanceId)) {
authority.setInstanceTitle(instanceTitle);
}
}
}
}

private SearchSourceBuilder buildQuery(List<String> instanceIds) {
var boolQueryBuilder = idsQuery().addIds(instanceIds.toArray(String[]::new));

return new SearchSourceBuilder()
.query(boolQueryBuilder)
.fetchSource(INSTANCE_TITLE_FIELD, null)
.trackTotalHits(true);
}
}
Loading

0 comments on commit c68142b

Please sign in to comment.