SOLR-6267: Let user override Interval Faceting key with LocalParams. Thanks Tomas

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1613308 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Erick Erickson 2014-07-24 21:43:18 +00:00
parent 0bfaf5cdfc
commit 0b11b8ab6b
4 changed files with 136 additions and 34 deletions

View File

@ -158,6 +158,9 @@ New Features
* SOLR-6216: Better faceting for multiple intervals on DV fields (Tomas Fernandez-Lobbe
via Erick Erickson)
* SOLR-6267: Let user override Interval Faceting key with LocalParams (Tomas Fernandez_Lobbe
via Erick Erickson)
Bug Fixes
----------------------

View File

@ -22,12 +22,15 @@ import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.NumericUtils;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.request.IntervalFacets.FacetInterval;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.schema.TrieDateField;
import org.apache.solr.search.DocIterator;
import org.apache.solr.search.DocSet;
import org.apache.solr.search.QueryParsing;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.search.SyntaxError;
@ -60,7 +63,7 @@ import org.apache.solr.search.SyntaxError;
* be faster in cases where there are a larger number of intervals per field.
* <p/>
* To use this class, create an instance using
* {@link #IntervalFacets(SchemaField, SolrIndexSearcher, DocSet, String[])}
* {@link #IntervalFacets(SchemaField, SolrIndexSearcher, DocSet, String[], SolrParams)}
* and then iterate the {@link FacetInterval} using {@link #iterator()}
* <p/>
* Intervals Format</br>
@ -82,11 +85,14 @@ import org.apache.solr.search.SyntaxError;
* comparator can't be changed.
* Commas, brackets and square brackets can be escaped by using '\' in front of them.
* Whitespaces before and after the values will be omitted. Start limit can't be grater
* than the end limit. Equal limits are allowed.
* than the end limit. Equal limits are allowed.<p>
* As with facet.query, the key used to display the result can be set by using local params
* syntax, for example:<p>
* <code>{!key='First Half'}[0,5) </code>
* <p/>
* To use this class:
* <pre>
* IntervalFacets intervalFacets = new IntervalFacets(schemaField, searcher, docs, intervalStrs);
* IntervalFacets intervalFacets = new IntervalFacets(schemaField, searcher, docs, intervalStrs, params);
* for (FacetInterval interval : intervalFacets) {
* results.add(interval.getKey(), interval.getCount());
* }
@ -98,19 +104,19 @@ public class IntervalFacets implements Iterable<FacetInterval> {
private final DocSet docs;
private final FacetInterval[] intervals;
public IntervalFacets(SchemaField schemaField, SolrIndexSearcher searcher, DocSet docs, String[] intervals) throws SyntaxError, IOException {
public IntervalFacets(SchemaField schemaField, SolrIndexSearcher searcher, DocSet docs, String[] intervals, SolrParams params) throws SyntaxError, IOException {
this.schemaField = schemaField;
this.searcher = searcher;
this.docs = docs;
this.intervals = getSortedIntervals(intervals);
this.intervals = getSortedIntervals(intervals, params);
doCount();
}
private FacetInterval[] getSortedIntervals(String[] intervals) throws SyntaxError {
private FacetInterval[] getSortedIntervals(String[] intervals, SolrParams params) throws SyntaxError {
FacetInterval[] sortedIntervals = new FacetInterval[intervals.length];
int idx = 0;
for (String intervalStr : intervals) {
sortedIntervals[idx++] = new FacetInterval(schemaField, intervalStr);
sortedIntervals[idx++] = new FacetInterval(schemaField, intervalStr, params);
}
/*
@ -400,11 +406,31 @@ public class IntervalFacets implements Iterable<FacetInterval> {
*/
private int count;
FacetInterval(SchemaField schemaField, String intervalStr) throws SyntaxError {
FacetInterval(SchemaField schemaField, String intervalStr, SolrParams params) throws SyntaxError {
if (intervalStr == null) throw new SyntaxError("empty facet interval");
intervalStr = intervalStr.trim();
if (intervalStr.length() == 0) throw new SyntaxError("empty facet interval");
key = intervalStr;
try {
SolrParams localParams = QueryParsing.getLocalParams(intervalStr, params);
if (localParams != null ) {
int localParamEndIdx = 2; // omit index of {!
while (true) {
localParamEndIdx = intervalStr.indexOf(QueryParsing.LOCALPARAM_END, localParamEndIdx);
// Local param could be escaping '}'
if (intervalStr.charAt(localParamEndIdx - 1) != '\\') {
break;
}
localParamEndIdx++;
}
intervalStr = intervalStr.substring(localParamEndIdx + 1);
key = localParams.get(CommonParams.OUTPUT_KEY, intervalStr);
} else {
key = intervalStr;
}
} catch (SyntaxError e) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
}
if (intervalStr.charAt(0) == '(') {
startOpen = true;
} else if (intervalStr.charAt(0) == '[') {

View File

@ -17,6 +17,26 @@
package org.apache.solr.request;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.EnumSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.DocsEnum;
@ -74,26 +94,6 @@ import org.apache.solr.util.BoundedTreeSet;
import org.apache.solr.util.DateMathParser;
import org.apache.solr.util.DefaultSolrThreadFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.EnumSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* A class that generates simple Facet information for a request.
*
@ -1433,7 +1433,7 @@ public class SimpleFacets {
SimpleOrderedMap<Integer> fieldResults = new SimpleOrderedMap<Integer>();
res.add(field, fieldResults);
IntervalFacets intervalFacets = new IntervalFacets(schemaField, searcher, docs, intervalStrs);
IntervalFacets intervalFacets = new IntervalFacets(schemaField, searcher, docs, intervalStrs, params);
for (FacetInterval interval : intervalFacets) {
fieldResults.add(interval.getKey(), interval.getCount());
}

View File

@ -389,6 +389,24 @@ public class TestIntervalFaceting extends SolrTestCaseJ4 {
assertBadInterval("test_s_dv", "(B,A)", "Start is higher than end in interval for key");
assertBadInterval("test_s_dv", "(a,B)", "Start is higher than end in interval for key");
assertIntervalKey("test_s_dv", "[A,B]", "[A,B]");
assertIntervalKey("test_s_dv", "(A,*]", "(A,*]");
assertIntervalKey("test_s_dv", "{!}(A,*]", "(A,*]");
assertIntervalKey("test_s_dv", "{!key=foo}(A,*]", "foo");
assertIntervalKey("test_s_dv", "{!key='foo'}(A,*]", "foo");
assertIntervalKey("test_s_dv", "{!key='foo bar'}(A,*]", "foo bar");
assertIntervalKey("test_s_dv", "{!key='foo' bar}(A,*]", "foo");
assertIntervalKey("test_s_dv", "{!key=$i}(A,*]", "foo", "i", "foo");
assertIntervalKey("test_s_dv", "{!key=$i}(A,*]", "foo bar", "i", "foo bar");
assertIntervalKey("test_s_dv", "{!key=$i}(A,*]", "'foo'", "i", "'foo'");
assertIntervalKey("test_s_dv", "{!key=$i}(A,*]", "\"foo\"", "i", "\"foo\"");
assertIntervalKey("test_s_dv", "{!key='[A,B]'}(A,B)", "[A,B]");
assertIntervalKey("test_s_dv", "{!key='\\{\\{\\{'}(A,B)", "{{{");
assertIntervalKey("test_s_dv", "{!key='\\{A,B\\}'}(A,B)", "{A,B}");
assertIntervalKey("test_s_dv", "{!key='\"A,B\"'}(A,B)", "\"A,B\"");
assertIntervalKey("test_s_dv", "{!key='A..B'}(A,B)", "A..B");
assertIntervalKey("test_s_dv", "{!key='A TO B'}(A,B)", "A TO B");
assertU(adoc("id", "1", "test_s_dv", "dog", "test_l_dv", "1"));
assertU(adoc("id", "2", "test_s_dv", "cat", "test_l_dv", "2"));
@ -496,7 +514,7 @@ public class TestIntervalFaceting extends SolrTestCaseJ4 {
private void assertStringInterval(String fieldName, String intervalStr,
String expectedStart, String expectedEnd) throws SyntaxError {
SchemaField f = h.getCore().getLatestSchema().getField(fieldName);
FacetInterval interval = new FacetInterval(f, intervalStr);
FacetInterval interval = new FacetInterval(f, intervalStr, new ModifiableSolrParams());
assertEquals("Expected start " + expectedStart + " but found " + f.getType().toObject(f, interval.start),
interval.start, new BytesRef(f.getType().toInternal(expectedStart)));
@ -508,7 +526,7 @@ public class TestIntervalFaceting extends SolrTestCaseJ4 {
private void assertBadInterval(String fieldName, String intervalStr, String errorMsg) {
SchemaField f = h.getCore().getLatestSchema().getField(fieldName);
try {
new FacetInterval(f, intervalStr);
new FacetInterval(f, intervalStr, new ModifiableSolrParams());
fail("Expecting SyntaxError for interval String: " + intervalStr);
} catch (SyntaxError e) {
assertTrue("Unexpected error message for interval String: " + intervalStr + ": " +
@ -518,7 +536,7 @@ public class TestIntervalFaceting extends SolrTestCaseJ4 {
private void assertInterval(String fieldName, String intervalStr, long[] included, long[] lowerThanStart, long[] graterThanEnd) throws SyntaxError {
SchemaField f = h.getCore().getLatestSchema().getField(fieldName);
FacetInterval interval = new FacetInterval(f, intervalStr);
FacetInterval interval = new FacetInterval(f, intervalStr, new ModifiableSolrParams());
for (long l : included) {
assertEquals("Value " + l + " should be INCLUDED for interval" + interval,
IntervalCompareResult.INCLUDED, interval.includes(l));
@ -533,6 +551,61 @@ public class TestIntervalFaceting extends SolrTestCaseJ4 {
}
}
private void assertIntervalKey(String fieldName, String intervalStr,
String expectedKey, String...params) throws SyntaxError {
assert (params.length&1)==0:"Params must have an even number of elements";
SchemaField f = h.getCore().getLatestSchema().getField(fieldName);
ModifiableSolrParams solrParams = new ModifiableSolrParams();
for (int i = 0; i < params.length - 1;) {
solrParams.set(params[i], params[i+1]);
i+=2;
}
FacetInterval interval = new FacetInterval(f, intervalStr, solrParams);
assertEquals("Expected key " + expectedKey + " but found " + interval.getKey(),
expectedKey, interval.getKey());
}
public void testChangeKey() {
assertU(adoc("id", "1", "test_s_dv", "dog"));
assertU(adoc("id", "2", "test_s_dv", "cat"));
assertU(adoc("id", "3", "test_s_dv", "bird"));
assertU(adoc("id", "4", "test_s_dv", "cat"));
assertU(adoc("id", "5", "test_s_dv", "turtle"));
assertU(adoc("id", "6", "test_s_dv", "dog"));
assertU(adoc("id", "7", "test_s_dv", "dog"));
assertU(adoc("id", "8", "test_s_dv", "dog"));
assertU(adoc("id", "9", "test_s_dv", "cat"));
assertU(adoc("id", "10"));
assertU(commit());
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "test_s_dv",
"f.test_s_dv.facet.interval.set", "{!key=foo}[bird,bird]",
"f.test_s_dv.facet.interval.set", "{!key='bar'}(bird,dog)"),
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='foo'][.=1]",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='bar'][.=3]");
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "test_s_dv",
"f.test_s_dv.facet.interval.set", "{!key=Birds}[bird,bird]",
"f.test_s_dv.facet.interval.set", "{!key='foo bar'}(bird,dog)"),
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='Birds'][.=1]",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='foo bar'][.=3]");
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "test_s_dv",
"f.test_s_dv.facet.interval.set", "{!key=$p}[bird,bird]",
"p", "foo bar"),
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='foo bar'][.=1]");
assertQ(req("q", "*:*", "facet", "true", "facet.interval", "test_s_dv",
"f.test_s_dv.facet.interval.set", "{!key='[bird,\\}'}[bird,*]",
"f.test_s_dv.facet.interval.set", "{!key='\\{bird,dog\\}'}(bird,dog)",
"f.test_s_dv.facet.interval.set", "{!key='foo'}(bird,dog})"),
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='[bird,}'][.=9]",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='{bird,dog}'][.=3]",
"//lst[@name='facet_intervals']/lst[@name='test_s_dv']/int[@name='foo'][.=7]");
}
@Test
public void testLongFields() {
@ -755,7 +828,7 @@ public class TestIntervalFaceting extends SolrTestCaseJ4 {
assertIntervalQuery(field, "[0,2)", "2");
assertIntervalQuery(field, "(0,2]", "2");
assertIntervalQuery(field, "[*,5]", "6");
assertIntervalQuery(field, "[*,3)", "3", "[2,5)", "3", "[6,8)", "2", "[3,*]", "7", "[10,10]", "1");
assertIntervalQuery(field, "[*,3)", "3", "[2,5)", "3", "[6,8)", "2", "[3,*]", "7", "[10,10]", "1", "[10,10]", "1", "[10,10]", "1");
}