SOLR-13417: handle stats on date/str fields in solrj's JSON facet resp

* Except for min/max aggregation in all other cases values woudl be
  number. As for same data/string field, value can vary based on
  aggregation used, capture response in Map<String, Object>
This commit is contained in:
Munendra S N 2019-09-28 12:21:18 +05:30
parent 9586396dba
commit b7ce53d0bf
5 changed files with 47 additions and 12 deletions

View File

@ -183,6 +183,9 @@ Bug Fixes
* SOLR-13180: Fix ClassCastException in Json Request API (Johannes Kloos, Jan Høydahl, Munendra S N) * SOLR-13180: Fix ClassCastException in Json Request API (Johannes Kloos, Jan Høydahl, Munendra S N)
* SOLR-13417: Handle stats aggregation on date and string fields in SolrJ's JSON facet response processing
(Jason Gerlowski, Munendra S N)
Other Changes Other Changes
---------------------- ----------------------

View File

@ -17,6 +17,7 @@
package org.apache.solr.client.solrj.response.json; package org.apache.solr.client.solrj.response.json;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -36,6 +37,7 @@ public class NestableJsonFacet {
private final Map<String, NestableJsonFacet> queryFacetsByName; private final Map<String, NestableJsonFacet> queryFacetsByName;
private final Map<String, BucketBasedJsonFacet> bucketBasedFacetByName; private final Map<String, BucketBasedJsonFacet> bucketBasedFacetByName;
private final Map<String, Number> statFacetsByName; private final Map<String, Number> statFacetsByName;
private final Map<String, Object> statsByName;
private final Map<String, HeatmapJsonFacet> heatmapFacetsByName; private final Map<String, HeatmapJsonFacet> heatmapFacetsByName;
public NestableJsonFacet(NamedList<Object> facetNL) { public NestableJsonFacet(NamedList<Object> facetNL) {
@ -43,6 +45,7 @@ public class NestableJsonFacet {
bucketBasedFacetByName = new HashMap<>(); bucketBasedFacetByName = new HashMap<>();
statFacetsByName = new HashMap<>(); statFacetsByName = new HashMap<>();
heatmapFacetsByName = new HashMap<>(); heatmapFacetsByName = new HashMap<>();
statsByName = new HashMap<>();
for (Map.Entry<String, Object> entry : facetNL) { for (Map.Entry<String, Object> entry : facetNL) {
final String key = entry.getKey(); final String key = entry.getKey();
@ -52,6 +55,9 @@ public class NestableJsonFacet {
domainCount = ((Number) entry.getValue()).longValue(); domainCount = ((Number) entry.getValue()).longValue();
} else if(entry.getValue() instanceof Number) { // Stat/agg facet value } else if(entry.getValue() instanceof Number) { // Stat/agg facet value
statFacetsByName.put(key, (Number)entry.getValue()); statFacetsByName.put(key, (Number)entry.getValue());
statsByName.put(key, (Number) entry.getValue());
} else if (entry.getValue() instanceof String || entry.getValue() instanceof Date) {
statsByName.put(key, entry.getValue());
} else if(entry.getValue() instanceof NamedList) { // Either heatmap/query/range/terms facet } else if(entry.getValue() instanceof NamedList) { // Either heatmap/query/range/terms facet
final NamedList<Object> facet = (NamedList<Object>) entry.getValue(); final NamedList<Object> facet = (NamedList<Object>) entry.getValue();
final boolean isBucketBased = facet.get("buckets") != null; final boolean isBucketBased = facet.get("buckets") != null;
@ -104,18 +110,34 @@ public class NestableJsonFacet {
/** /**
* Retrieve the value for a stat or agg facet with the provided name * Retrieve the value for a stat or agg facet with the provided name
* @deprecated this method works only for numeric value stats, instead use {@link #getStatValue(String)}
*/ */
public Number getStatFacetValue(String name) { public Number getStatFacetValue(String name) {
return statFacetsByName.get(name); return statFacetsByName.get(name);
} }
/**
* Retrieve the value for a stat or agg with the provided name
*/
public Object getStatValue(String name) {
return statsByName.get(name);
}
/** /**
* @return the names of any stat or agg facets that are direct descendants of this facet * @return the names of any stat or agg facets that are direct descendants of this facet
* @deprecated this method returns only stats names with numeric value, instead use {@link #getStatNames()}
*/ */
public Set<String> getStatFacetNames() { public Set<String> getStatFacetNames() {
return statFacetsByName.keySet(); return statFacetsByName.keySet();
} }
/**
* @return the names of any stat or agg that are direct descendants of this facet
*/
public Set<String> getStatNames() {
return statsByName.keySet();
}
/** /**
* Retrieve a "heatmap" facet by its name * Retrieve a "heatmap" facet by its name
*/ */

View File

@ -20,6 +20,7 @@ package org.apache.solr.client.ref_guide_examples;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -34,10 +35,10 @@ import org.apache.solr.client.solrj.request.json.JsonQueryRequest;
import org.apache.solr.client.solrj.request.json.QueryFacetMap; import org.apache.solr.client.solrj.request.json.QueryFacetMap;
import org.apache.solr.client.solrj.request.json.RangeFacetMap; import org.apache.solr.client.solrj.request.json.RangeFacetMap;
import org.apache.solr.client.solrj.request.json.TermsFacetMap; import org.apache.solr.client.solrj.request.json.TermsFacetMap;
import org.apache.solr.client.solrj.response.json.BucketJsonFacet;
import org.apache.solr.client.solrj.response.json.NestableJsonFacet;
import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.UpdateResponse; import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.client.solrj.response.json.BucketJsonFacet;
import org.apache.solr.client.solrj.response.json.NestableJsonFacet;
import org.apache.solr.cloud.SolrCloudTestCase; import org.apache.solr.cloud.SolrCloudTestCase;
import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.ModifiableSolrParams;
@ -455,6 +456,7 @@ public class JsonRequestApiTest extends SolrCloudTestCase {
.setQuery("memory") .setQuery("memory")
.withFilter("inStock:true") .withFilter("inStock:true")
.withStatFacet("avg_price", "avg(price)") .withStatFacet("avg_price", "avg(price)")
.withStatFacet("min_manufacturedate_dt", "min(manufacturedate_dt)")
.withStatFacet("num_suppliers", "unique(manu_exact)") .withStatFacet("num_suppliers", "unique(manu_exact)")
.withStatFacet("median_weight", "percentile(weight,50)"); .withStatFacet("median_weight", "percentile(weight,50)");
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME); QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);
@ -464,9 +466,13 @@ public class JsonRequestApiTest extends SolrCloudTestCase {
assertEquals(4, queryResponse.getResults().getNumFound()); assertEquals(4, queryResponse.getResults().getNumFound());
assertEquals(4, queryResponse.getResults().size()); assertEquals(4, queryResponse.getResults().size());
final NestableJsonFacet topLevelFacetingData = queryResponse.getJsonFacetingResponse(); final NestableJsonFacet topLevelFacetingData = queryResponse.getJsonFacetingResponse();
assertEquals(146.66, (double) topLevelFacetingData.getStatFacetValue("avg_price"), 0.5); assertEquals(146.66, (double) topLevelFacetingData.getStatValue("avg_price"), 0.5);
assertEquals(3, topLevelFacetingData.getStatFacetValue("num_suppliers")); assertEquals(3, topLevelFacetingData.getStatValue("num_suppliers"));
assertEquals(352.0, (double) topLevelFacetingData.getStatFacetValue("median_weight"), 0.5); assertEquals(352.0, (double) topLevelFacetingData.getStatValue("median_weight"), 0.5);
Object val = topLevelFacetingData.getStatValue("min_manufacturedate_dt");
assertTrue(val instanceof Date);
assertEquals("2006-02-13T15:26:37Z", ((Date)val).toInstant().toString());
} }
@Test @Test
@ -478,6 +484,7 @@ public class JsonRequestApiTest extends SolrCloudTestCase {
.setQuery("*:*") .setQuery("*:*")
.withFilter("price:[1.0 TO *]") .withFilter("price:[1.0 TO *]")
.withFilter("popularity:[0 TO 10]") .withFilter("popularity:[0 TO 10]")
.withStatFacet("min_manu_id_s", "min(manu_id_s)")
.withStatFacet("avg_value", "avg(div(popularity,price))"); .withStatFacet("avg_value", "avg(div(popularity,price))");
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME); QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);
//end::solrj-json-metrics-facet-simple[] //end::solrj-json-metrics-facet-simple[]
@ -486,7 +493,10 @@ public class JsonRequestApiTest extends SolrCloudTestCase {
assertEquals(13, queryResponse.getResults().getNumFound()); assertEquals(13, queryResponse.getResults().getNumFound());
assertEquals(10, queryResponse.getResults().size()); assertEquals(10, queryResponse.getResults().size());
final NestableJsonFacet topLevelFacetingData = queryResponse.getJsonFacetingResponse(); final NestableJsonFacet topLevelFacetingData = queryResponse.getJsonFacetingResponse();
assertEquals(0.036, (double) topLevelFacetingData.getStatFacetValue("avg_value"), 0.1); assertEquals(0.036, (double) topLevelFacetingData.getStatValue("avg_value"), 0.1);
Object val = topLevelFacetingData.getStatValue("min_manu_id_s");
assertTrue(val instanceof String);
assertEquals("apple", val.toString());
} }
@Test @Test
@ -511,7 +521,7 @@ public class JsonRequestApiTest extends SolrCloudTestCase {
assertEquals(13, queryResponse.getResults().getNumFound()); assertEquals(13, queryResponse.getResults().getNumFound());
assertEquals(10, queryResponse.getResults().size()); assertEquals(10, queryResponse.getResults().size());
final NestableJsonFacet topLevelFacetingData = queryResponse.getJsonFacetingResponse(); final NestableJsonFacet topLevelFacetingData = queryResponse.getJsonFacetingResponse();
assertEquals(0.108, (double) topLevelFacetingData.getStatFacetValue("avg_value"), 0.1); assertEquals(0.108, (double) topLevelFacetingData.getStatValue("avg_value"), 0.1);
} }
@Test @Test
@ -551,7 +561,7 @@ public class JsonRequestApiTest extends SolrCloudTestCase {
assertEquals(10, queryResponse.getResults().size()); assertEquals(10, queryResponse.getResults().size());
final NestableJsonFacet topLevelFacetingData = queryResponse.getJsonFacetingResponse(); final NestableJsonFacet topLevelFacetingData = queryResponse.getJsonFacetingResponse();
assertEquals(2, topLevelFacetingData.getQueryFacet("high_popularity").getCount()); assertEquals(2, topLevelFacetingData.getQueryFacet("high_popularity").getCount());
assertEquals(199.5, topLevelFacetingData.getQueryFacet("high_popularity").getStatFacetValue("average_price")); assertEquals(199.5, topLevelFacetingData.getQueryFacet("high_popularity").getStatValue("average_price"));
} }
@Test @Test

View File

@ -545,8 +545,8 @@ public class DirectJsonQueryRequestFacetingIntegrationTest extends SolrCloudTest
private void assertHasStatFacetWithValue(NestableJsonFacet response, String expectedFacetName, Double expectedStatValue) { private void assertHasStatFacetWithValue(NestableJsonFacet response, String expectedFacetName, Double expectedStatValue) {
assertTrue("Expected response to have stat facet named '" + expectedFacetName + "'", assertTrue("Expected response to have stat facet named '" + expectedFacetName + "'",
response.getStatFacetValue(expectedFacetName) != null); response.getStatValue(expectedFacetName) != null);
assertEquals(expectedStatValue, response.getStatFacetValue(expectedFacetName)); assertEquals(expectedStatValue, response.getStatValue(expectedFacetName));
} }
private void assertExpectedDocumentsFoundAndReturned(QueryResponse response, int expectedNumFound, int expectedReturned) { private void assertExpectedDocumentsFoundAndReturned(QueryResponse response, int expectedNumFound, int expectedReturned) {

View File

@ -571,8 +571,8 @@ public class JsonQueryRequestFacetingIntegrationTest extends SolrCloudTestCase {
private void assertHasStatFacetWithValue(NestableJsonFacet response, String expectedFacetName, Double expectedStatValue) { private void assertHasStatFacetWithValue(NestableJsonFacet response, String expectedFacetName, Double expectedStatValue) {
assertTrue("Expected response to have stat facet named '" + expectedFacetName + "'", assertTrue("Expected response to have stat facet named '" + expectedFacetName + "'",
response.getStatFacetValue(expectedFacetName) != null); response.getStatValue(expectedFacetName) != null);
assertEquals(expectedStatValue, response.getStatFacetValue(expectedFacetName)); assertEquals(expectedStatValue, response.getStatValue(expectedFacetName));
} }
private void assertExpectedDocumentsFoundAndReturned(QueryResponse response, int expectedNumFound, int expectedReturned) { private void assertExpectedDocumentsFoundAndReturned(QueryResponse response, int expectedNumFound, int expectedReturned) {