Commit ed8344c1 authored by Jean-Baptiste Nizet's avatar Jean-Baptiste Nizet
Browse files

feat: add the taxon aggregation, add a type to aggregations

parent 6d36cc7c
......@@ -23,8 +23,6 @@ import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilde
*/
public class GeneticResourceDaoImpl implements GeneticResourceDaoCustom {
private static final int MAX_BUCKETS = 100;
/**
* Contains the fields searchable on a {@link GeneticResource}.
* This is basically all fields at the exception of a few ones like `identifier`,
......@@ -72,7 +70,7 @@ public class GeneticResourceDaoImpl implements GeneticResourceDaoCustom {
Stream.of(RareAggregation.values()).forEach(rareAggregation ->
builder.addAggregation(AggregationBuilders.terms(rareAggregation.getName())
.field(rareAggregation.getField())
.size(MAX_BUCKETS)));
.size(rareAggregation.getType().getMaxBuckets())));
}
return elasticsearchTemplate.queryForPage(builder.build(), GeneticResource.class);
......
package fr.inra.urgi.rare.dao;
import static fr.inra.urgi.rare.dao.RareAggregation.Type.LARGE;
import static fr.inra.urgi.rare.dao.RareAggregation.Type.SMALL;
import java.util.stream.Stream;
/**
* Enum listing the terms aggregations used by RARe, and their corresponding name and field
* @author JB Nizet
*/
public enum RareAggregation {
DOMAIN("domain", "domain.keyword"),
BIOTOPE("biotope", "biotopeType.keyword"),
MATERIAL("material", "materialType.keyword"),
COUNTRY_OF_ORIGIN("coo", "countryOfOrigin.keyword");
DOMAIN("domain", "domain.keyword", SMALL),
BIOTOPE("biotope", "biotopeType.keyword", SMALL),
MATERIAL("material", "materialType.keyword", SMALL),
COUNTRY_OF_ORIGIN("coo", "countryOfOrigin.keyword", SMALL),
TAXON("taxon", "taxon.keyword", LARGE);
private final String name;
private final String field;
private final Type type;
RareAggregation(String name, String field) {
RareAggregation(String name, String field, Type type) {
this.name = name;
this.field = field;
this.type = type;
}
public String getName() {
......@@ -25,4 +33,35 @@ public enum RareAggregation {
public String getField() {
return field;
}
public Type getType() {
return type;
}
public static RareAggregation fromName(String name) {
return Stream.of(RareAggregation.values())
.filter(ra -> ra.getName().equals(name))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("Unknown RareAggregation name: " + name));
}
/**
* The type of an aggregation. On the server, it's used to know what is the maximum number of buckets to
* retrieve. On the client, it's used to know if the aggregation must be displayed using a list of checkbowes to
* choose from, or using a typeahead which will allow adding choices among the potentially large number of results
*/
public enum Type {
SMALL(100),
LARGE(2000);
private final int maxBuckets;
Type(int maxBuckets) {
this.maxBuckets = maxBuckets;
}
public int getMaxBuckets() {
return maxBuckets;
}
}
}
......@@ -5,6 +5,7 @@ import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import fr.inra.urgi.rare.dao.RareAggregation;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
/**
......@@ -13,11 +14,27 @@ import org.elasticsearch.search.aggregations.bucket.terms.Terms;
* @author JB Nizet
*/
public final class AggregationDTO {
/**
* The name of the aggregation, used in the frontend to know which label to display for this aggregation,
* and also used as a query parameter when querying with a value for that aggregation
*/
private final String name;
/**
* The type of the aggregation, used in the frontend to know how to display that aggregation: as
* a list of checkboxes (type SMALL) or as a typeahead to enter values (type LARGE)
*/
private final RareAggregation.Type type;
/**
* The buckets of the aggregation, each containing one of the values, and the number of documents
* selected by the query that fall into that bucket
*/
private final List<BucketDTO> buckets;
public AggregationDTO(Terms aggregation) {
this.name = aggregation.getName();
this.type = RareAggregation.fromName(aggregation.getName()).getType();
this.buckets = Collections.unmodifiableList(
aggregation.getBuckets().stream().map(BucketDTO::new).collect(Collectors.toList())
);
......@@ -27,6 +44,10 @@ public final class AggregationDTO {
return name;
}
public RareAggregation.Type getType() {
return type;
}
public List<BucketDTO> getBuckets() {
return buckets;
}
......
......@@ -187,6 +187,7 @@ class GeneticResourceDaoTest {
.withBiotopeType(Arrays.asList("Biotope", "Human host"))
.withMaterialType(Arrays.asList("Specimen", "DNA"))
.withCountryOfOrigin("France")
.withTaxon(Arrays.asList("Vitis vinifera"))
.build();
GeneticResource geneticResource2 = new GeneticResourceBuilder()
......@@ -196,6 +197,7 @@ class GeneticResourceDaoTest {
.withBiotopeType(Arrays.asList("Biotope"))
.withMaterialType(Arrays.asList("DNA"))
.withCountryOfOrigin("France")
.withTaxon(Arrays.asList("Girolla mucha gusta"))
.build();
geneticResourceDao.saveAll(Arrays.asList(geneticResource1, geneticResource2));
......@@ -223,6 +225,11 @@ class GeneticResourceDaoTest {
assertThat(countryOfOrigin.getName()).isEqualTo(RareAggregation.COUNTRY_OF_ORIGIN.getName());
assertThat(countryOfOrigin.getBuckets()).extracting(Bucket::getKeyAsString).containsExactly("France");
assertThat(countryOfOrigin.getBuckets()).extracting(Bucket::getDocCount).containsExactly(2L);
Terms taxon = result.getAggregations().get(RareAggregation.TAXON.getName());
assertThat(taxon.getName()).isEqualTo(RareAggregation.TAXON.getName());
assertThat(taxon.getBuckets()).extracting(Bucket::getKeyAsString).containsOnly("Vitis vinifera", "Girolla mucha gusta");
assertThat(taxon.getBuckets()).extracting(Bucket::getDocCount).containsOnly(1L);
}
@Nested
......
......@@ -80,10 +80,10 @@ class SearchControllerTest {
pageRequest,
1,
new Aggregations(
Arrays.asList(new MockTermsAggregation("domain",
Arrays.asList(new MockTermsAggregation(RareAggregation.DOMAIN.getName(),
Arrays.asList(new MockBucket("Plantae", 123),
new MockBucket("Fungi", 2))),
new MockTermsAggregation("countryOfOrigin",
new MockTermsAggregation(RareAggregation.COUNTRY_OF_ORIGIN.getName(),
Collections.emptyList())))
));
......@@ -96,13 +96,14 @@ class SearchControllerTest {
.andExpect(jsonPath("$.content[0].name").value(resource.getName()))
.andExpect(jsonPath("$.content[0].description").value(resource.getDescription()))
.andExpect(jsonPath("$.aggregations").isArray())
.andExpect(jsonPath("$.aggregations[0].name").value("domain"))
.andExpect(jsonPath("$.aggregations[0].name").value(RareAggregation.DOMAIN.getName()))
.andExpect(jsonPath("$.aggregations[0].buckets").isArray())
.andExpect(jsonPath("$.aggregations[0].buckets[0].key").value("Plantae"))
.andExpect(jsonPath("$.aggregations[0].buckets[0].documentCount").value(123))
.andExpect(jsonPath("$.aggregations[0].buckets[1].key").value("Fungi"))
.andExpect(jsonPath("$.aggregations[0].buckets[1].documentCount").value(2))
.andExpect(jsonPath("$.aggregations[1].name").value("countryOfOrigin"));
.andExpect(jsonPath("$.aggregations[0].type").value(RareAggregation.Type.SMALL.toString()))
.andExpect(jsonPath("$.aggregations[1].name").value(RareAggregation.COUNTRY_OF_ORIGIN.getName()));
}
@Test
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment