diff --git a/solr/core/src/java/org/apache/solr/handler/MoreLikeThisHandler.java b/solr/core/src/java/org/apache/solr/handler/MoreLikeThisHandler.java index e2a9572c7ab..dc560e9b46d 100644 --- a/solr/core/src/java/org/apache/solr/handler/MoreLikeThisHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/MoreLikeThisHandler.java @@ -16,16 +16,6 @@ */ package org.apache.solr.handler; -import java.io.IOException; -import java.io.Reader; -import java.lang.invoke.MethodHandles; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; import net.jcip.annotations.NotThreadSafe; import org.apache.lucene.document.Document; import org.apache.lucene.index.ExitableDirectoryReader; @@ -58,25 +48,21 @@ import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.DocIterator; -import org.apache.solr.search.DocList; -import org.apache.solr.search.DocListAndSet; -import org.apache.solr.search.QParser; -import org.apache.solr.search.QParserPlugin; -import org.apache.solr.search.QueryCommand; -import org.apache.solr.search.QueryLimits; -import org.apache.solr.search.QueryParsing; -import org.apache.solr.search.QueryUtils; -import org.apache.solr.search.ReturnFields; -import org.apache.solr.search.SolrIndexSearcher; -import org.apache.solr.search.SolrReturnFields; -import org.apache.solr.search.SortSpec; -import org.apache.solr.search.SyntaxError; +import org.apache.solr.search.*; +import org.apache.solr.search.facet.FacetParserFactory; +import org.apache.solr.search.facet.FacetRequest; +import org.apache.solr.search.facet.OneFacetParser; import org.apache.solr.security.AuthorizationContext; import org.apache.solr.util.SolrPluginUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.io.Reader; +import java.lang.invoke.MethodHandles; +import java.util.*; +import java.util.regex.Pattern; + /** * Solr MoreLikeThis -- * @@ -224,7 +210,13 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw final ResponseBuilder responseBuilder = new ResponseBuilder(req, rsp, Collections.emptyList()); responseBuilder.setQuery(mlt.getRealMLTQuery()); - SimpleFacets f = new SimpleFacets(req, mltDocs.docSet, params, responseBuilder); + SimpleFacets f = new SimpleFacets(req, mltDocs.docSet, params, responseBuilder){ + @Override + public FacetRequest parseOneFacetReq(SolrQueryRequest req, Map jsonFacet) { + OneFacetParser facetRequestFactory = new FacetParserFactory(); + return facetRequestFactory.parseOneFacetReq(req, jsonFacet); + } + }; FacetComponent.FacetContext.initContext(responseBuilder); rsp.add("facet_counts", FacetComponent.getFacetCounts(f)); } @@ -275,6 +267,7 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw } } + @Override public Name getPermissionName(AuthorizationContext request) { return Name.READ_PERM; diff --git a/solr/core/src/java/org/apache/solr/handler/component/FacetComponent.java b/solr/core/src/java/org/apache/solr/handler/component/FacetComponent.java index bf3bb17f833..6fb3bbf111c 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/FacetComponent.java +++ b/solr/core/src/java/org/apache/solr/handler/component/FacetComponent.java @@ -51,7 +51,10 @@ import org.apache.solr.search.QueryLimits; import org.apache.solr.search.QueryParsing; import org.apache.solr.search.SyntaxError; +import org.apache.solr.search.facet.AbstractFacetComponent; import org.apache.solr.search.facet.FacetDebugInfo; +import org.apache.solr.search.facet.FacetRequest; +import org.apache.solr.search.facet.OneFacetParser; import org.apache.solr.util.RTimer; import org.apache.solr.util.SolrResponseUtil; import org.slf4j.Logger; @@ -62,7 +65,7 @@ * * @since solr 1.3 */ -public class FacetComponent extends SearchComponent { +public class FacetComponent extends AbstractFacetComponent { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); public static final String COMPONENT_NAME = "facet"; @@ -107,7 +110,13 @@ public void prepare(ResponseBuilder rb) throws IOException { /* Custom facet components can return a custom SimpleFacets object */ protected SimpleFacets newSimpleFacets( SolrQueryRequest req, DocSet docSet, SolrParams params, ResponseBuilder rb) { - return new SimpleFacets(req, docSet, params, rb); + return new SimpleFacets(req, docSet, params, rb) { + @Override + public FacetRequest parseOneFacetReq(SolrQueryRequest req, Map jsonFacet) { + OneFacetParser facetRequestFactory = createFacetRequestFactory(req, jsonFacet); + return facetRequestFactory.parseOneFacetReq(req, jsonFacet); + } + }; } /** @@ -284,7 +293,13 @@ public void process(ResponseBuilder rb) throws IOException { String[] pivots = params.getParams(FacetParams.FACET_PIVOT); if (pivots != null && Array.getLength(pivots) != 0) { PivotFacetProcessor pivotProcessor = - new PivotFacetProcessor(rb.req, rb.getResults().docSet, params, rb); + new PivotFacetProcessor(rb.req, rb.getResults().docSet, params, rb) { + @Override + public FacetRequest parseOneFacetReq(SolrQueryRequest req, Map jsonFacet) { + OneFacetParser facetRequestFactory = createFacetRequestFactory(req, jsonFacet); + return facetRequestFactory.parseOneFacetReq(req, jsonFacet); + } + }; SimpleOrderedMap>> v = pivotProcessor.process(pivots); if (v != null) { counts.add(PIVOT_KEY, v); @@ -311,7 +326,6 @@ public static NamedList getFacetCounts(SimpleFacets simpleFacets) { * * @see SimpleFacets#getFacetQueryCounts * @see SimpleFacets#getFacetFieldCounts - * @see RangeFacetProcessor#getFacetRangeCounts * @see RangeFacetProcessor#getFacetIntervalCounts * @see FacetParams#FACET * @return a NamedList of Facet Count info or null @@ -325,7 +339,13 @@ public static NamedList getFacetCounts(SimpleFacets simpleFacets, FacetD simpleFacets.getRequest(), simpleFacets.getDocsOrig(), simpleFacets.getGlobalParams(), - simpleFacets.getResponseBuilder()); + simpleFacets.getResponseBuilder()) { + + @Override + public FacetRequest parseOneFacetReq(SolrQueryRequest req, Map jsonFacet) { + return simpleFacets.parseOneFacetReq(req,jsonFacet); + } + }; NamedList counts = new SimpleOrderedMap<>(); try { counts.add(FACET_QUERY_KEY, simpleFacets.getFacetQueryCounts()); @@ -540,7 +560,7 @@ public void modifyRequest(ResponseBuilder rb, SearchComponent who, ShardRequest FacetInfo fi = rb._facetInfo; if (fi == null) { - rb._facetInfo = fi = new FacetInfo(); + rb._facetInfo = fi = new FacetInfo(createFacetRequestFactory(rb.req, null)); fi.parse(rb.req.getParams(), rb); } @@ -1246,6 +1266,7 @@ public Category getCategory() { * @see org.apache.solr.handler.component.FacetComponent.FacetContext */ public static class FacetInfo { + private final OneFacetParser facetRequestFactory; /** * Incremented counter used to track the values being refined in a given request. This counter * is used in conjunction with {@link PivotFacet#REFINE_PARAM} to identify which refinement @@ -1262,6 +1283,10 @@ public static class FacetInfo { public SimpleOrderedMap pivotFacets = new SimpleOrderedMap<>(); public LinkedHashMap heatmapFacets; + public FacetInfo(OneFacetParser facetRequestFactory) { + this.facetRequestFactory = facetRequestFactory; + } + void parse(SolrParams params, ResponseBuilder rb) { queryFacets = new LinkedHashMap<>(); facets = new LinkedHashMap<>(); @@ -1299,7 +1324,7 @@ void parse(SolrParams params, ResponseBuilder rb) { } } - heatmapFacets = SpatialHeatmapFacets.distribParse(params, rb); + heatmapFacets = SpatialHeatmapFacets.distribParse(this.facetRequestFactory, params, rb); } } diff --git a/solr/core/src/java/org/apache/solr/handler/component/PivotFacetProcessor.java b/solr/core/src/java/org/apache/solr/handler/component/PivotFacetProcessor.java index 77b275ea4c6..2dfc09c7e43 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/PivotFacetProcessor.java +++ b/solr/core/src/java/org/apache/solr/handler/component/PivotFacetProcessor.java @@ -43,10 +43,12 @@ import org.apache.solr.search.DocSet; import org.apache.solr.search.SolrIndexSearcher; import org.apache.solr.search.SyntaxError; +import org.apache.solr.search.facet.FacetParserFactory; +import org.apache.solr.search.facet.FacetRequest; import org.apache.solr.util.PivotListEntry; /** Processes all Pivot facet logic for a single node -- both non-distrib, and per-shard */ -public class PivotFacetProcessor extends SimpleFacets { +public abstract class PivotFacetProcessor extends SimpleFacets { public static final String QUERY = "query"; public static final String RANGE = "range"; protected SolrParams params; @@ -446,7 +448,7 @@ protected void addPivotQueriesAndRanges( assert null != facetRanges; if (!facetQueries.isEmpty()) { - SimpleFacets facets = new SimpleFacets(req, docs, params); + SimpleFacets facets = newSimpleFacets(req, docs); NamedList res = new SimpleOrderedMap<>(); for (FacetComponent.FacetBase facetQuery : facetQueries) { try { @@ -467,7 +469,13 @@ protected void addPivotQueriesAndRanges( pivot.add(PivotListEntry.QUERIES.getName(), res); } if (!facetRanges.isEmpty()) { - RangeFacetProcessor rangeFacetProcessor = new RangeFacetProcessor(req, docs, params, null); + RangeFacetProcessor rangeFacetProcessor = new RangeFacetProcessor(req, docs, params, null){ + + @Override + public FacetRequest parseOneFacetReq(SolrQueryRequest req, Map jsonFacet) { + return PivotFacetProcessor.this.parseOneFacetReq(req,jsonFacet); + } + }; NamedList resOuter = new SimpleOrderedMap<>(); for (RangeFacetRequest rangeFacet : facetRanges) { try { @@ -488,6 +496,16 @@ protected void addPivotQueriesAndRanges( } } + protected SimpleFacets newSimpleFacets( + SolrQueryRequest req, DocSet docSet) { + return new SimpleFacets(req, docSet, req.getParams()) { + @Override + public FacetRequest parseOneFacetReq(SolrQueryRequest req, Map jsonFacet) { + return PivotFacetProcessor.this.parseOneFacetReq(req, jsonFacet); + } + }; + } + private ParsedParams getParsedParams( SolrParams params, DocSet docs, FacetComponent.FacetBase facet) { SolrParams wrapped = SolrParams.wrapDefaults(facet.localParams, global); diff --git a/solr/core/src/java/org/apache/solr/handler/component/RangeFacetProcessor.java b/solr/core/src/java/org/apache/solr/handler/component/RangeFacetProcessor.java index a4ddabe5aa4..4578a0638ae 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/RangeFacetProcessor.java +++ b/solr/core/src/java/org/apache/solr/handler/component/RangeFacetProcessor.java @@ -39,7 +39,7 @@ import org.apache.solr.search.SyntaxError; /** Processor for Range Facets */ -public class RangeFacetProcessor extends SimpleFacets { +public abstract class RangeFacetProcessor extends SimpleFacets { public RangeFacetProcessor( SolrQueryRequest req, DocSet docs, SolrParams params, ResponseBuilder rb) { diff --git a/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java b/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java index 5062f3641cb..9499aa66b98 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java +++ b/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java @@ -26,10 +26,12 @@ import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.request.SimpleFacets; import org.apache.solr.search.DocSet; import org.apache.solr.search.facet.FacetHeatmap; import org.apache.solr.search.facet.FacetMerger; import org.apache.solr.search.facet.FacetRequest; +import org.apache.solr.search.facet.OneFacetParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,14 +51,14 @@ public class SpatialHeatmapFacets { /** Called by {@link org.apache.solr.request.SimpleFacets} to compute heatmap facets. */ @SuppressWarnings("unchecked") public static NamedList getHeatmapForField( - String fieldKey, String fieldName, ResponseBuilder rb, SolrParams params, DocSet docSet) + SimpleFacets simpleFacets, String fieldKey, String fieldName, ResponseBuilder rb, SolrParams params, DocSet docSet) throws IOException { - final FacetRequest facetRequest = createHeatmapRequest(fieldKey, fieldName, rb, params); + final FacetRequest facetRequest = createHeatmapRequest(simpleFacets, fieldKey, fieldName, rb, params); return (NamedList) facetRequest.process(rb.req, docSet); } private static FacetRequest createHeatmapRequest( - String fieldKey, String fieldName, ResponseBuilder rb, SolrParams params) { + OneFacetParser simpleFacets, String fieldKey, String fieldName, ResponseBuilder rb, SolrParams params) { Map jsonFacet = new HashMap<>(); jsonFacet.put("type", "heatmap"); jsonFacet.put("field", fieldName); @@ -78,7 +80,7 @@ private static FacetRequest createHeatmapRequest( FacetHeatmap.FORMAT_PARAM, params.getFieldParam(fieldKey, FacetParams.FACET_HEATMAP_FORMAT)); - return FacetRequest.parseOneFacetReq(rb.req, jsonFacet); + return simpleFacets.parseOneFacetReq(rb.req, jsonFacet); } // @@ -87,12 +89,12 @@ private static FacetRequest createHeatmapRequest( /** Parses request to "HeatmapFacet" instances. */ public static LinkedHashMap distribParse( - SolrParams params, ResponseBuilder rb) { + OneFacetParser facetRequestFactory, SolrParams params, ResponseBuilder rb) { final LinkedHashMap heatmapFacets = new LinkedHashMap<>(); final String[] heatmapFields = params.getParams(FacetParams.FACET_HEATMAP); if (heatmapFields != null) { for (String heatmapField : heatmapFields) { - HeatmapFacet facet = new HeatmapFacet(rb, heatmapField); + HeatmapFacet facet = new HeatmapFacet(facetRequestFactory, rb, heatmapField); heatmapFacets.put(facet.getKey(), facet); } } @@ -168,7 +170,7 @@ public static NamedList> distribFinish( /** * Goes in {@link org.apache.solr.handler.component.FacetComponent.FacetInfo#heatmapFacets}, - * created by {@link #distribParse(org.apache.solr.common.params.SolrParams, ResponseBuilder)}. + * created by {@link #distribParse(OneFacetParser, SolrParams, ResponseBuilder)}. */ public static class HeatmapFacet extends FacetComponent.FacetBase { // note: 'public' following-suit with FacetBase & existing subclasses... though should this @@ -176,11 +178,11 @@ public static class HeatmapFacet extends FacetComponent.FacetBase { public FacetMerger jsonFacetMerger; - public HeatmapFacet(ResponseBuilder rb, String facetStr) { + public HeatmapFacet(OneFacetParser facetRequestFactory, ResponseBuilder rb, String facetStr) { super(rb, FacetParams.FACET_HEATMAP, facetStr); // note: logic in super (FacetBase) is partially redundant with SimpleFacet.parseParams :-( final SolrParams params = SolrParams.wrapDefaults(localParams, rb.req.getParams()); - final FacetRequest heatmapRequest = createHeatmapRequest(getKey(), facetOn, rb, params); + final FacetRequest heatmapRequest = createHeatmapRequest(facetRequestFactory, getKey(), facetOn, rb, params); jsonFacetMerger = heatmapRequest.createFacetMerger(null); } } diff --git a/solr/core/src/java/org/apache/solr/request/SimpleFacets.java b/solr/core/src/java/org/apache/solr/request/SimpleFacets.java index 276626123f5..338bc9298e4 100644 --- a/solr/core/src/java/org/apache/solr/request/SimpleFacets.java +++ b/solr/core/src/java/org/apache/solr/request/SimpleFacets.java @@ -91,7 +91,9 @@ import org.apache.solr.search.SolrIndexSearcher; import org.apache.solr.search.SyntaxError; import org.apache.solr.search.facet.FacetDebugInfo; +import org.apache.solr.search.facet.FacetParserFactory; import org.apache.solr.search.facet.FacetRequest; +import org.apache.solr.search.facet.OneFacetParser; import org.apache.solr.search.grouping.GroupingSpecification; import org.apache.solr.util.BoundedTreeSet; import org.apache.solr.util.RTimer; @@ -104,7 +106,7 @@ *

More advanced facet implementations may compose or subclass this class to leverage any of its * functionality. */ -public class SimpleFacets { +public abstract class SimpleFacets implements OneFacetParser { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); /** The main set of documents all facet counts should be relative to */ @@ -627,7 +629,7 @@ private NamedList getTermCounts(String field, Integer mincount, ParsedP // TODO do we handle debug? Should probably already be handled by the legacy code - Object resObj = FacetRequest.parseOneFacetReq(req, jsonFacet).process(req, docs); + Object resObj = parseOneFacetReq(req, jsonFacet).process(req, docs); // Go through the response to build the expected output for SimpleFacets counts = new NamedList<>(); if (resObj != null) { @@ -674,6 +676,8 @@ private NamedList getTermCounts(String field, Integer mincount, ParsedP return counts; } + public abstract FacetRequest parseOneFacetReq(SolrQueryRequest req, Map jsonFacet) ; + /** * @param existsRequested facet.exists=true is passed for the given field */ @@ -1362,7 +1366,7 @@ public NamedList getHeatmapCounts() throws IOException, SyntaxError { resOuter.add( parsed.key, - SpatialHeatmapFacets.getHeatmapForField( + SpatialHeatmapFacets.getHeatmapForField(this, parsed.key, parsed.facetValue, rb, parsed.params, parsed.docs)); } return resOuter; diff --git a/solr/core/src/java/org/apache/solr/search/facet/AbstractFacetComponent.java b/solr/core/src/java/org/apache/solr/search/facet/AbstractFacetComponent.java new file mode 100644 index 00000000000..2cc41206c96 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/search/facet/AbstractFacetComponent.java @@ -0,0 +1,28 @@ +package org.apache.solr.search.facet; + +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; +import org.apache.solr.request.SimpleFacets; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.DocSet; + +import java.util.Map; + +public abstract class AbstractFacetComponent extends SearchComponent { + protected FacetParserFactory createFacetRequestFactory(SolrQueryRequest req, Map jsonFacet) { + return new FacetParserFactory(); + } + + protected SimpleFacets newSimpleFacets( + SolrQueryRequest req, DocSet docSet, SolrParams params, ResponseBuilder rb) { + return new SimpleFacets(req, docSet, params, rb) { + @Override + public FacetRequest parseOneFacetReq(SolrQueryRequest req, Map jsonFacet) { + OneFacetParser facetRequestFactory = createFacetRequestFactory(req, jsonFacet); + return facetRequestFactory.parseOneFacetReq(req, jsonFacet); + } + }; + } + +} diff --git a/solr/core/src/java/org/apache/solr/search/facet/DocValuesAcc.java b/solr/core/src/java/org/apache/solr/search/facet/DocValuesAcc.java index fddfcf92118..25c108ebfb6 100644 --- a/solr/core/src/java/org/apache/solr/search/facet/DocValuesAcc.java +++ b/solr/core/src/java/org/apache/solr/search/facet/DocValuesAcc.java @@ -37,7 +37,7 @@ * org.apache.lucene.index.DocValues} */ public abstract class DocValuesAcc extends SlotAcc { - SchemaField sf; + protected final SchemaField sf; public DocValuesAcc(FacetContext fcontext, SchemaField sf) throws IOException { super(fcontext); diff --git a/solr/core/src/java/org/apache/solr/search/facet/FacetHeatmap.java b/solr/core/src/java/org/apache/solr/search/facet/FacetHeatmap.java index 4a13ddc51af..076338b0e0f 100644 --- a/solr/core/src/java/org/apache/solr/search/facet/FacetHeatmap.java +++ b/solr/core/src/java/org/apache/solr/search/facet/FacetHeatmap.java @@ -104,7 +104,7 @@ public class FacetHeatmap extends FacetRequest { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - static class Parser extends FacetParser { + static abstract class Parser extends FacetParser { Parser(FacetParser parent, String key) { super(parent, key); } diff --git a/solr/core/src/java/org/apache/solr/search/facet/FacetModule.java b/solr/core/src/java/org/apache/solr/search/facet/FacetModule.java index 5a84339a775..da9266e9db6 100644 --- a/solr/core/src/java/org/apache/solr/search/facet/FacetModule.java +++ b/solr/core/src/java/org/apache/solr/search/facet/FacetModule.java @@ -42,7 +42,7 @@ import org.noggit.CharArr; import org.noggit.JSONWriter; -public class FacetModule extends SearchComponent { +public class FacetModule extends AbstractFacetComponent { public static final String COMPONENT_NAME = "facet_module"; @@ -106,7 +106,8 @@ public void prepare(ResponseBuilder rb) throws IOException { rb.setNeedDocSet(true); // Parse the facet in the prepare phase? - FacetRequest facetRequest = FacetRequest.parse(rb.req, jsonFacet); + FacetParserFactory requestFactory = createFacetRequestFactory(rb.req, jsonFacet); + FacetRequest facetRequest = requestFactory.parseRequest(rb.req, jsonFacet); FacetComponentState fcState = new FacetComponentState(); fcState.rb = rb; @@ -370,7 +371,7 @@ static class FacetComponentState { } // base class for facet functions that can be used in a sort - abstract static class FacetSortableMerger extends FacetMerger { + public abstract static class FacetSortableMerger extends FacetMerger { public void prepareSort() {} @Override @@ -386,7 +387,7 @@ public void finish(Context mcontext) { public abstract int compareTo(FacetSortableMerger other, FacetRequest.SortDirection direction); } - abstract static class FacetDoubleMerger extends FacetSortableMerger { + public abstract static class FacetDoubleMerger extends FacetSortableMerger { @Override public abstract void merge(Object facetResult, Context mcontext); @@ -422,8 +423,8 @@ public static int compare(double a, double b, FacetRequest.SortDirection directi } } - static class FacetLongMerger extends FacetSortableMerger { - long val; + public static class FacetLongMerger extends FacetSortableMerger { + private long val; @Override public void merge(Object facetResult, Context mcontext) { @@ -442,8 +443,9 @@ public int compareTo(FacetSortableMerger other, FacetRequest.SortDirection direc } // base class for facets that create buckets (and can hence have sub-facets) - abstract static class FacetBucketMerger extends FacetMerger { - FacetRequestT freq; + public abstract static class FacetBucketMerger + extends FacetMerger { + protected final FacetRequestT freq; public FacetBucketMerger(FacetRequestT freq) { this.freq = freq; @@ -483,8 +485,8 @@ FacetMerger createFacetMerger(String key, Object val) { } } - static class FacetQueryMerger extends FacetBucketMerger { - FacetBucket bucket; + public static class FacetQueryMerger extends FacetBucketMerger { + private FacetBucket bucket; public FacetQueryMerger(FacetQuery freq) { super(freq); diff --git a/solr/core/src/java/org/apache/solr/search/facet/FacetParser.java b/solr/core/src/java/org/apache/solr/search/facet/FacetParser.java index 6ef55cb4be1..194a35ecde8 100644 --- a/solr/core/src/java/org/apache/solr/search/facet/FacetParser.java +++ b/solr/core/src/java/org/apache/solr/search/facet/FacetParser.java @@ -33,7 +33,7 @@ import org.apache.solr.search.QParser; import org.apache.solr.search.SyntaxError; -abstract class FacetParser { +public abstract class FacetParser { protected T facet; protected FacetParser parent; protected String key; @@ -138,25 +138,7 @@ public Object parseFacetOrStat(String key, Object o) throws SyntaxError { return parseFacetOrStat(key, type, args); } - public Object parseFacetOrStat(String key, String type, Object args) throws SyntaxError { - // TODO: a place to register all these facet types? - - switch (type) { - case "field": - case "terms": - return new FacetFieldParser(this, key).parse(args); - case "query": - return new FacetQueryParser(this, key).parse(args); - case "range": - return new FacetRangeParser(this, key).parse(args); - case "heatmap": - return new FacetHeatmap.Parser(this, key).parse(args); - case "func": - return parseStat(key, args); - } - - throw err("Unknown facet or stat. key=" + key + " type=" + type + " args=" + args); - } + public abstract Object parseFacetOrStat(String key, String type, Object args) throws SyntaxError ; public Object parseStringFacetOrStat(String key, String s) throws SyntaxError { // "avg(myfield)" @@ -174,7 +156,7 @@ private AggValueSource parseStatWithParams(String key, SolrParams localparams, S } /** Parses simple strings like "avg(x)" or robust Maps that may contain local params */ - private AggValueSource parseStat(String key, Object args) throws SyntaxError { + protected AggValueSource parseStat(String key, Object args) throws SyntaxError { assert null != args; if (args instanceof CharSequence) { @@ -459,7 +441,7 @@ public static SolrParams jsonToSolrParams(Map jsonObject) { // TODO Make this private (or at least not static) and introduce // a newInstance method on FacetParser that returns one of these? - static class FacetTopParser extends FacetParser { + static abstract class FacetTopParser extends FacetParser { private SolrQueryRequest req; public FacetTopParser(SolrQueryRequest req) { @@ -485,7 +467,7 @@ public IndexSchema getSchema() { } } - static class FacetQueryParser extends FacetParser { + static abstract class FacetQueryParser extends FacetParser { public FacetQueryParser(FacetParser parent, String key) { super(parent, key); facet = new FacetQuery(); @@ -532,7 +514,7 @@ public FacetQuery parse(Object arg) throws SyntaxError { } } - static class FacetFieldParser extends FacetParser { + static abstract class FacetFieldParser extends FacetParser { public FacetFieldParser(FacetParser parent, String key) { super(parent, key); facet = new FacetField(); diff --git a/solr/core/src/java/org/apache/solr/search/facet/FacetParserFactory.java b/solr/core/src/java/org/apache/solr/search/facet/FacetParserFactory.java new file mode 100644 index 00000000000..75c666c23e9 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/search/facet/FacetParserFactory.java @@ -0,0 +1,79 @@ +package org.apache.solr.search.facet; + +import org.apache.solr.common.SolrException; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.SyntaxError; + +import java.util.Map; + +public class FacetParserFactory implements OneFacetParser { + + public FacetRequest parseRequest(SolrQueryRequest req, Map jsonFacet) { + FacetParser parser = createTopParser(req); + try { + return parser.parse(jsonFacet); + } catch (SyntaxError syntaxError) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, syntaxError); + } + } + + private FacetParser.FacetTopParser createTopParser(SolrQueryRequest req) { + return new FacetParser.FacetTopParser(req) { + @Override + public Object parseFacetOrStat(String key, String type, Object args) throws SyntaxError { + return FacetParserFactory.this.parseFacetOrStat(this, key, type, args); + } + }; + } + + @Override + public FacetRequest parseOneFacetReq(SolrQueryRequest req, Map params) { + @SuppressWarnings("rawtypes") + FacetParser parser = createTopParser(req); + try { + return (FacetRequest) parser.parseFacetOrStat("", params); + } catch (SyntaxError syntaxError) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, syntaxError); + } + } + + protected Object parseFacetOrStat(FacetParser.FacetTopParser facetTopParser, String key, String type, Object args) throws SyntaxError { + // TODO: a place to register all these facet types? + switch (type) { + case "field": + case "terms": + FacetParser.FacetFieldParser facetFieldParser = new FacetParser.FacetFieldParser(facetTopParser, key){ + public Object parseFacetOrStat(String key, String type, Object args) throws SyntaxError { + return FacetParserFactory.this.parseFacetOrStat(facetTopParser, key, type, args); + } + }; + return facetFieldParser.parse(args); + case "query": + FacetParser.FacetQueryParser facetQueryParser = new FacetParser.FacetQueryParser(facetTopParser, key) { + public Object parseFacetOrStat(String key, String type, Object args) throws SyntaxError { + return FacetParserFactory.this.parseFacetOrStat(facetTopParser, key, type, args); + } + }; + return facetQueryParser.parse(args); + case "range": + FacetRangeParser facetRangeParser = new FacetRangeParser(facetTopParser, key){ + @Override + public Object parseFacetOrStat(String key, String type, Object args) throws SyntaxError { + return FacetParserFactory.this.parseFacetOrStat(facetTopParser, key, type, args); + } + }; + return facetRangeParser.parse(args); + case "heatmap": + FacetHeatmap.Parser heatmapParser = new FacetHeatmap.Parser(facetTopParser, key) { + @Override + public Object parseFacetOrStat(String key, String type, Object args) throws SyntaxError { + return FacetParserFactory.this.parseFacetOrStat(facetTopParser, key, type, args); + } + }; + return heatmapParser.parse(args); + case "func": + return facetTopParser.parseStat(key, args); + } + throw facetTopParser.err("Unknown facet or stat. key=" + key + " type=" + type + " args=" + args); + } +} diff --git a/solr/core/src/java/org/apache/solr/search/facet/FacetRangeParser.java b/solr/core/src/java/org/apache/solr/search/facet/FacetRangeParser.java index 679719910c8..00336ba84c1 100644 --- a/solr/core/src/java/org/apache/solr/search/facet/FacetRangeParser.java +++ b/solr/core/src/java/org/apache/solr/search/facet/FacetRangeParser.java @@ -22,7 +22,7 @@ import org.apache.solr.common.params.FacetParams; import org.apache.solr.search.SyntaxError; -class FacetRangeParser extends FacetParser { +abstract class FacetRangeParser extends FacetParser { public FacetRangeParser(FacetParser parent, String key) { super(parent, key); facet = new FacetRange(); diff --git a/solr/core/src/java/org/apache/solr/search/facet/FacetRequest.java b/solr/core/src/java/org/apache/solr/search/facet/FacetRequest.java index 5b8fec842c9..9eb24704550 100644 --- a/solr/core/src/java/org/apache/solr/search/facet/FacetRequest.java +++ b/solr/core/src/java/org/apache/solr/search/facet/FacetRequest.java @@ -42,7 +42,6 @@ * A request to do facets/stats that might itself be composed of sub-FacetRequests. This is a * cornerstone of the facet module. * - * @see #parse(SolrQueryRequest, Map) */ public abstract class FacetRequest { @@ -342,44 +341,6 @@ public Query createDomainQuery(FacetContext fcontext) { } } - /** - * Factory method to parse a facet request tree. The outer keys are arbitrary labels and their - * values are facet request specifications. Will throw a {@link SolrException} if it fails to - * parse. - * - * @param req the overall request - * @param params a typed parameter structure (unlike SolrParams which are all string values). - */ - public static FacetRequest parse(SolrQueryRequest req, Map params) { - FacetParser parser = new FacetParser.FacetTopParser(req); - try { - return parser.parse(params); - } catch (SyntaxError syntaxError) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, syntaxError); - } - } - - // TODO it would be nice if there was no distinction. If the top level request had "type" as - // special then there wouldn't be a need. - - /** - * Factory method to parse out a rooted facet request tree that would normally go one level below - * a label. The params must contain a "type". This is intended to be useful externally, such as by - * {@link org.apache.solr.request.SimpleFacets}. - * - * @param req the overall request - * @param params a typed parameter structure (unlike SolrParams which are all string values). - */ - public static FacetRequest parseOneFacetReq(SolrQueryRequest req, Map params) { - @SuppressWarnings("rawtypes") - FacetParser parser = new FacetParser.FacetTopParser(req); - try { - return (FacetRequest) parser.parseFacetOrStat("", params); - } catch (SyntaxError syntaxError) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, syntaxError); - } - } - public FacetRequest() { facetStats = new LinkedHashMap<>(); subFacets = new LinkedHashMap<>(); diff --git a/solr/core/src/java/org/apache/solr/search/facet/OneFacetParser.java b/solr/core/src/java/org/apache/solr/search/facet/OneFacetParser.java new file mode 100644 index 00000000000..2c3b9fced8e --- /dev/null +++ b/solr/core/src/java/org/apache/solr/search/facet/OneFacetParser.java @@ -0,0 +1,9 @@ +package org.apache.solr.search.facet; + +import org.apache.solr.request.SolrQueryRequest; + +import java.util.Map; + +public interface OneFacetParser { + FacetRequest parseOneFacetReq(SolrQueryRequest req, Map params); +} diff --git a/solr/core/src/java/org/apache/solr/search/facet/UniqueMultiDvSlotAcc.java b/solr/core/src/java/org/apache/solr/search/facet/UniqueMultiDvSlotAcc.java index 4670bc54b31..b0be52b90ae 100644 --- a/solr/core/src/java/org/apache/solr/search/facet/UniqueMultiDvSlotAcc.java +++ b/solr/core/src/java/org/apache/solr/search/facet/UniqueMultiDvSlotAcc.java @@ -28,7 +28,7 @@ import org.apache.lucene.util.LongValues; import org.apache.solr.schema.SchemaField; -class UniqueMultiDvSlotAcc extends UniqueSlotAcc { +public class UniqueMultiDvSlotAcc extends UniqueSlotAcc { SortedSetDocValues topLevel; SortedSetDocValues[] subDvs; OrdinalMap ordMap; diff --git a/solr/core/src/java/org/apache/solr/search/facet/UniqueMultivaluedSlotAcc.java b/solr/core/src/java/org/apache/solr/search/facet/UniqueMultivaluedSlotAcc.java index 6a013147cbc..6839102de0c 100644 --- a/solr/core/src/java/org/apache/solr/search/facet/UniqueMultivaluedSlotAcc.java +++ b/solr/core/src/java/org/apache/solr/search/facet/UniqueMultivaluedSlotAcc.java @@ -24,7 +24,7 @@ import org.apache.solr.schema.SchemaField; import org.apache.solr.search.SolrIndexSearcher; -class UniqueMultivaluedSlotAcc extends UniqueSlotAcc implements UnInvertedField.Callback { +public class UniqueMultivaluedSlotAcc extends UniqueSlotAcc implements UnInvertedField.Callback { private UnInvertedField uif; private UnInvertedField.DocToTerm docToTerm; diff --git a/solr/core/src/java/org/apache/solr/search/facet/UniqueSinglevaluedSlotAcc.java b/solr/core/src/java/org/apache/solr/search/facet/UniqueSinglevaluedSlotAcc.java index dd51050e4ca..c94f162338e 100644 --- a/solr/core/src/java/org/apache/solr/search/facet/UniqueSinglevaluedSlotAcc.java +++ b/solr/core/src/java/org/apache/solr/search/facet/UniqueSinglevaluedSlotAcc.java @@ -28,7 +28,7 @@ import org.apache.lucene.util.LongValues; import org.apache.solr.schema.SchemaField; -class UniqueSinglevaluedSlotAcc extends UniqueSlotAcc { +public class UniqueSinglevaluedSlotAcc extends UniqueSlotAcc { SortedDocValues topLevel; SortedDocValues[] subDvs; OrdinalMap ordMap; diff --git a/solr/core/src/java/org/apache/solr/search/facet/UniqueSlotAcc.java b/solr/core/src/java/org/apache/solr/search/facet/UniqueSlotAcc.java index 84a1ea835f1..ffeebe9f3f2 100644 --- a/solr/core/src/java/org/apache/solr/search/facet/UniqueSlotAcc.java +++ b/solr/core/src/java/org/apache/solr/search/facet/UniqueSlotAcc.java @@ -27,7 +27,7 @@ import org.apache.solr.schema.SchemaField; import org.apache.solr.util.hll.HLL; -abstract class UniqueSlotAcc extends SlotAcc { +public abstract class UniqueSlotAcc extends SlotAcc { HLLAgg.HLLFactory factory; SchemaField field; FixedBitSet[] arr; diff --git a/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacetRefinement.java b/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacetRefinement.java index 8c8b8e0f056..e280016bfd6 100644 --- a/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacetRefinement.java +++ b/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacetRefinement.java @@ -21,6 +21,8 @@ import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Map; + import org.apache.lucene.tests.util.TestUtil; import org.apache.solr.JSONTestUtil; import org.apache.solr.SolrTestCaseHS; @@ -124,13 +126,14 @@ public void addKeyVal(Object map, Object key, Object val) { return ob.getObject(); } + @SuppressWarnings("unchecked") void doTestRefine(String facet, String... responsesAndTests) throws Exception { SolrQueryRequest req = req(); try { int nShards = responsesAndTests.length / 2; Object jsonFacet = Utils.fromJSONString(facet); - FacetParser parser = new FacetParser.FacetTopParser(req); - FacetRequest facetRequest = parser.parse(jsonFacet); + FacetParserFactory facetParserFactory = new FacetParserFactory(); + FacetRequest facetRequest = facetParserFactory.parseRequest(req, (Map) jsonFacet); FacetMerger merger = null; FacetMerger.Context ctx = new FacetMerger.Context(nShards); diff --git a/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacetsStatsParsing.java b/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacetsStatsParsing.java index 4fdb05dff68..51c1d9370c3 100644 --- a/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacetsStatsParsing.java +++ b/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacetsStatsParsing.java @@ -23,8 +23,10 @@ import java.util.Map; import org.apache.lucene.queries.function.valuesource.IntFieldSource; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrException; import org.apache.solr.common.util.Utils; import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.search.SyntaxError; import org.apache.solr.search.function.FieldNameValueSource; import org.junit.BeforeClass; import org.noggit.ObjectBuilder; @@ -56,35 +58,41 @@ public void testSortEquality() { not(new FacetRequest.FacetSort("foo", FacetRequest.SortDirection.desc))); } + @SuppressWarnings("unchecked") public void testEquality() { try (SolrQueryRequest req = req( "custom_req_param", "foo_i", "overridden_param", "xxxxx_i")) { - @SuppressWarnings({"unchecked"}) + FacetRequest result; + Map params = (Map) + Utils.fromJSONString( + "{ " + + + // with valuesource + " f1:'min(field(\"foo_i\"))', " + + " f2:'min(field($custom_req_param))', " + + + // with fieldName and query de-reference + " s1:'min(foo_i)', " + + " s2:'min($custom_req_param)', " + + " s3:{ func:'min($custom_req_param)' }, " + + " s4:{ type:func, func:'min($custom_req_param)' }, " + + " s5:{ type:func, func:'min($custom_local_param)', custom_local_param:foo_i }, " + + " s6:{ type:func, func:'min($overridden_param)', overridden_param:foo_i }, " + + + // test the test... + " diff:'min(field(\"bar_i\"))'," + + "}"); + FacetParserFactory facetParserFactory = new FacetParserFactory(); + result = facetParserFactory.parseRequest(req,params); + // with valuesource + // with fieldName and query de-reference + // test the test... + @SuppressWarnings({"unchecked"}) final FacetRequest fr = - FacetRequest.parse( - req, - (Map) - Utils.fromJSONString( - "{ " - + - // with valuesource - " f1:'min(field(\"foo_i\"))', " - + " f2:'min(field($custom_req_param))', " - + - // with fieldName and query de-reference - " s1:'min(foo_i)', " - + " s2:'min($custom_req_param)', " - + " s3:{ func:'min($custom_req_param)' }, " - + " s4:{ type:func, func:'min($custom_req_param)' }, " - + " s5:{ type:func, func:'min($custom_local_param)', custom_local_param:foo_i }, " - + " s6:{ type:func, func:'min($overridden_param)', overridden_param:foo_i }, " - + - // test the test... - " diff:'min(field(\"bar_i\"))'," - + "}")); + result; final Map stats = fr.getFacetStats(); assertEquals(9, stats.size()); @@ -121,12 +129,14 @@ public void testVerboseSyntaxWithLocalParams() throws IOException { // local params, but DebugAgg does not -- so use these to test that the // JSON Parsing doesn't pollute the local params the ValueSourceParser gets... try (SolrQueryRequest req = req("foo", "zzzz", "yaz", "zzzzz")) { - final FacetRequest fr = - FacetRequest.parse( - req, - (Map) - ObjectBuilder.fromJSON( - "{ x:{type:func, func:'debug()', foo:['abc','xyz'], bar:4.2 } }")); + FacetRequest result; + Map params = (Map) + ObjectBuilder.fromJSON( + "{ x:{type:func, func:'debug()', foo:['abc','xyz'], bar:4.2 } }"); + FacetParserFactory facetParserFactory = new FacetParserFactory(); + result = facetParserFactory.parseRequest(req,params); + final FacetRequest fr = + result; final Map stats = fr.getFacetStats(); assertEquals(1, stats.size()); diff --git a/solr/core/src/test/org/apache/solr/search/function/AggValueSourceTest.java b/solr/core/src/test/org/apache/solr/search/function/AggValueSourceTest.java index 5946d4dc955..19926f00ae7 100644 --- a/solr/core/src/test/org/apache/solr/search/function/AggValueSourceTest.java +++ b/solr/core/src/test/org/apache/solr/search/function/AggValueSourceTest.java @@ -23,6 +23,7 @@ import org.apache.solr.SolrTestCase; import org.apache.solr.search.facet.FacetContext; import org.apache.solr.search.facet.FacetMerger; +import org.apache.solr.search.facet.FacetModule; import org.apache.solr.search.facet.SimpleAggValueSource; import org.apache.solr.search.facet.SlotAcc; import org.junit.Test; @@ -76,22 +77,22 @@ public Object getValue(int slot) { @Override public FacetMerger createFacetMerger(Object prototype) { - return new FacetMerger() { - double total = 0.0D; - - @Override - public void merge(Object facetResult, Context mcontext) { - total += (Double) facetResult; - } - - @Override - public void finish(Context mcontext) {} - - @Override - public Object getMergedResult() { - return total; - } - }; + // check these inner classes can be referenced by name + FacetModule.FacetSortableMerger merger = + new FacetModule.FacetDoubleMerger() { + double total = 0.0D; + + @Override + public void merge(Object facetResult, Context mcontext) { + total += (Double) facetResult; + } + + @Override + public double getDouble() { + return total; + } + }; + return merger; } @Override