Skip to content

Commit

Permalink
feat(facets): add support for instance classification facets (#566)
Browse files Browse the repository at this point in the history
Closes: MSEARCH-727
  • Loading branch information
psmagin authored Apr 15, 2024
1 parent ea90d8c commit 2ae496c
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 11 deletions.
12 changes: 7 additions & 5 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

### New APIs versions
* Provides `indices v0.7`
* Provides `search v1.3`
* Requires `locations v3.0`

### Features
* Create location index and process location events ([MSEARCH-703](https://folio-org.atlassian.net/browse/MSEARCH-703))
* Implement reindexing of locations ([MSEARCH-702](https://folio-org.atlassian.net/browse/MSEARCH-702))
* Modify diacritics handling in search, browse and sorting ([MSEARCH-690](https://folio-org.atlassian.net/browse/MSEARCH-690))
* Instance search: add search option that search instances by normalized classification number ([MSEARCH-697](https://folio-org.atlassian.net/browse/MSEARCH-697))
* Instance search: make "all" search field option to search by full-text fields ([MSEARCH-606](https://folio-org.atlassian.net/browse/MSEARCH-606))
* Create location index and process location events ([MSEARCH-703](https://issues.folio.org/browse/MSEARCH-703))
* Implement reindexing of locations ([MSEARCH-702](https://issues.folio.org/browse/MSEARCH-702))
* Modify diacritics handling in search, browse and sorting ([MSEARCH-690](https://issues.folio.org/browse/MSEARCH-690))
* Instance search: add search option that search instances by normalized classification number ([MSEARCH-697](https://issues.folio.org/browse/MSEARCH-697))
* Instance search: make "all" search field option to search by full-text fields ([MSEARCH-606](https://issues.folio.org/browse/MSEARCH-606))
* Facets: add support for instance classification facets ([MSEARCH-606](https://issues.folio.org/browse/MSEARCH-606))

### Bug fixes
* Do not delete kafka topics if collection topic is enabled ([MSEARCH-725](https://folio-org.atlassian.net/browse/MSEARCH-725))
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ and [Cross-cluster replication](https://docs.aws.amazon.com/opensearch-service/l
| SEARCH_BY_ALL_FIELDS_ENABLED | false | Specifies if globally search by all field values must be enabled or not (tenant can override this setting) |
| BROWSE_CN_INTERMEDIATE_VALUES_ENABLED | true | Specifies if globally intermediate values (nested instance items) must be populated or not (tenant can override this setting) |
| BROWSE_CN_INTERMEDIATE_REMOVE_DUPLICATES | true | Specifies if globally intermediate duplicate values (fullCallNumber) should be removed or not (Active only with BROWSE_CN_INTERMEDIATE_VALUES_ENABLED) |
| BROWSE_CLASSIFICATIONS_ENABLED | false | Specifies if globally instance classification indexing will be performed |
| BROWSE_CLASSIFICATIONS_ENABLED | true | Specifies if globally instance classification indexing will be performed |
| SCROLL_QUERY_SIZE | 1000 | The number of records to be loaded by each scroll query. 10_000 is a max value |
| STREAM_ID_RETRY_INTERVAL_MS | 1000 | Specifies time to wait before reattempting query. |
| STREAM_ID_RETRY_ATTEMPTS | 3 | Specifies how many queries attempt to perform after the first one failed. |
Expand Down Expand Up @@ -668,7 +668,7 @@ Facets can be retrieved by using following API `GET /{recordType}/facets`. It co

| Parameter | Required | Description |
|:-------------|:--------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `recordType` | Yes | Type of record: authorities, instances, or contributors |
| `recordType` | Yes | Type of record: authorities, instances, contributors, subjects, classifications |
| `query` | Yes | A CQL query to search by |
| `facet` | Yes | A name of the facet with optional size in the format `{facetName}` or `{facetName}:{size}` (for example: `source`, `source:5`). If the size is not specified, all values will be retrieved |

Expand Down Expand Up @@ -751,6 +751,13 @@ GET /instances/facets?query=title all book&facet=source:5,discoverySuppress:2
| `instances.tenantId` | term | Requests a tenantId facet |
| `instances.shared` | term | Requests a shared/local facet |

##### Classifications facets

| Option | Type | Description |
|:---------------------|:----:|:------------------------------|
| `instances.tenantId` | term | Requests a tenantId facet |
| `instances.shared` | term | Requests a shared/local facet |

#### Sorting results

The default sorting is by relevancy. The `sortBy` clause is used to define sorting, for example:
Expand Down
2 changes: 1 addition & 1 deletion descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
},
{
"id": "search",
"version": "1.2",
"version": "1.3",
"handlers": [
{
"methods": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.folio.search.utils.SearchUtils.AUTHORITY_RESOURCE;
import static org.folio.search.utils.SearchUtils.CONTRIBUTOR_RESOURCE;
import static org.folio.search.utils.SearchUtils.INSTANCE_CLASSIFICATION_RESOURCE;
import static org.folio.search.utils.SearchUtils.INSTANCE_RESOURCE;
import static org.folio.search.utils.SearchUtils.INSTANCE_SUBJECT_RESOURCE;

Expand Down Expand Up @@ -29,7 +30,8 @@ public class FacetsController implements FacetsApi {
RecordType.INSTANCES, INSTANCE_RESOURCE,
RecordType.AUTHORITIES, AUTHORITY_RESOURCE,
RecordType.CONTRIBUTORS, CONTRIBUTOR_RESOURCE,
RecordType.SUBJECTS, INSTANCE_SUBJECT_RESOURCE
RecordType.SUBJECTS, INSTANCE_SUBJECT_RESOURCE,
RecordType.CLASSIFICATIONS, INSTANCE_CLASSIFICATION_RESOURCE
);

private final FacetService facetService;
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ folio:
search-all-fields: ${SEARCH_BY_ALL_FIELDS_ENABLED:false}
browse-cn-intermediate-values: ${BROWSE_CN_INTERMEDIATE_VALUES_ENABLED:true}
browse-cn-intermediate-remove-duplicates: ${BROWSE_CN_INTERMEDIATE_REMOVE_DUPLICATES:true}
browse-classifications: ${BROWSE_CLASSIFICATIONS_ENABLED:false}
browse-classifications: ${BROWSE_CLASSIFICATIONS_ENABLED:true}
indexing:
instance-subjects:
retry-attempts: ${INSTANCE_SUBJECTS_INDEXING_RETRY_ATTEMPTS:3}
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/swagger.api/parameters/record-type.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ schema:
- authorities
- contributors
- subjects
- classifications
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ enum:
- authorities
- contributors
- subjects
- classifications
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,45 @@
import static org.folio.search.model.Pair.pair;
import static org.folio.search.support.base.ApiEndpoints.instanceClassificationBrowsePath;
import static org.folio.search.support.base.ApiEndpoints.instanceSearchPath;
import static org.folio.search.support.base.ApiEndpoints.recordFacetsPath;
import static org.folio.search.utils.SearchUtils.getIndexName;
import static org.folio.search.utils.TestConstants.CENTRAL_TENANT_ID;
import static org.folio.search.utils.TestConstants.MEMBER_TENANT_ID;
import static org.folio.search.utils.TestUtils.array;
import static org.folio.search.utils.TestUtils.classificationBrowseItem;
import static org.folio.search.utils.TestUtils.classificationBrowseResult;
import static org.folio.search.utils.TestUtils.facet;
import static org.folio.search.utils.TestUtils.facetItem;
import static org.folio.search.utils.TestUtils.mapOf;
import static org.folio.search.utils.TestUtils.parseResponse;
import static org.folio.search.utils.TestUtils.randomId;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.opensearch.index.query.QueryBuilders.matchAllQuery;
import static org.opensearch.search.builder.SearchSourceBuilder.searchSource;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.folio.search.domain.dto.BrowseOptionType;
import org.folio.search.domain.dto.ClassificationNumberBrowseResult;
import org.folio.search.domain.dto.Facet;
import org.folio.search.domain.dto.FacetResult;
import org.folio.search.domain.dto.Instance;
import org.folio.search.domain.dto.InstanceClassificationsInner;
import org.folio.search.domain.dto.RecordType;
import org.folio.search.model.Pair;
import org.folio.search.support.base.BaseConsortiumIntegrationTest;
import org.folio.search.utils.SearchUtils;
import org.folio.spring.testing.type.IntegrationTest;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.opensearch.action.search.SearchRequest;
import org.opensearch.client.RequestOptions;

Expand Down Expand Up @@ -103,6 +118,31 @@ void browseByClassification_local() {
)));
}

@MethodSource("facetQueriesProvider")
@ParameterizedTest(name = "[{index}] query={0}, facets={1}")
@DisplayName("getFacetsForClassifications_parameterized")
void getFacetsForClassifications_parameterized(String query, String[] facets, Map<String, Facet> expected) {
var actual = parseResponse(doGet(recordFacetsPath(RecordType.CLASSIFICATIONS, query, facets)), FacetResult.class);

expected.forEach((facetName, expectedFacet) -> {
var actualFacet = actual.getFacets().get(facetName);

assertThat(actualFacet).isNotNull();
assertThat(actualFacet.getValues())
.containsExactlyInAnyOrderElementsOf(expectedFacet.getValues());
});
}

private static Stream<Arguments> facetQueriesProvider() {
return Stream.of(
arguments("cql.allRecords=1", array("instances.shared"), mapOf("instances.shared",
facet(facetItem("false", 11), facetItem("true", 8)))),
arguments("cql.allRecords=1", array("instances.tenantId"),
mapOf("instances.tenantId", facet(facetItem(MEMBER_TENANT_ID, 11),
facetItem(CENTRAL_TENANT_ID, 8))))
);
}

private static Instance[] instancesCentral() {
return classificationBrowseInstanceData().subList(0, 5).stream()
.map(BrowseClassificationConsortiumIT::instance)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import static org.folio.search.utils.SearchUtils.AUTHORITY_RESOURCE;
import static org.folio.search.utils.SearchUtils.CONTRIBUTOR_RESOURCE;
import static org.folio.search.utils.SearchUtils.INSTANCE_CLASSIFICATION_RESOURCE;
import static org.folio.search.utils.SearchUtils.INSTANCE_RESOURCE;
import static org.folio.search.utils.SearchUtils.INSTANCE_SUBJECT_RESOURCE;
import static org.folio.search.utils.TestConstants.TENANT_ID;
import static org.folio.search.utils.TestUtils.defaultFacetServiceRequest;
import static org.folio.search.utils.TestUtils.facet;
Expand Down Expand Up @@ -58,7 +60,9 @@ public static Stream<Arguments> facetsTestSource() {
return Stream.of(
Arguments.arguments("authorities", AUTHORITY_RESOURCE),
Arguments.arguments("instances", INSTANCE_RESOURCE),
Arguments.arguments("contributors", CONTRIBUTOR_RESOURCE)
Arguments.arguments("contributors", CONTRIBUTOR_RESOURCE),
Arguments.arguments("subjects", INSTANCE_SUBJECT_RESOURCE),
Arguments.arguments("classifications", INSTANCE_CLASSIFICATION_RESOURCE)
);
}

Expand Down

0 comments on commit 2ae496c

Please sign in to comment.