Skip to content

Commit

Permalink
Fixed issue with FacetDeserialization when the data array is empty. I…
Browse files Browse the repository at this point in the history
…mproved usability of the getFacet methods
  • Loading branch information
concurrent-recursion committed Jun 16, 2023
1 parent 8e657df commit 0ebee1f
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 18 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ Add the following to your pom.xml
<dependency>
<groupId>io.github.concurrent-recursion</groupId>
<artifactId>enterprisesearch-java-client</artifactId>
<version>0.4.0</version>
<version>0.4.2</version>
</dependency>
```
### Gradle
Add the following dependency
```groovy
dependencies {
implementation 'io.github.concurrent-recursion:enterprisesearch-java-client:0.4.0'
implementation 'io.github.concurrent-recursion:enterprisesearch-java-client:0.4.2'
}
```

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>io.github.concurrent-recursion</groupId>
<artifactId>enterprisesearch-java-client</artifactId>
<version>0.4.1</version>
<version>0.4.2</version>
<packaging>jar</packaging>

<name>Enterprise Search Java Client</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class DeserializationUtil {
* @return The JsonNode or the first element in the array
*/
public JsonNode getFirstValue(TreeNode filterNode) {
if (filterNode instanceof ArrayNode) {
if (filterNode instanceof ArrayNode && !((ArrayNode) filterNode).isEmpty()) {
return ((ArrayNode) filterNode).elements().next();
} else {
return (JsonNode) filterNode;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package co.elasticsearch.enterprisesearch.client.model.response.search;

import co.elasticsearch.enterprisesearch.client.model.response.ErrorableResponse;
import co.elasticsearch.enterprisesearch.client.model.response.search.facet.Facet;
import co.elasticsearch.enterprisesearch.client.model.response.search.facet.*;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Text;

import java.util.*;
import java.util.function.Consumer;
Expand All @@ -20,6 +22,7 @@
*/
@Data
@Accessors(chain = true)
@Slf4j
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class SearchApiResponse<T extends ResponseDocument> implements Iterable<T>, ErrorableResponse {
/**
Expand Down Expand Up @@ -66,20 +69,80 @@ public List<Facet> getFacets() {
return facetMap.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
}

/**
* Get a list of Facets that are mapped to a specific field
* @param fieldName The name of the field in the search document
* @return A list of facets
*/
public List<Facet> getFacetsByField(String fieldName){
return facetMap.getOrDefault(fieldName,Collections.emptyList());
}

/**
* Get the Facet on the specified field, with the specified name, and type. The facet
* @param facetName The name of the facet
* @param facetType the type of the facet
* @return The facet if one is found, and is the correct class, or an empty Optional
* @param <F> The type of facet expected. If the type does not match, it will return an empty Optional
*/
public <F extends Facet> Optional<F> getFacetByName(String facetName,Class<F> facetType){
return facetMap.values().stream()
.flatMap(Collection::stream)
.filter(f -> facetName.equals(f.getName()))
.map(f -> castFacet(f,facetType))
.filter(Objects::nonNull)
.findFirst();
}

/**
* Get the Facet on the specified field, with the specified name, and type. The facet
* @param fieldName The name of the field in the search document
* @param facetName The name of the facet
* @param facetType The type of the facet
* @return The facet if one is found, and is the correct class, or an empty Optional
* @param <F> The type of facet expected. If the type does not match, it will return an empty Optional
*/

public <F extends Facet> Optional<F> getFacetByFieldAndName(String fieldName,String facetName,Class<F> facetType){
List<F> facetList = facetMap.getOrDefault(fieldName,Collections.emptyList()).stream().filter(facetType::isInstance).map(facetType::cast).collect(Collectors.toList());
return facetList.stream().filter(f -> f.getName().equals(facetName)).findFirst();
return getFacetsByField(fieldName).stream()
.filter(f -> facetName.equals(f.getName()))
.map(f -> castFacet(f,facetType))
.filter(Objects::nonNull)
.findFirst();
}

public <F extends Facet> Optional<F> getFacetByName(String facetName,Class<F> facetType){
List<F> facetList = facetMap.values().stream().filter(facetType::isInstance).map(facetType::cast).collect(Collectors.toList());
return facetList.stream().filter(f -> f.getName().equals(facetName)).findFirst();
@SuppressWarnings("unchecked")
private static <F extends Facet> F castFacet(Facet f, Class<F> facetType){
if(f.getClass().equals(facetType)){
return facetType.cast(f);
}else if(f.getClass().equals(EmptyValueFacet.class)){
if(facetType.equals(TextValueFacet.class)){
return (F) new TextValueFacet().setName(f.getName());
} else if(facetType.equals(NumberValueFacet.class)){
return (F) new NumberValueFacet().setName(f.getName());
} else if(facetType.equals(DateValueFacet.class)){
return (F) new DateValueFacet().setName(f.getName());
}else{
log.warn("Requested Facet Type {} but was {}",facetType, f.getClass());
return null;
}
} else if (f.getClass().equals(EmptyRangeFacet.class)) {
if(facetType.equals(NumberRangeFacet.class)){
return (F) new NumberRangeFacet().setName(f.getName());
} else if(facetType.equals(DateRangeFacet.class)){
return (F) new DateRangeFacet().setName(f.getName());
} else if(facetType.equals(GeolocationRangeFacet.class)){
return (F) new GeolocationRangeFacet().setName(f.getName());
}else{
log.warn("Requested Facet Type {} but was {}",facetType, f.getClass());
return null;
}
}
return null;
}



@JsonIgnore
@NotNull
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
@Getter
@Setter
@Accessors(chain = true)
public class EmptyValueFacet implements Facet{
public class EmptyValueFacet implements Facet {
/**
* The name of the facet
* @param name the facet name
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
package co.elasticsearch.enterprisesearch.client.model.response.search.facet;

import co.elasticsearch.enterprisesearch.client.model.FacetType;
import co.elasticsearch.enterprisesearch.client.model.request.search.facet.ValueFacet;
import co.elasticsearch.enterprisesearch.client.model.response.search.facet.value.FacetValue;
import co.elasticsearch.enterprisesearch.client.model.response.search.facet.value.TextValue;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.function.Consumer;


/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package co.elasticsearch.enterprisesearch.client.model.response;

import co.elasticsearch.enterprisesearch.TestUtil;
import co.elasticsearch.enterprisesearch.client.model.request.search.facet.ValueFacet;
import co.elasticsearch.enterprisesearch.client.model.response.documents.IndexResponse;
import co.elasticsearch.enterprisesearch.client.model.response.documents.IndexResult;
import co.elasticsearch.enterprisesearch.client.model.response.search.MultiSearchApiResponse;
import co.elasticsearch.enterprisesearch.client.model.response.search.SearchApiResponse;
import co.elasticsearch.enterprisesearch.client.model.response.search.facet.Facet;
import co.elasticsearch.enterprisesearch.client.model.response.search.facet.*;
import co.elasticsearch.enterprisesearch.client.model.response.search.facet.value.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
Expand All @@ -18,6 +19,7 @@
import java.math.BigDecimal;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;

Expand Down Expand Up @@ -102,7 +104,7 @@ void searchResponse_NumberRange_Facet() throws JsonProcessingException {
}

@Test
void searchResponse_DateRange_Facet() throws JsonProcessingException {
void searchResponse_DateRange_Facet_Direct() throws JsonProcessingException {
String json = TestUtil.readResourceFile("examples/responses/facets/search-facet-date-range.json");
JavaType type = objectMapper.getTypeFactory().constructParametricType(SearchApiResponse.class, NationalParkDocument.class);
SearchApiResponse<NationalParkDocument> response = objectMapper.readValue(json, type);
Expand All @@ -119,6 +121,59 @@ void searchResponse_DateRange_Facet() throws JsonProcessingException {

}

@Test
void searchResponse_DateRange_Facet() throws JsonProcessingException {
String json = TestUtil.readResourceFile("examples/responses/facets/search-facet-date-range.json");
JavaType type = objectMapper.getTypeFactory().constructParametricType(SearchApiResponse.class, NationalParkDocument.class);
SearchApiResponse<NationalParkDocument> response = objectMapper.readValue(json, type);
Optional<DateRangeFacet> halfCentury = response.getFacetByFieldAndName("date_established","half-century",DateRangeFacet.class);
assertTrue(halfCentury.isPresent());
assertEquals(1,halfCentury.get().getData().size());
assertEquals("half-century",halfCentury.get().getName());
DateRange dateRangeValue = halfCentury.get().getDateRanges().get(0);
assertEquals(OffsetDateTime.parse("1950-01-01T00:00:00.000Z"), dateRangeValue.getTo());
assertEquals(OffsetDateTime.parse("1900-01-01T12:00:00.000Z"),dateRangeValue.getFrom());
assertEquals(15L, dateRangeValue.getCount());

}

@Test
void searchResponse_Empty_Facet() throws JsonProcessingException {
String json = TestUtil.readResourceFile("examples/responses/facets/search-facet-empty.json");
JavaType type = objectMapper.getTypeFactory().constructParametricType(SearchApiResponse.class, NationalParkDocument.class);
SearchApiResponse<NationalParkDocument> response = objectMapper.readValue(json, type);

final String valueFieldName = "states";
final String valueFacetName = "states_name";
final String rangeFieldName = "acres";
final String rangeFacetName = "acres_name";

Optional<TextValueFacet> textValueFacet = response.getFacetByFieldAndName( valueFieldName, valueFacetName, TextValueFacet.class);
assertTrue(textValueFacet.isPresent());
assertEquals( valueFacetName,textValueFacet.get().getName());

Optional<NumberValueFacet> numberValueFacet = response.getFacetByFieldAndName( valueFieldName, valueFacetName, NumberValueFacet.class);
assertTrue(numberValueFacet.isPresent());
assertEquals( valueFacetName,numberValueFacet.get().getName());

Optional<DateValueFacet> dateValueFacet = response.getFacetByFieldAndName( valueFieldName, valueFacetName, DateValueFacet.class);
assertTrue(dateValueFacet.isPresent());
assertEquals( valueFacetName,dateValueFacet.get().getName());

Optional<DateRangeFacet> dateRangeFacet = response.getFacetByFieldAndName( rangeFieldName, rangeFacetName, DateRangeFacet.class);
assertTrue(dateRangeFacet.isPresent());
assertEquals( rangeFacetName,dateRangeFacet.get().getName());

Optional<GeolocationRangeFacet> geoRangeFacet = response.getFacetByFieldAndName( rangeFieldName, rangeFacetName, GeolocationRangeFacet.class);
assertTrue(geoRangeFacet.isPresent());
assertEquals( rangeFacetName,geoRangeFacet.get().getName());

Optional<NumberRangeFacet> numberRangeFacet = response.getFacetByFieldAndName( rangeFieldName, rangeFacetName, NumberRangeFacet.class);
assertTrue(numberRangeFacet.isPresent());
assertEquals( rangeFacetName,numberRangeFacet.get().getName());

}

@Test
void indexResponse() throws JsonProcessingException{
String json = TestUtil.readResourceFile("examples/responses/index.json");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package co.elasticsearch.enterprisesearch.client.model.response;

import co.elasticsearch.enterprisesearch.TestUtil;
import co.elasticsearch.enterprisesearch.client.model.Engine;
import co.elasticsearch.enterprisesearch.client.model.request.search.SearchRequest;
import co.elasticsearch.enterprisesearch.client.model.response.search.Meta;
Expand All @@ -8,6 +9,7 @@
import co.elasticsearch.enterprisesearch.client.model.response.search.facet.Facet;
import co.elasticsearch.enterprisesearch.client.model.response.search.facet.TextValueFacet;
import co.elasticsearch.enterprisesearch.client.model.response.search.facet.value.TextValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"meta": {
"alerts": [],
"warnings": [],
"precision": 2,
"engine": {
"name": "national-parks-demo",
"type": "default"
},
"page": {
"current": 1,
"total_pages": 0,
"total_results": 0,
"size": 10
},
"request_id": "6tQl8qIFR_eaAm-YkjcLwQ"
},
"results": [],
"facets": {
"states": [
{
"type": "value",
"name": "states_name",
"data": []
}
],
"acres": [
{
"type": "range",
"name" : "acres_name",
"data": []
}
]
}
}

0 comments on commit 0ebee1f

Please sign in to comment.