mirror of https://github.com/apache/lucene.git
SOLR-2523: Added support in SolrJ to easily interact range with facets.
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1150361 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
3626220146
commit
e5ac5ed28a
|
@ -322,6 +322,11 @@ New Features
|
|||
|
||||
* SOLR-2670: Added NIOFSDirectoryFactory (yonik)
|
||||
|
||||
* SOLR-2523: Added support in SolrJ to easily interact range with facets.
|
||||
The range facet response can be parsed and is retrievable from the
|
||||
QueryResponse class. The SolrQuery has convenient methods for using
|
||||
range facets. (Martijn van Groningen)
|
||||
|
||||
Optimizations
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -17,13 +17,17 @@
|
|||
|
||||
package org.apache.solr.client.solrj;
|
||||
|
||||
import org.apache.solr.client.solrj.util.ClientUtils;
|
||||
import org.apache.solr.common.params.CommonParams;
|
||||
import org.apache.solr.common.params.FacetParams;
|
||||
import org.apache.solr.common.params.HighlightParams;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.common.params.StatsParams;
|
||||
import org.apache.solr.common.params.TermsParams;
|
||||
import org.apache.solr.common.util.DateUtil;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Date;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
|
@ -227,6 +231,42 @@ public class SolrQuery extends ModifiableSolrParams
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a numeric range facet.
|
||||
*
|
||||
* @param field The field
|
||||
* @param start The start of range
|
||||
* @param end The end of the range
|
||||
* @param gap The gap between each count
|
||||
* @return this
|
||||
*/
|
||||
public SolrQuery addNumericRangeFacet(String field, Number start, Number end, Number gap) {
|
||||
add(FacetParams.FACET_RANGE, field);
|
||||
add(String.format("f.%s.%s", field, FacetParams.FACET_RANGE_START), start.toString());
|
||||
add(String.format("f.%s.%s", field, FacetParams.FACET_RANGE_END), end.toString());
|
||||
add(String.format("f.%s.%s", field, FacetParams.FACET_RANGE_GAP), gap.toString());
|
||||
this.set(FacetParams.FACET, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a numeric range facet.
|
||||
*
|
||||
* @param field The field
|
||||
* @param start The start of range
|
||||
* @param end The end of the range
|
||||
* @param gap The gap between each count
|
||||
* @return this
|
||||
*/
|
||||
public SolrQuery addDateRangeFacet(String field, Date start, Date end, String gap) {
|
||||
add(FacetParams.FACET_RANGE, field);
|
||||
add(String.format("f.%s.%s", field, FacetParams.FACET_RANGE_START), DateUtil.getThreadLocalDateFormat().format(start));
|
||||
add(String.format("f.%s.%s", field, FacetParams.FACET_RANGE_END), DateUtil.getThreadLocalDateFormat().format(end));
|
||||
add(String.format("f.%s.%s", field, FacetParams.FACET_RANGE_GAP), gap);
|
||||
this.set(FacetParams.FACET, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** get the facet fields
|
||||
*
|
||||
* @return string array of facet fields or null if not set/empty
|
||||
|
|
|
@ -17,17 +17,12 @@
|
|||
|
||||
package org.apache.solr.client.solrj.response;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.solr.common.SolrDocumentList;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.client.solrj.SolrServer;
|
||||
import org.apache.solr.client.solrj.beans.DocumentObjectBinder;
|
||||
import org.apache.solr.common.SolrDocumentList;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -53,6 +48,7 @@ public class QueryResponse extends SolrResponseBase
|
|||
private List<FacetField> _facetFields = null;
|
||||
private List<FacetField> _limitingFacets = null;
|
||||
private List<FacetField> _facetDates = null;
|
||||
private List<RangeFacet> _facetRanges = null;
|
||||
private NamedList<List<PivotField>> _facetPivot = null;
|
||||
|
||||
// Highlight Info
|
||||
|
@ -244,6 +240,36 @@ public class QueryResponse extends SolrResponseBase
|
|||
_facetDates.add(f);
|
||||
}
|
||||
}
|
||||
|
||||
//Parse range facets
|
||||
NamedList<NamedList<Object>> rf = (NamedList<NamedList<Object>>) info.get("facet_ranges");
|
||||
if (rf != null) {
|
||||
_facetRanges = new ArrayList<RangeFacet>( rf.size() );
|
||||
for (Map.Entry<String, NamedList<Object>> facet : rf) {
|
||||
NamedList<Object> values = facet.getValue();
|
||||
Object rawGap = values.get("gap");
|
||||
|
||||
RangeFacet rangeFacet;
|
||||
if (rawGap instanceof Number) {
|
||||
Number gap = (Number) rawGap;
|
||||
Number start = (Number) values.get("start");
|
||||
Number end = (Number) values.get("end");
|
||||
rangeFacet = new RangeFacet.Numeric(facet.getKey(), start, end, gap);
|
||||
} else {
|
||||
String gap = (String) rawGap;
|
||||
Date start = (Date) values.get("start");
|
||||
Date end = (Date) values.get("end");
|
||||
rangeFacet = new RangeFacet.Date(facet.getKey(), start, end, gap);
|
||||
}
|
||||
|
||||
NamedList<Integer> counts = (NamedList<Integer>) values.get("counts");
|
||||
for (Map.Entry<String, Integer> entry : counts) {
|
||||
rangeFacet.addCount(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
_facetRanges.add(rangeFacet);
|
||||
}
|
||||
}
|
||||
|
||||
//Parse pivot facets
|
||||
NamedList pf = (NamedList) info.get("facet_pivot");
|
||||
|
@ -329,6 +355,10 @@ public class QueryResponse extends SolrResponseBase
|
|||
return _facetDates;
|
||||
}
|
||||
|
||||
public List<RangeFacet> getFacetRanges() {
|
||||
return _facetRanges;
|
||||
}
|
||||
|
||||
public NamedList<List<PivotField>> getFacetPivot() {
|
||||
return _facetPivot;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
package org.apache.solr.client.solrj.response;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a range facet result
|
||||
*/
|
||||
public abstract class RangeFacet<B, G> {
|
||||
|
||||
private final String name;
|
||||
private final List<Count> counts = new ArrayList<Count>();
|
||||
|
||||
private final B start;
|
||||
private final B end;
|
||||
private final G gap;
|
||||
|
||||
protected RangeFacet(String name, B start, B end, G gap) {
|
||||
this.name = name;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.gap = gap;
|
||||
}
|
||||
|
||||
public void addCount(String value, int count) {
|
||||
counts.add(new Count(value, count, this));
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<Count> getCounts() {
|
||||
return counts;
|
||||
}
|
||||
|
||||
public B getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public B getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public G getGap() {
|
||||
return gap;
|
||||
}
|
||||
|
||||
|
||||
public static class Numeric extends RangeFacet<Number, Number> {
|
||||
|
||||
public Numeric(String name, Number start, Number end, Number gap) {
|
||||
super(name, start, end, gap);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Date extends RangeFacet<java.util.Date, String> {
|
||||
|
||||
public Date(String name, java.util.Date start, java.util.Date end, String gap) {
|
||||
super(name, start, end, gap);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Count {
|
||||
|
||||
private final String value;
|
||||
private final int count;
|
||||
private final RangeFacet rangeFacet;
|
||||
|
||||
public Count(String value, int count, RangeFacet rangeFacet) {
|
||||
this.value = value;
|
||||
this.count = count;
|
||||
this.rangeFacet = rangeFacet;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public RangeFacet getRangeFacet() {
|
||||
return rangeFacet;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,78 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<response>
|
||||
<lst name="responseHeader"><int name="status">0</int><int name="QTime">0</int><lst name="params"><str name="facet.date.start">NOW/DAY-5DAYS</str><str name="facet">true</str><str name="facet.date.hardend">true</str><str name="q">*:*</str><arr name="facet.date"><str>timestamp</str><str>timestamp2</str></arr><str name="facet.date.gap">+1DAY</str><str name="facet.date.other">ALL</str><str name="facet.date.end">NOW/DAY+1DAY</str><str name="rows">0</str></lst></lst><result name="response" numFound="16" start="0"/><lst name="facet_counts"><lst name="facet_queries"/><lst name="facet_fields"/><lst name="facet_dates"><lst name="timestamp"><int name="2008-03-06T00:00:00.000Z">0</int><int name="2008-03-07T00:00:00.000Z">0</int><int name="2008-03-08T00:00:00.000Z">0</int><int name="2008-03-09T00:00:00.000Z">0</int><int name="2008-03-10T00:00:00.000Z">0</int><int name="2008-03-11T00:00:00.000Z">0</int><str name="gap">+1DAY</str><date name="end">2008-03-12T00:00:00Z</date><int name="before">16</int><int name="after">0</int><int name="between">0</int></lst><lst name="timestamp2"><int name="2008-03-06T00:00:00.000Z">0</int><int name="2008-03-07T00:00:00.000Z">0</int><int name="2008-03-08T00:00:00.000Z">0</int><int name="2008-03-09T00:00:00.000Z">0</int><int name="2008-03-10T00:00:00.000Z">0</int><int name="2008-03-11T00:00:00.000Z">0</int><str name="gap">+1DAY</str><date name="end">2008-03-12T00:00:00Z</date><int name="before">0</int><int name="after">0</int><int name="between">0</int></lst></lst></lst>
|
||||
</response>
|
||||
<lst name="responseHeader">
|
||||
<int name="status">0</int>
|
||||
<int name="QTime">0</int>
|
||||
<lst name="params">
|
||||
<str name="facet.date.start">NOW/DAY-5DAYS</str>
|
||||
<str name="facet">true</str>
|
||||
<str name="facet.date.hardend">true</str>
|
||||
<str name="q">*:*</str>
|
||||
<arr name="facet.date">
|
||||
<str>timestamp</str>
|
||||
<str>timestamp2</str>
|
||||
</arr>
|
||||
<str name="facet.date.gap">+1DAY</str>
|
||||
<str name="facet.date.other">ALL</str>
|
||||
<str name="facet.date.end">NOW/DAY+1DAY</str>
|
||||
<str name="rows">0</str>
|
||||
</lst>
|
||||
</lst>
|
||||
<result name="response" numFound="16" start="0"/>
|
||||
<lst name="facet_counts">
|
||||
<lst name="facet_queries"/>
|
||||
<lst name="facet_fields"/>
|
||||
<lst name="facet_dates">
|
||||
<lst name="timestamp">
|
||||
<int name="2008-03-06T00:00:00.000Z">0</int>
|
||||
<int name="2008-03-07T00:00:00.000Z">0</int>
|
||||
<int name="2008-03-08T00:00:00.000Z">0</int>
|
||||
<int name="2008-03-09T00:00:00.000Z">0</int>
|
||||
<int name="2008-03-10T00:00:00.000Z">0</int>
|
||||
<int name="2008-03-11T00:00:00.000Z">0</int>
|
||||
<str name="gap">+1DAY</str>
|
||||
<date name="end">2008-03-12T00:00:00Z</date>
|
||||
<int name="before">16</int>
|
||||
<int name="after">0</int>
|
||||
<int name="between">0</int>
|
||||
</lst>
|
||||
<lst name="timestamp2">
|
||||
<int name="2008-03-06T00:00:00.000Z">0</int>
|
||||
<int name="2008-03-07T00:00:00.000Z">0</int>
|
||||
<int name="2008-03-08T00:00:00.000Z">0</int>
|
||||
<int name="2008-03-09T00:00:00.000Z">0</int>
|
||||
<int name="2008-03-10T00:00:00.000Z">0</int>
|
||||
<int name="2008-03-11T00:00:00.000Z">0</int>
|
||||
<str name="gap">+1DAY</str>
|
||||
<date name="end">2008-03-12T00:00:00Z</date>
|
||||
<int name="before">0</int>
|
||||
<int name="after">0</int>
|
||||
<int name="between">0</int>
|
||||
</lst>
|
||||
</lst>
|
||||
<lst name="facet_ranges">
|
||||
<lst name="price">
|
||||
<lst name="counts">
|
||||
<int name="0.0">3</int>
|
||||
<int name="1.0">0</int>
|
||||
<int name="2.0">0</int>
|
||||
<int name="3.0">0</int>
|
||||
<int name="4.0">0</int>
|
||||
</lst>
|
||||
<float name="gap">1.0</float>
|
||||
<float name="start">0.0</float>
|
||||
<float name="end">5.0</float>
|
||||
</lst>
|
||||
<lst name="manufacturedate_dt">
|
||||
<lst name="counts">
|
||||
<int name="2005-02-13T15:26:37Z">4</int>
|
||||
<int name="2006-02-13T15:26:37Z">7</int>
|
||||
<int name="2007-02-13T15:26:37Z">0</int>
|
||||
</lst>
|
||||
<str name="gap">+1YEAR</str>
|
||||
<date name="start">2005-02-13T15:26:37Z</date>
|
||||
<date name="end">2008-02-13T15:26:37Z</date>
|
||||
</lst>
|
||||
</lst>
|
||||
</lst>
|
||||
</response>
|
|
@ -21,6 +21,11 @@ import org.apache.lucene.util.LuceneTestCase;
|
|||
import org.apache.solr.common.params.FacetParams;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import org.apache.solr.common.util.DateUtil;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -106,6 +111,47 @@ public class SolrQueryTest extends LuceneTestCase {
|
|||
assertFalse("expected set value to be false", q.getFacetSort());
|
||||
}
|
||||
|
||||
public void testFacetNumericRange() {
|
||||
SolrQuery q = new SolrQuery("dog");
|
||||
q.addNumericRangeFacet("field", 1, 10, 1);
|
||||
assertEquals("true", q.get(FacetParams.FACET));
|
||||
assertEquals("field", q.get(FacetParams.FACET_RANGE));
|
||||
assertEquals("1", q.get("f.field." + FacetParams.FACET_RANGE_START));
|
||||
assertEquals("10", q.get("f.field." + FacetParams.FACET_RANGE_END));
|
||||
assertEquals("1", q.get("f.field." + FacetParams.FACET_RANGE_GAP));
|
||||
|
||||
q = new SolrQuery("dog");
|
||||
q.addNumericRangeFacet("field", 1.0d, 10.0d, 1.0d);
|
||||
assertEquals("true", q.get(FacetParams.FACET));
|
||||
assertEquals("field", q.get(FacetParams.FACET_RANGE));
|
||||
assertEquals("1.0", q.get("f.field." + FacetParams.FACET_RANGE_START));
|
||||
assertEquals("10.0", q.get("f.field." + FacetParams.FACET_RANGE_END));
|
||||
assertEquals("1.0", q.get("f.field." + FacetParams.FACET_RANGE_GAP));
|
||||
|
||||
q = new SolrQuery("dog");
|
||||
q.addNumericRangeFacet("field", 1.0f, 10.0f, 1.0f);
|
||||
assertEquals("true", q.get(FacetParams.FACET));
|
||||
assertEquals("field", q.get(FacetParams.FACET_RANGE));
|
||||
assertEquals("1.0", q.get("f.field." + FacetParams.FACET_RANGE_START));
|
||||
assertEquals("10.0", q.get("f.field." + FacetParams.FACET_RANGE_END));
|
||||
assertEquals("1.0", q.get("f.field." + FacetParams.FACET_RANGE_GAP));
|
||||
}
|
||||
|
||||
public void testFacetDateRange() {
|
||||
SolrQuery q = new SolrQuery("dog");
|
||||
Calendar calendar = Calendar.getInstance(Locale.UK);
|
||||
calendar.set(2010, 1, 1);
|
||||
Date start = calendar.getTime();
|
||||
calendar.set(2011, 1, 1);
|
||||
Date end = calendar.getTime();
|
||||
q.addDateRangeFacet("field", start, end, "+1MONTH");
|
||||
assertEquals("true", q.get(FacetParams.FACET));
|
||||
assertEquals("field", q.get(FacetParams.FACET_RANGE));
|
||||
assertEquals(DateUtil.getThreadLocalDateFormat().format(start), q.get("f.field." + FacetParams.FACET_RANGE_START));
|
||||
assertEquals(DateUtil.getThreadLocalDateFormat().format(end), q.get("f.field." + FacetParams.FACET_RANGE_END));
|
||||
assertEquals("+1MONTH", q.get("f.field." + FacetParams.FACET_RANGE_GAP));
|
||||
}
|
||||
|
||||
public void testSettersGetters() {
|
||||
SolrQuery q = new SolrQuery("foo");
|
||||
assertEquals(10, q.setFacetLimit(10).getFacetLimit());
|
||||
|
|
|
@ -17,19 +17,18 @@
|
|||
|
||||
package org.apache.solr.client.solrj.response;
|
||||
|
||||
import java.io.FileReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.solr.client.solrj.impl.XMLResponseParser;
|
||||
import org.apache.solr.common.util.DateUtil;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.core.SolrResourceLoader;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
|
||||
/**
|
||||
* Simple test for Date facet support in QueryResponse
|
||||
*
|
||||
|
@ -59,4 +58,59 @@ public class QueryResponseTest extends LuceneTestCase {
|
|||
// System.out.println("END: " + f.getEnd());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangeFacets() throws Exception {
|
||||
XMLResponseParser parser = new XMLResponseParser();
|
||||
InputStream is = new SolrResourceLoader(null, null).openResource("sampleDateFacetResponse.xml");
|
||||
assertNotNull(is);
|
||||
Reader in = new InputStreamReader(is, "UTF-8");
|
||||
NamedList<Object> response = parser.processResponse(in);
|
||||
in.close();
|
||||
|
||||
QueryResponse qr = new QueryResponse(response, null);
|
||||
Assert.assertNotNull(qr);
|
||||
|
||||
int counter = 0;
|
||||
RangeFacet.Numeric price = null;
|
||||
RangeFacet.Date manufacturedateDt = null;
|
||||
for (RangeFacet r : qr.getFacetRanges()){
|
||||
assertNotNull(r);
|
||||
if ("price".equals(r.getName())) {
|
||||
price = (RangeFacet.Numeric) r;
|
||||
} else if ("manufacturedate_dt".equals(r.getName())) {
|
||||
manufacturedateDt = (RangeFacet.Date) r;
|
||||
}
|
||||
|
||||
counter++;
|
||||
}
|
||||
assertEquals(2, counter);
|
||||
assertNotNull(price);
|
||||
assertNotNull(manufacturedateDt);
|
||||
|
||||
assertEquals(0.0F, price.getStart());
|
||||
assertEquals(5.0F, price.getEnd());
|
||||
assertEquals(1.0F, price.getGap());
|
||||
assertEquals("0.0", price.getCounts().get(0).getValue());
|
||||
assertEquals(3, price.getCounts().get(0).getCount());
|
||||
assertEquals("1.0", price.getCounts().get(1).getValue());
|
||||
assertEquals(0, price.getCounts().get(1).getCount());
|
||||
assertEquals("2.0", price.getCounts().get(2).getValue());
|
||||
assertEquals(0, price.getCounts().get(2).getCount());
|
||||
assertEquals("3.0", price.getCounts().get(3).getValue());
|
||||
assertEquals(0, price.getCounts().get(3).getCount());
|
||||
assertEquals("4.0", price.getCounts().get(4).getValue());
|
||||
assertEquals(0, price.getCounts().get(4).getCount());
|
||||
|
||||
assertEquals(DateUtil.parseDate("2005-02-13T15:26:37Z"), manufacturedateDt.getStart());
|
||||
assertEquals(DateUtil.parseDate("2008-02-13T15:26:37Z"), manufacturedateDt.getEnd());
|
||||
assertEquals("+1YEAR", manufacturedateDt.getGap());
|
||||
assertEquals("2005-02-13T15:26:37Z", manufacturedateDt.getCounts().get(0).getValue());
|
||||
assertEquals(4, manufacturedateDt.getCounts().get(0).getCount());
|
||||
assertEquals("2006-02-13T15:26:37Z", manufacturedateDt.getCounts().get(1).getValue());
|
||||
assertEquals(7, manufacturedateDt.getCounts().get(1).getCount());
|
||||
assertEquals("2007-02-13T15:26:37Z", manufacturedateDt.getCounts().get(2).getValue());
|
||||
assertEquals(0, manufacturedateDt.getCounts().get(2).getCount());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue