You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by da...@apache.org on 2018/11/02 11:16:58 UTC
[49/56] lucene-solr:jira/gradle: Add :solr:contrib:analytics module
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestParser.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestParser.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestParser.java
deleted file mode 100644
index bcb7747..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestParser.java
+++ /dev/null
@@ -1,523 +0,0 @@
-/*
- * 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.
- */
-package org.apache.solr.analytics;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.function.Predicate;
-import java.util.regex.Pattern;
-
-import org.apache.solr.analytics.facet.PivotFacet;
-import org.apache.solr.analytics.facet.PivotNode;
-import org.apache.solr.analytics.facet.QueryFacet;
-import org.apache.solr.analytics.facet.RangeFacet;
-import org.apache.solr.analytics.facet.ValueFacet;
-import org.apache.solr.analytics.facet.SortableFacet.FacetSortSpecification;
-import org.apache.solr.analytics.facet.compare.DelegatingComparator;
-import org.apache.solr.analytics.facet.compare.FacetValueComparator;
-import org.apache.solr.analytics.facet.compare.FacetResultsComparator;
-import org.apache.solr.analytics.value.AnalyticsValue;
-import org.apache.solr.analytics.value.AnalyticsValueStream;
-import org.apache.solr.analytics.value.ComparableValue;
-import org.apache.solr.analytics.value.StringValueStream;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.SolrException.ErrorCode;
-import org.apache.solr.common.params.FacetParams.FacetRangeInclude;
-import org.apache.solr.common.params.FacetParams.FacetRangeOther;
-import org.apache.solr.schema.IndexSchema;
-import org.apache.solr.schema.SchemaField;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonSubTypes;
-import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.annotation.JsonTypeName;
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-/**
- * Class to manage the parsing of new-style analytics requests.
- */
-public class AnalyticsRequestParser {
-
- private static ObjectMapper mapper = new ObjectMapper();
-
- public static void init() {
- mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
- mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, true);
- }
-
- public static final String analyticsParamName = "analytics";
-
- private static Predicate<String> sortAscending = acceptNames("ascending", "asc", "a");
- private static Predicate<String> sortDescending = acceptNames("descending", "desc", "d");
-
- private static Predicate<String> acceptNames(String... names) {
- return Pattern.compile("^(?:" + Arrays.stream(names).reduce((a,b) -> a + "|" + b).orElse("") + ")$", Pattern.CASE_INSENSITIVE).asPredicate();
- }
-
- // Defaults
- public static final String DEFAULT_SORT_DIRECTION = "ascending";
- public static final int DEFAULT_OFFSET = 0;
- public static final int DEFAULT_LIMIT = -1;
- public static final boolean DEFAULT_HARDEND = false;
-
- @JsonInclude(Include.NON_EMPTY)
- public static class AnalyticsRequest {
- public Map<String, String> functions;
- public Map<String, String> expressions;
-
- public Map<String, AnalyticsGroupingRequest> groupings;
- }
-
- public static class AnalyticsGroupingRequest {
- public Map<String, String> expressions;
-
- public Map<String, AnalyticsFacetRequest> facets;
- }
-
- @JsonTypeInfo(
- use = JsonTypeInfo.Id.NAME,
- include = JsonTypeInfo.As.PROPERTY,
- property = "type"
- )
- @JsonSubTypes({
- @Type(value = AnalyticsValueFacetRequest.class, name = "value"),
- @Type(value = AnalyticsPivotFacetRequest.class, name = "pivot"),
- @Type(value = AnalyticsRangeFacetRequest.class, name = "range"),
- @Type(value = AnalyticsQueryFacetRequest.class, name = "query") }
- )
- @JsonInclude(Include.NON_EMPTY)
- public static interface AnalyticsFacetRequest { }
-
- @JsonTypeName("value")
- public static class AnalyticsValueFacetRequest implements AnalyticsFacetRequest {
- public String expression;
- public AnalyticsSortRequest sort;
- }
-
- @JsonTypeName("pivot")
- public static class AnalyticsPivotFacetRequest implements AnalyticsFacetRequest {
- public List<AnalyticsPivotRequest> pivots;
- }
-
- public static class AnalyticsPivotRequest {
- public String name;
- public String expression;
- public AnalyticsSortRequest sort;
- }
-
- @JsonInclude(Include.NON_EMPTY)
- public static class AnalyticsSortRequest {
- public List<AnalyticsSortCriteriaRequest> criteria;
- public int limit = DEFAULT_LIMIT;
- public int offset = DEFAULT_OFFSET;
- }
-
- @JsonTypeInfo(
- use = JsonTypeInfo.Id.NAME,
- include = JsonTypeInfo.As.PROPERTY,
- property = "type"
- )
- @JsonSubTypes({
- @Type(value = AnalyticsExpressionSortRequest.class, name = "expression"),
- @Type(value = AnalyticsFacetValueSortRequest.class, name = "facetvalue") }
- )
- @JsonInclude(Include.NON_EMPTY)
- public static abstract class AnalyticsSortCriteriaRequest {
- public String direction;
- }
-
- @JsonTypeName("expression")
- public static class AnalyticsExpressionSortRequest extends AnalyticsSortCriteriaRequest {
- public String expression;
- }
-
- @JsonTypeName("facetvalue")
- public static class AnalyticsFacetValueSortRequest extends AnalyticsSortCriteriaRequest { }
-
- @JsonTypeName("range")
- public static class AnalyticsRangeFacetRequest implements AnalyticsFacetRequest {
- public String field;
- public String start;
- public String end;
- public List<String> gaps;
- public boolean hardend = DEFAULT_HARDEND;
- public List<String> include;
- public List<String> others;
- }
-
- @JsonTypeName("query")
- public static class AnalyticsQueryFacetRequest implements AnalyticsFacetRequest {
- public Map<String, String> queries;
- }
-
- /* ***************
- * Request & Groupings
- * ***************/
-
- public static AnalyticsRequestManager parse(AnalyticsRequest request, ExpressionFactory expressionFactory, boolean isDistribRequest) throws SolrException {
- AnalyticsRequestManager manager = constructRequest(request, expressionFactory, isDistribRequest);
- if (isDistribRequest) {
- try {
- manager.analyticsRequest = mapper.writeValueAsString(request);
- } catch (JsonProcessingException e) {
- throw new RuntimeException(e);
- }
- }
- return manager;
- }
-
- public static AnalyticsRequestManager parse(String rawRequest, ExpressionFactory expressionFactory, boolean isDistribRequest) throws SolrException {
- JsonParser parser;
- try {
- parser = new JsonFactory().createParser(rawRequest)
- .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
- .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
- } catch (IOException e1) {
- throw new RuntimeException(e1);
- }
- AnalyticsRequest request;
- try {
- request = mapper.readValue(parser, AnalyticsRequest.class);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
-
- AnalyticsRequestManager manager = constructRequest(request, expressionFactory, isDistribRequest);
- if (isDistribRequest) {
- manager.analyticsRequest = rawRequest;
- }
- return manager;
- }
-
- private static AnalyticsRequestManager constructRequest(AnalyticsRequest request, ExpressionFactory expressionFactory, boolean isDistribRequest) throws SolrException {
- expressionFactory.startRequest();
-
- // Functions
- if (request.functions != null) {
- request.functions.forEach( (funcSig, retSig) -> expressionFactory.addUserDefinedVariableFunction(funcSig, retSig));
- }
-
- // Expressions
- Map<String,AnalyticsExpression> topLevelExpressions;
- if (request.expressions != null) {
- topLevelExpressions = constructExpressions(request.expressions, expressionFactory);
- } else {
- topLevelExpressions = new HashMap<>();
- }
- AnalyticsRequestManager manager = new AnalyticsRequestManager(expressionFactory.createReductionManager(isDistribRequest), topLevelExpressions.values());
-
- // Groupings
- if (request.groupings != null) {
- request.groupings.forEach( (name, grouping) -> {
- manager.addGrouping(constructGrouping(name, grouping, expressionFactory, isDistribRequest));
- });
- }
- return manager;
- }
-
- private static AnalyticsGroupingManager constructGrouping(String name, AnalyticsGroupingRequest grouping, ExpressionFactory expressionFactory, boolean isDistribRequest) throws SolrException {
- expressionFactory.startGrouping();
-
- // Expressions
- if (grouping.expressions == null || grouping.expressions.size() == 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"Groupings must contain at least one expression, '" + name + "' has none.");
- }
-
- Map<String,AnalyticsExpression> expressions = constructExpressions(grouping.expressions, expressionFactory);
- AnalyticsGroupingManager manager = new AnalyticsGroupingManager(name,
- expressionFactory.createGroupingReductionManager(isDistribRequest),
- expressions.values());
-
- if (grouping.facets == null) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"Groupings must contain at least one facet, '" + name + "' has none.");
- }
- // Parse the facets
- grouping.facets.forEach( (facetName, facet) -> {
- if (facet instanceof AnalyticsValueFacetRequest) {
- manager.addFacet(constructValueFacet(facetName, (AnalyticsValueFacetRequest) facet, expressionFactory, expressions));
- } else if (facet instanceof AnalyticsPivotFacetRequest) {
- manager.addFacet(constructPivotFacet(facetName, (AnalyticsPivotFacetRequest) facet, expressionFactory, expressions));
- } else if (facet instanceof AnalyticsRangeFacetRequest) {
- manager.addFacet(constructRangeFacet(facetName, (AnalyticsRangeFacetRequest) facet, expressionFactory.getSchema()));
- } else if (facet instanceof AnalyticsQueryFacetRequest) {
- manager.addFacet(constructQueryFacet(facetName, (AnalyticsQueryFacetRequest) facet));
- } else {
- throw new SolrException(ErrorCode.BAD_REQUEST,"The facet type, '" + facet.getClass().toString() + "' in "
- + "grouping '" + name + "' is not a valid type of facet");
- }
- });
-
- return manager;
- }
-
- /* ***************
- * Expression & Functions
- * ***************/
-
- private static Map<String, AnalyticsExpression> constructExpressions(Map<String, String> rawExpressions, ExpressionFactory expressionFactory) throws SolrException {
- Map<String, AnalyticsExpression> expressions = new HashMap<>();
- rawExpressions.forEach( (name, expression) -> {
- AnalyticsValueStream exprVal = expressionFactory.createExpression(expression);
- if (exprVal instanceof AnalyticsValue) {
- if (exprVal.getExpressionType().isReduced()) {
- expressions.put(name, (new AnalyticsExpression(name, (AnalyticsValue)exprVal)));
- } else {
- throw new SolrException(ErrorCode.BAD_REQUEST,"Top-level expressions must be reduced, the '" + name + "' expression is not.");
- }
- } else {
- throw new SolrException(ErrorCode.BAD_REQUEST,"Top-level expressions must be single-valued, the '" + name + "' expression is not.");
- }
- });
- return expressions;
- }
-
- /* ***************
- * FACETS
- * ***************/
-
- /*
- * Value Facets
- */
-
- private static ValueFacet constructValueFacet(String name, AnalyticsValueFacetRequest facetRequest, ExpressionFactory expressionFactory, Map<String, AnalyticsExpression> expressions) throws SolrException {
- if (facetRequest.expression == null) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Value Facets must contain a mapping expression to facet over, '" + name + "' has none.");
- }
-
- // The second parameter must be a mapping expression
- AnalyticsValueStream expr = expressionFactory.createExpression(facetRequest.expression);
- if (!expr.getExpressionType().isUnreduced()) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Value Facet expressions must be mapping expressions, "
- + "the following expression in value facet '" + name + "' contains a reduction: " + facetRequest.expression);
- }
- if (!(expr instanceof StringValueStream)) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Value Facet expressions must be castable to string expressions, "
- + "the following expression in value facet '" + name + "' is not: " + facetRequest.expression);
- }
-
- ValueFacet facet = new ValueFacet(name, (StringValueStream)expr);
-
- // Check if the value facet is sorted
- if (facetRequest.sort != null) {
- facet.setSort(constructSort(facetRequest.sort, expressions));
- }
- return facet;
- }
-
- /*
- * Pivot Facets
- */
-
- private static PivotFacet constructPivotFacet(String name, AnalyticsPivotFacetRequest facetRequest, ExpressionFactory expressionFactory, Map<String, AnalyticsExpression> expressions) throws SolrException {
- PivotNode<?> topPivot = null;
-
- // Pivots
- if (facetRequest.pivots == null || facetRequest.pivots.size() == 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Pivot Facets must contain at least one pivot to facet over, '" + name + "' has none.");
- }
-
- ListIterator<AnalyticsPivotRequest> iter = facetRequest.pivots.listIterator(facetRequest.pivots.size());
- while (iter.hasPrevious()) {
- topPivot = constructPivot(iter.previous(), topPivot, expressionFactory, expressions);
- }
-
- return new PivotFacet(name, topPivot);
- }
-
- @SuppressWarnings({"unchecked", "rawtypes"})
- private static PivotNode<?> constructPivot(AnalyticsPivotRequest pivotRequest,
- PivotNode<?> childPivot,
- ExpressionFactory expressionFactory,
- Map<String, AnalyticsExpression> expressions) throws SolrException {
- if (pivotRequest.name == null || pivotRequest.name.length() == 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Pivots must have a name.");
- }
- if (pivotRequest.expression == null) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Pivots must have an expression to facet over, '" + pivotRequest.name + "' does not.");
- }
-
- // The second parameter must be a mapping expression
- AnalyticsValueStream expr = expressionFactory.createExpression(pivotRequest.expression);
- if (!expr.getExpressionType().isUnreduced()) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Pivot expressions must be mapping expressions, "
- + "the following expression in pivot '" + pivotRequest.name + "' contains a reduction: " + pivotRequest.expression);
- }
- if (!(expr instanceof StringValueStream)) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Pivot expressions must be castable to string expressions, "
- + "the following expression in pivot '" + pivotRequest.name + "' is not: '" + pivotRequest.expression);
- }
-
- PivotNode<?> pivot;
- if (childPivot == null) {
- pivot = new PivotNode.PivotLeaf(pivotRequest.name, (StringValueStream)expr);
- } else {
- pivot = new PivotNode.PivotBranch(pivotRequest.name, (StringValueStream)expr, childPivot);
- }
-
- // Check if the pivot is sorted
- if (pivotRequest.sort != null) {
- pivot.setSort(constructSort(pivotRequest.sort, expressions));
- }
- return pivot;
- }
-
- /*
- * Range Facets
- */
-
- private static RangeFacet constructRangeFacet(String name, AnalyticsRangeFacetRequest facetRequest, IndexSchema schema) throws SolrException {
- if (facetRequest.field == null || facetRequest.field.length() == 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Range Facets must specify a field to facet over, '" +name + "' does not.");
- }
- SchemaField field = schema.getFieldOrNull(facetRequest.field);
- if (field == null) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Range Facets must have a valid field as the second parameter. The '" + name + "' facet "
- + "tries to facet over the non-existent field: " + facetRequest.field);
- }
-
- if (facetRequest.start == null || facetRequest.start.length() == 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Range Facets must specify a start value, '" +name + "' does not.");
- }
- if (facetRequest.end == null || facetRequest.end.length() == 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Range Facets must specify a end value, '" +name + "' does not.");
- }
- if (facetRequest.gaps == null || facetRequest.gaps.size() == 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Range Facets must specify a gap or list of gaps to determine facet buckets, '" +name + "' does not.");
- }
- RangeFacet facet = new RangeFacet(name, field, facetRequest.start, facetRequest.end, facetRequest.gaps);
-
- facet.setHardEnd(facetRequest.hardend);
-
- if (facetRequest.include != null && facetRequest.include.size() > 0) {
- facet.setInclude(constructInclude(facetRequest.include));
- }
- if (facetRequest.others != null && facetRequest.others.size() > 0) {
- facet.setOthers(constructOthers(facetRequest.others, name));
- }
- return facet;
- }
-
- private static EnumSet<FacetRangeInclude> constructInclude(List<String> includes) throws SolrException {
- return FacetRangeInclude.parseParam(includes.toArray(new String[includes.size()]));
- }
-
- private static EnumSet<FacetRangeOther> constructOthers(List<String> othersRequest, String facetName) throws SolrException {
- EnumSet<FacetRangeOther> others = EnumSet.noneOf(FacetRangeOther.class);
- for (String rawOther : othersRequest) {
- if (!others.add(FacetRangeOther.get(rawOther))) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Duplicate include value '" + rawOther + "' found in range facet '" + facetName + "'");
- }
- }
- if (others.contains(FacetRangeOther.NONE)) {
- if (others.size() > 1) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Include value 'NONE' is used with other includes in a range facet '" + facetName + "'. "
- + "If 'NONE' is used, it must be the only include.");
- }
- return EnumSet.noneOf(FacetRangeOther.class);
- }
- if (others.contains(FacetRangeOther.ALL)) {
- if (others.size() > 1) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Include value 'ALL' is used with other includes in a range facet '" + facetName + "'. "
- + "If 'ALL' is used, it must be the only include.");
- }
- return EnumSet.of(FacetRangeOther.BEFORE, FacetRangeOther.BETWEEN, FacetRangeOther.AFTER);
- }
- return others;
- }
-
- /*
- * Query Facets
- */
-
- private static QueryFacet constructQueryFacet(String name, AnalyticsQueryFacetRequest facetRequest) throws SolrException {
- if (facetRequest.queries == null || facetRequest.queries.size() == 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Query Facets must be contain at least 1 query to facet over, '" + name + "' does not.");
- }
-
- // The first param must be the facet name
- return new QueryFacet(name, facetRequest.queries);
- }
-
- /*
- * Facet Sorting
- */
-
- private static FacetSortSpecification constructSort(AnalyticsSortRequest sortRequest, Map<String, AnalyticsExpression> expressions) throws SolrException {
- if (sortRequest.criteria == null || sortRequest.criteria.size() == 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "Sorts must be given at least 1 criteria.");
- }
-
- return new FacetSortSpecification(constructSortCriteria(sortRequest.criteria, expressions), sortRequest.limit, sortRequest.offset);
- }
-
- private static FacetResultsComparator constructSortCriteria(List<AnalyticsSortCriteriaRequest> criteria, Map<String, AnalyticsExpression> expressions) {
- ArrayList<FacetResultsComparator> comparators = new ArrayList<>();
- for (AnalyticsSortCriteriaRequest criterion : criteria) {
- FacetResultsComparator comparator;
- if (criterion instanceof AnalyticsExpressionSortRequest) {
- comparator = constructExpressionSortCriteria((AnalyticsExpressionSortRequest) criterion, expressions);
- } else if (criterion instanceof AnalyticsFacetValueSortRequest) {
- comparator = constructFacetValueSortCriteria((AnalyticsFacetValueSortRequest) criterion);
- } else {
- // Shouldn't happen
- throw new SolrException(ErrorCode.BAD_REQUEST,"Sort Criteria must either be expressions or facetValues, '" + criterion.getClass().getName() + "' given.");
- }
- if (criterion.direction != null && criterion.direction.length() > 0) {
- if (sortAscending.test(criterion.direction)) {
- comparator.setDirection(true);
- } else if (sortDescending.test(criterion.direction)) {
- comparator.setDirection(false);
- } else {
- throw new SolrException(ErrorCode.BAD_REQUEST,"Sort direction '" + criterion.direction + " is not a recognized direction.");
- }
- }
- comparators.add(comparator);
- }
- return DelegatingComparator.joinComparators(comparators);
- }
-
- private static FacetResultsComparator constructExpressionSortCriteria(AnalyticsExpressionSortRequest criterion, Map<String, AnalyticsExpression> expressions) {
- if (criterion.expression == null || criterion.expression.length() == 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"Expression Sorts must contain an expression parameter, none given.");
- }
-
- AnalyticsExpression expression = expressions.get(criterion.expression);
- if (expression == null) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"Sort Expression not defined within the grouping: " + criterion.expression);
- }
- if (!(expression.getExpression() instanceof ComparableValue)) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"Expression Sorts must be comparable, the following is not: " + criterion.expression);
- }
- return ((ComparableValue)expression.getExpression()).getObjectComparator(expression.getName());
- }
-
- private static FacetResultsComparator constructFacetValueSortCriteria(AnalyticsFacetValueSortRequest criterion) {
- return new FacetValueComparator();
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/java/org/apache/solr/analytics/ExpressionFactory.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/ExpressionFactory.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/ExpressionFactory.java
deleted file mode 100644
index 18020b7..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/ExpressionFactory.java
+++ /dev/null
@@ -1,891 +0,0 @@
-/*
- * 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.
- */
-package org.apache.solr.analytics;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.solr.analytics.function.MergingReductionCollectionManager;
-import org.apache.solr.analytics.function.ReductionCollectionManager;
-import org.apache.solr.analytics.function.mapping.*;
-import org.apache.solr.analytics.function.mapping.ComparisonFunction.GTEFunction;
-import org.apache.solr.analytics.function.mapping.ComparisonFunction.GTFunction;
-import org.apache.solr.analytics.function.mapping.ComparisonFunction.LTEFunction;
-import org.apache.solr.analytics.function.mapping.ComparisonFunction.LTFunction;
-import org.apache.solr.analytics.function.mapping.ConcatFunction.SeparatedConcatFunction;
-import org.apache.solr.analytics.function.mapping.DecimalNumericConversionFunction.CeilingFunction;
-import org.apache.solr.analytics.function.mapping.DecimalNumericConversionFunction.FloorFunction;
-import org.apache.solr.analytics.function.mapping.DecimalNumericConversionFunction.RoundFunction;
-import org.apache.solr.analytics.function.mapping.LogicFunction.AndFunction;
-import org.apache.solr.analytics.function.mapping.LogicFunction.OrFunction;
-import org.apache.solr.analytics.function.reduction.*;
-import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
-import org.apache.solr.analytics.value.*;
-import org.apache.solr.analytics.value.AnalyticsValueStream.ExpressionType;
-import org.apache.solr.analytics.value.constant.ConstantValue;
-import org.apache.solr.analytics.function.field.*;
-import org.apache.solr.analytics.function.ReductionFunction;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.SolrException.ErrorCode;
-import org.apache.solr.schema.BoolField;
-import org.apache.solr.schema.DatePointField;
-import org.apache.solr.schema.DoublePointField;
-import org.apache.solr.schema.FieldType;
-import org.apache.solr.schema.FloatPointField;
-import org.apache.solr.schema.IndexSchema;
-import org.apache.solr.schema.IntPointField;
-import org.apache.solr.schema.LongPointField;
-import org.apache.solr.schema.SchemaField;
-import org.apache.solr.schema.StrField;
-import org.apache.solr.schema.TrieDateField;
-import org.apache.solr.schema.TrieDoubleField;
-import org.apache.solr.schema.TrieFloatField;
-import org.apache.solr.schema.TrieIntField;
-import org.apache.solr.schema.TrieLongField;
-
-/**
- * A factory to parse and create expressions, and capture information about those expressions along the way.
- *
- * <p>
- * In order to use, first call {@link #startRequest()} and create all ungrouped expressions,
- * then call {@link #createReductionManager} to get the ungrouped reduction manager.
- * <br>
- * Then for each grouping call {@link #startGrouping()} first then create all expressions within that grouping,
- * finally calling {@link #createGroupingReductionManager} to get the reduction manager for that grouping.
- */
-public class ExpressionFactory {
- private static final Pattern functionNamePattern = Pattern.compile("^\\s*([^().\\s]+)\\s*(?:\\(.*\\)\\s*)?$", Pattern.CASE_INSENSITIVE);
- private static final Pattern functionParamsPattern = Pattern.compile("^\\s*(?:[^(.)]+)\\s*\\(\\s*(.+)\\s*\\)\\s*$", Pattern.CASE_INSENSITIVE);
- private static final String funtionVarParamUniqueName = ".%s_(%d)";
-
- /**
- * Used to denote a variable length parameter.
- */
- public final static String variableLengthParamSuffix = "..";
- /**
- * The character used to denote the start of a for each lambda expression
- */
- public final static char variableForEachSep = ':';
- /**
- * The character used to denote the looped parameter in the for each lambda expression
- */
- public final static char variableForEachParam = '_';
-
- private HashMap<String, VariableFunctionInfo> systemVariableFunctions;
- private HashMap<String, VariableFunctionInfo> variableFunctions;
- private HashSet<String> variableFunctionNameHistory;
-
- private HashMap<String, CreatorFunction> expressionCreators;
- private final ConstantFunction constantCreator;
-
- private LinkedHashMap<String, ReductionFunction> reductionFunctions;
- private LinkedHashMap<String, ReductionDataCollector<?>> collectors;
- private LinkedHashMap<String, AnalyticsField> fields;
- private HashMap<String, AnalyticsValueStream> expressions;
-
- private IndexSchema schema;
-
- private Map<String, ReductionDataCollector<?>> groupedCollectors;
- private Map<String, AnalyticsField> groupedFields;
- private boolean isGrouped;
-
- public ExpressionFactory(IndexSchema schema) {
- this.schema = schema;
-
- expressionCreators = new HashMap<>();
- systemVariableFunctions = new HashMap<>();
-
- constantCreator = ConstantValue.creatorFunction;
- addSystemFunctions();
- }
-
- /**
- * Get the index schema used by this factory.
- *
- * @return the index schema
- */
- public IndexSchema getSchema() {
- return schema;
- }
-
- /**
- * Prepare the factory to start building the request.
- */
- public void startRequest() {
- reductionFunctions = new LinkedHashMap<>();
- collectors = new LinkedHashMap<>();
- fields = new LinkedHashMap<>();
- expressions = new HashMap<>();
-
- variableFunctions = new HashMap<>();
- variableFunctions.putAll(systemVariableFunctions);
- variableFunctionNameHistory = new HashSet<>();
-
- isGrouped = false;
- }
-
- /**
- * Prepare the factory to start building the next grouping.
- * <br>
- * NOTE: MUST be called before each new grouping.
- */
- public void startGrouping() {
- groupedCollectors = new HashMap<>();
- groupedFields = new HashMap<>();
-
- isGrouped = true;
- }
-
- /**
- * Add a system function to the expression factory.
- * This will be treated as a native function and not a variable function.
- *
- * @param functionName the unique name for the function
- * @param functionCreator the creator function to generate an expression
- * @return this factory, to easily chain function adds
- * @throws SolrException if the functionName is not unique
- */
- public ExpressionFactory addSystemFunction(final String functionName, final CreatorFunction functionCreator) throws SolrException {
- if (expressionCreators.containsKey(functionName)) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"System function " + functionName + " defined twice.");
- }
- expressionCreators.put(CountFunction.name, CountFunction.creatorFunction);
- return this;
- }
-
- /**
- * Add a variable function that will be treated like a system function.
- *
- * @param functionName the function's name
- * @param functionParams the comma separated and ordered parameters of the function (e.g. {@code "a,b"} )
- * @param returnSignature the return signature of the variable function (e.g. {@code div(sum(a,b),count(b))} )
- * @return this factory, to easily chain function adds
- * @throws SolrException if the name of the function is not unique or the syntax of either signature is incorrect
- */
- public ExpressionFactory addSystemVariableFunction(final String functionName, final String functionParams, final String returnSignature) throws SolrException {
- return addVariableFunction(functionName,
- Arrays.stream(functionParams.split(",")).map(param -> param.trim()).toArray(size -> new String[size]),
- returnSignature,
- systemVariableFunctions);
- }
-
- /**
- * Add a variable function that was defined in an analytics request.
- *
- * @param functionSignature the function signature of the variable function (e.g. {@code func(a,b)} )
- * @param returnSignature the return signature of the variable function (e.g. {@code div(sum(a,b),count(b))} )
- * @return this factory, to easily chain function adds
- * @throws SolrException if the name of the function is not unique or the syntax of either signature is incorrect
- */
- public ExpressionFactory addUserDefinedVariableFunction(final String functionSignature, final String returnSignature) throws SolrException {
- return addVariableFunction(functionSignature, returnSignature, variableFunctions);
- }
-
- /**
- * Add a variable function to the given map of variable functions.
- *
- * @param functionSignature the function signature of the variable function (e.g. {@code func(a,b)} )
- * @param returnSignature the return signature of the variable function (e.g. {@code div(sum(a,b),count(b))} )
- * @param variableFunctions the map of variable functions to add the new function to
- * @return this factory, to easily chain function adds
- * @throws SolrException if the name of the function is not unique or the syntax of either signature is incorrect
- */
- private ExpressionFactory addVariableFunction(final String functionSignature,
- final String returnSignature,
- Map<String,VariableFunctionInfo> variableFunctions) throws SolrException {
- addVariableFunction(getFunctionName(functionSignature), getParams(functionSignature, null, null), returnSignature, variableFunctions);
- return this;
- }
-
- /**
- * Add a variable function to the given map of variable functions.
- *
- * @param functionName the function's name
- * @param functionParams the parameters of the function (this is ordered)
- * @param returnSignature the return signature of the variable function (e.g. {@code div(sum(a,b),count(b))} )
- * @param variableFunctions the map of variable functions to add the new function to
- * @return this factory, to easily chain function adds
- * @throws SolrException if the name of the function is not unique or the syntax of either signature is incorrect
- */
- private ExpressionFactory addVariableFunction(final String functionName,
- final String[] functionParams,
- final String returnSignature,
- Map<String,VariableFunctionInfo> variableFunctions) throws SolrException {
- if (expressionCreators.containsKey(functionName)) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"Users cannot define a variable function with the same name as an existing function: " + functionName);
- }
- VariableFunctionInfo varFuncInfo = new VariableFunctionInfo();
- varFuncInfo.params = functionParams;
- varFuncInfo.returnSignature = returnSignature;
- variableFunctions.put(functionName, varFuncInfo);
- return this;
- }
-
- /**
- * Create a reduction manager to manage the collection of all expressions that have been created since
- * {@link #startRequest()} was called.
- *
- * @param isCloudCollection whether the request is a distributed request
- * @return a reduction manager
- */
- public ReductionCollectionManager createReductionManager(boolean isCloudCollection) {
- ReductionDataCollector<?>[] collectorsArr = new ReductionDataCollector<?>[collectors.size()];
- collectors.values().toArray(collectorsArr);
- if (isCloudCollection) {
- return new MergingReductionCollectionManager(collectorsArr, fields.values());
- } else {
- return new ReductionCollectionManager(collectorsArr, fields.values());
- }
- }
-
- /**
- * Create a reduction manager to manage the collection of all expressions that have been created since
- * {@link #startGrouping()} was called.
- *
- * @param isCloudCollection whether the request is a distributed request
- * @return a reduction manager
- */
- public ReductionCollectionManager createGroupingReductionManager(boolean isCloudCollection) {
- ReductionDataCollector<?>[] collectorsArr = new ReductionDataCollector<?>[groupedCollectors.size()];
- groupedCollectors.values().toArray(collectorsArr);
- if (isCloudCollection) {
- return new MergingReductionCollectionManager(collectorsArr, groupedFields.values());
- } else {
- return new ReductionCollectionManager(collectorsArr, groupedFields.values());
- }
- }
-
- /**
- * Parse and build an expression from the given expression string.
- *
- * @param expressionStr string that represents the desired expression
- * @return the object representation of the expression
- * @throws SolrException if an error occurs while constructing the expression
- */
- public AnalyticsValueStream createExpression(String expressionStr) throws SolrException {
- return createExpression(expressionStr, new HashMap<>(), null, null);
- }
-
- /**
- * Create an expression from the given expression string, with the given variable function information.
- *
- * @param expressionStr string that represents the desired expression
- * @param varFuncParams the current set of variable function parameters and their values. If this expression is not a variable function
- * return signature, the map should be empty.
- * @param varFuncVarParamName if the current expression is a variable function return signature, this must be the name of the variable length
- * parameter if it is included in the function signature.
- * @param varFuncVarParamValues if the current expression is a variable function return signature, this must be the array values of the variable length
- * parameter if they are included when calling the function.
- * @return the object representation of the expression
- * @throws SolrException if an error occurs while constructing the expression
- */
- private AnalyticsValueStream createExpression(String expressionStr, Map<String,AnalyticsValueStream> varFuncParams,
- String varFuncVarParamName, String[] varFuncVarParamValues) throws SolrException {
- AnalyticsValueStream expression;
- expressionStr = expressionStr.trim();
-
- boolean isField = false;
- try {
- // Try to make a constant value
- expression = constantCreator.apply(expressionStr);
- } catch (SolrException e1) {
- // Not a constant
- // If the expression has parens, it is an expression otherwise it is a field
- if (!expressionStr.contains("(")) {
- // Try to make a field out of it
- expression = createField(schema.getField(expressionStr));
- isField = true;
- } else {
- // Must be a function
- expression = createFunction(expressionStr, varFuncParams, varFuncVarParamName, varFuncVarParamValues);
- }
- }
-
- // Try to use an already made expression instead of the new one.
- // This will decrease the amount of collection needed to be done.
- if (expressions.containsKey(expression.getExpressionStr())) {
- expression = expressions.get(expression.getExpressionStr());
- // If this is a grouped expression, make sure that the reduction info for the expression is included in the grouped reduction manager.
- if (expression.getExpressionType() == ExpressionType.REDUCTION && isGrouped) {
- ((ReductionFunction)expression).synchronizeDataCollectors( collector -> {
- groupedCollectors.put(collector.getExpressionStr(), collector);
- return collector;
- });
- }
- }
- else {
- expressions.put(expression.getExpressionStr(), expression);
- // Make sure that the reduction info for the expression is included in the reduction manager and grouped reduction manager if necessary.
- if (expression.getExpressionType() == ExpressionType.REDUCTION) {
- reductionFunctions.put(expression.getExpressionStr(), (ReductionFunction)expression);
- ((ReductionFunction)expression).synchronizeDataCollectors( collector -> {
- String collectorStr = collector.getExpressionStr();
- ReductionDataCollector<?> usedCollector = collectors.get(collectorStr);
- if (usedCollector == null) {
- usedCollector = collector;
- collectors.put(collectorStr, collector);
- }
- if (isGrouped) {
- groupedCollectors.put(collectorStr, usedCollector);
- }
- return usedCollector;
- });
- }
- // Add the field info to the reduction manager
- if (isField) {
- fields.put(expression.getExpressionStr(), (AnalyticsField)expression);
- }
- }
- // If this is a grouped expression, make sure that the field info is included in the grouped reduction manager.
- if (isField && isGrouped) {
- groupedFields.put(expression.getExpressionStr(), (AnalyticsField)expression);
- }
- return expression;
- }
-
- /**
- * Create a function expression from the given expression string, with the given variable function information.
- *
- * @param expressionStr string that represents the desired expression
- * @param varFuncParams the current set of variable function parameters and their values. If this expression is not a variable function
- * return signature, the map should be empty.
- * @param varFuncVarParamName if the current expression is a variable function return signature, this must be the name of the variable length
- * parameter if it is included in the function signature.
- * @param varFuncVarParamValues if the current expression is a variable function return signature, this must be the array values of the variable length
- * parameter if they are included when calling the function.
- * @return the object representation of the expression
- * @throws SolrException if an error occurs while constructing the expression
- */
- private AnalyticsValueStream createFunction(String expressionStr, Map<String,AnalyticsValueStream> varFuncParams,
- String varFuncVarParamName, String[] varFuncVarParamValues) throws SolrException {
- AnalyticsValueStream expression = null;
- String name = getFunctionName(expressionStr);
-
- final String[] params = getParams(expressionStr, varFuncVarParamName, varFuncVarParamValues);
- AnalyticsValueStream[] paramStreams = new AnalyticsValueStream[params.length];
-
- boolean allParamsConstant = true;
-
- for (int i = 0; i < params.length; i++) {
- // First check if the parameter is a variable function variable otherwise create the expression
- if (varFuncParams.containsKey(params[i])) {
- paramStreams[i] = varFuncParams.get(params[i]);
- } else {
- paramStreams[i] = createExpression(params[i], varFuncParams, varFuncVarParamName, varFuncVarParamValues);
- }
-
- // Then update whether all of the params are constant
- allParamsConstant &= paramStreams[i].getExpressionType().equals(ExpressionType.CONST);
- }
-
- // Check to see if the function name is a variable function name, if so apply the variables to the return signature
- if (variableFunctions.containsKey(name)) {
- if (variableFunctionNameHistory.contains(name)) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"The following variable function is self referencing : " + name);
- }
- variableFunctionNameHistory.add(name);
- VariableFunctionInfo newVarFunc = variableFunctions.get(name);
- Map<String, AnalyticsValueStream> newVarFuncParams = new HashMap<>();
-
- boolean varLenEnd = false;
-
- if (paramStreams.length < newVarFunc.params.length) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"The variable function '" + name + "' requires at least " + newVarFunc.params.length + " parameters."
- + " Only " + paramStreams.length + " arguments given in the following invocation : " + expressionStr);
- }
- for (int i = 0; i < newVarFunc.params.length; ++i) {
- String variable = newVarFunc.params[i];
- if (variable.endsWith(variableLengthParamSuffix)) {
- if (i != newVarFunc.params.length - 1) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"The following invocation of a variable function has the incorrect number of arguments : " + expressionStr);
- }
- variable = variable.substring(0, variable.length() - variableLengthParamSuffix.length()).trim();
- int numVars = paramStreams.length - i;
- String[] newVarFuncVarParamValues = new String[numVars];
- for (int j = 0; j < numVars; ++j) {
- // Create a new name for each variable length parameter value
- String paramName = String.format(Locale.ROOT, funtionVarParamUniqueName, variable, j);
- newVarFuncVarParamValues[j] = paramName;
- newVarFuncParams.put(paramName, paramStreams[i + j]);
- }
- expression = createFunction(newVarFunc.returnSignature, newVarFuncParams, variable, newVarFuncVarParamValues);
- varLenEnd = true;
- } else {
- newVarFuncParams.put(variable, paramStreams[i]);
- }
- }
- if (!varLenEnd) {
- if (paramStreams.length > newVarFunc.params.length) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"The variable function '" + name + "' requires " + newVarFunc.params.length + " parameters."
- + " " + paramStreams.length + " arguments given in the following invocation : " + expressionStr);
- }
- expression = createExpression(newVarFunc.returnSignature, newVarFuncParams, null, null);
- }
- variableFunctionNameHistory.remove(name);
- } else if (expressionCreators.containsKey(name)) {
- // It is a regular system function
- expression = expressionCreators.get(name).apply(paramStreams);
- } else {
- throw new SolrException(ErrorCode.BAD_REQUEST,"The following function does not exist: " + name);
- }
-
- // If the all params are constant, then try to convert the expression to a constant value.
- expression = expression.convertToConstant();
-
- return expression;
- }
-
- /**
- * Create an {@link AnalyticsField} out of the given {@link SchemaField}.
- * <p>
- * Currently only fields with doc-values enabled are supported.
- *
- * @param field the field to convert for analytics
- * @return an analytics representation of the field
- * @throws SolrException if the field is not supported by the analytics framework
- */
- private AnalyticsField createField(SchemaField field) throws SolrException {
- String fieldName = field.getName();
- if (fields.containsKey(fieldName)) {
- return fields.get(fieldName);
- }
- if (!field.hasDocValues()) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"The field "+fieldName+" does not have docValues enabled.");
- }
- boolean multivalued = field.multiValued();
- FieldType fieldType = field.getType();
- AnalyticsField aField;
- if (fieldType instanceof BoolField) {
- if (multivalued) {
- aField = new BooleanMultiField(fieldName);
- } else {
- aField = new BooleanField(fieldName);
- }
- } else if (fieldType instanceof TrieIntField) {
- if (multivalued) {
- aField = new IntMultiTrieField(fieldName);
- } else {
- aField = new IntField(fieldName);
- }
- } else if (fieldType instanceof IntPointField) {
- if (multivalued) {
- aField = new IntMultiPointField(fieldName);
- } else {
- aField = new IntField(fieldName);
- }
- } else if (fieldType instanceof TrieLongField) {
- if (multivalued) {
- aField = new LongMultiTrieField(fieldName);
- } else {
- aField = new LongField(fieldName);
- }
- } else if (fieldType instanceof LongPointField) {
- if (multivalued) {
- aField = new LongMultiPointField(fieldName);
- } else {
- aField = new LongField(fieldName);
- }
- } else if (fieldType instanceof TrieFloatField) {
- if (multivalued) {
- aField = new FloatMultiTrieField(fieldName);
- } else {
- aField = new FloatField(fieldName);
- }
- } else if (fieldType instanceof FloatPointField) {
- if (multivalued) {
- aField = new FloatMultiPointField(fieldName);
- } else {
- aField = new FloatField(fieldName);
- }
- } else if (fieldType instanceof TrieDoubleField) {
- if (multivalued) {
- aField = new DoubleMultiTrieField(fieldName);
- } else {
- aField = new DoubleField(fieldName);
- }
- } else if (fieldType instanceof DoublePointField) {
- if (multivalued) {
- aField = new DoubleMultiPointField(fieldName);
- } else {
- aField = new DoubleField(fieldName);
- }
- } else if (fieldType instanceof TrieDateField) {
- if (multivalued) {
- aField = new DateMultiTrieField(fieldName);
- } else {
- aField = new DateField(fieldName);
- }
- } else if (fieldType instanceof DatePointField) {
- if (multivalued) {
- aField = new DateMultiPointField(fieldName);
- } else {
- aField = new DateField(fieldName);
- }
- } else if (fieldType instanceof StrField) {
- if (multivalued) {
- aField = new StringMultiField(fieldName);
- } else {
- aField = new StringField(fieldName);
- }
- } else {
- throw new SolrException(ErrorCode.BAD_REQUEST,"FieldType of the following field not supported by analytics: "+fieldName);
- }
- return aField;
- }
-
- /**
- * Get the name of the top function used in the given expression.
- *
- * @param expression the expression to find the function name of
- * @return the name of the function
- * @throws SolrException if the expression has incorrect syntax
- */
- private static String getFunctionName(String expression) throws SolrException {
- Matcher m = functionNamePattern.matcher(expression);
- if (!m.matches()) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"The following function has no name: " + expression);
- }
- String name = m.group(1);
- return name;
- }
-
- /**
- * Get the params of a function.
- *
- * @param function the function to parse
- * @return an array of param strings
- * @throws SolrException if the function has incorrect syntax
- */
- private static String[] getFunctionParams(String function) throws SolrException {
- return getParams(function, null, null);
- }
-
- /**
- * Parse a function expression string and break up the parameters of the function into separate strings.
- * <p>
- * The parsing replaces the variable length parameter, and lambda for-each's using the variable length parameter,
- * with the parameter values in the returned parameter string.
- * <p>
- * Parsing breaks up parameters by commas (',') and ignores ',' inside of extra parens and quotes (both ' and "), since these commas are either
- * splitting up the parameters of nested functions or are apart of strings.
- * <br>
- * The only escaping that needs to be done is " within a double quote string and ' within a single quote string and \ within any string.
- * For example\:
- * <ul>
- * <li> {@code func("This is \" the \\ escaping ' example")} will be treated as {@code func(This is " the \ escaping ' example)}
- * <li> {@code func('This is " the \\ escaping \' example')} will be treated as {@code func(This is " the \ escaping ' example)}
- * </ul>
- * In string constants the \ character is used to escape quotes, so it can never be used alone. in order to write a \ you must write \\
- *
- * @param expression the function expression to parse
- * @param varLengthParamName the name of the variable length parameter that is used in the expression, pass null if none is used.
- * @param varLengthParamValues the values of the variable length parameter that are used in the expression, pass null if none are used.
- * @return the parsed and split arguments to the function
- * @throws SolrException if the expression has incorrect syntax.
- */
- private static String[] getParams(String expression, String varLengthParamName, String[] varLengthParamValues) throws SolrException {
- Matcher m = functionParamsPattern.matcher(expression);
- if (!m.matches()) {
- return new String[0];
- }
- String paramsStr = m.group(1);
-
- ArrayList<String> paramsList = new ArrayList<String>();
- StringBuilder param = new StringBuilder();
-
- // Variables to help while filling out the values of for-each lambda functions.
- boolean inForEach = false;
- int forEachStart = -1;
- int forEachIter = -1;
- int forEachLevel = -1;
-
- // The current level of nested parenthesis, 0 means the iteration is in no nested parentheses
- int parenCount = 0;
- // If the iteration is currently in a single-quote string constant
- boolean singleQuoteOn = false;
- // If the iteration is currently in a double-quote string constant
- boolean doubleQuoteOn = false;
- // If the iteration is currently in any kind of string constant
- boolean quoteOn = false;
- // Is the next character escaped.
- boolean escaped = false;
-
- char[] chars = paramsStr.toCharArray();
-
- // Iterate through every character, building the params one at a time
- for (int i = 0; i < chars.length; ++i) {
- char c = chars[i];
-
- if (c == ' ' && !quoteOn) {
- // Ignore white space that is not in string constants
- continue;
- } else if (c == ',' && parenCount == 0 && !quoteOn) {
- // This signifies the end of one parameter and the start of another, since we are not in a nested parenthesis or a string constant
- String paramStr = param.toString();
- if (paramStr.length() == 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"Empty parameter in expression: " + expression);
- }
- // check to see if the parameter is a variable length parameter
- if (paramStr.equals(varLengthParamName)) {
- // Add every variable length parameter value, since there are a variable amount
- for (String paramName : varLengthParamValues) {
- paramsList.add(paramName);
- }
- } else {
- paramsList.add(paramStr);
- }
-
- param.setLength(0);
- continue;
- } else if (c == ',' && !quoteOn && inForEach) {
- // separate the for each parameters, so they can be replaced with the result of the for each
- if (param.charAt(param.length()-1) == variableForEachParam &&
- (param.charAt(param.length()-2) == '(' || param.charAt(param.length()-2) == ',')) {
- param.setLength(param.length()-1);
- param.append(varLengthParamValues[forEachIter++]);
- }
- } else if (c == '"' && !singleQuoteOn) {
- // Deal with escaping, or ending string constants
- if (doubleQuoteOn && !escaped) {
- doubleQuoteOn = false;
- quoteOn = false;
- } else if (!quoteOn) {
- doubleQuoteOn = true;
- quoteOn = true;
- } else {
- // only happens if escaped is true
- escaped = false;
- }
- } else if (c== '\'' && !doubleQuoteOn) {
- // Deal with escaping, or ending string constants
- if (singleQuoteOn && !escaped) {
- singleQuoteOn = false;
- quoteOn = false;
- } else if (!singleQuoteOn) {
- singleQuoteOn = true;
- quoteOn = true;
- } else {
- // only happens if escaped is true
- escaped = false;
- }
- } else if (c == '(' && !quoteOn) {
- // Reached a further level of nested parentheses
- parenCount++;
- } else if (c == ')' && !quoteOn) {
- // Returned from a level of nested parentheses
- parenCount--;
- if (parenCount < 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"The following expression has extra end parens: " + param.toString());
- }
- if (inForEach) {
- if (param.charAt(param.length()-1) == variableForEachParam &&
- (param.charAt(param.length()-2) == '(' || param.charAt(param.length()-2) == ',')) {
- param.setLength(param.length()-1);
- param.append(varLengthParamValues[forEachIter++]);
- }
- if (forEachLevel == parenCount) {
- // at the end of the for-each start the parsing of the for-each again, with the next value of the variable length parameter
- if (forEachIter == 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"For each statement for variable '" + varLengthParamName + "' has no use of lambda variable " + variableForEachParam);
- } else if (forEachIter < varLengthParamValues.length) {
- if (parenCount == 0) {
- param.append(')');
- paramsList.add(param.toString());
- param.setLength(0);
- } else {
- param.append(')');
- param.append(',');
- }
- i = forEachStart;
- continue;
- } else {
- inForEach = false;
- }
- }
- }
- }
- if (c == '\\') {
- // Escaping or escaped backslash
- if (!quoteOn) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"The following expression has escaped characters outside of quotation marks: " + expression.toString());
- }
- if (escaped) {
- escaped = false;
- } else {
- escaped = true;
- if (parenCount == 0) {
- continue;
- }
- }
- } else if (escaped) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"Invalid escape character '" + c + "' used in the following expression: " + expression.toString());
- }
- if (c == variableForEachSep && !quoteOn && varLengthParamName != null) {
- int varStart = param.length()-varLengthParamName.length();
- if (param.subSequence(varStart, param.length()).equals(varLengthParamName)) {
- inForEach = true;
- forEachStart = i;
- forEachIter = 0;
- forEachLevel = parenCount;
- param.setLength(varStart);
- continue;
- }
- throw new SolrException(ErrorCode.BAD_REQUEST,"For-each called on invalid parameter '" + param.toString().trim());
- }
- param.append(c);
- }
- String paramStr = param.toString().trim();
- if (paramStr.length() == 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"Empty parameter in expression: " + expression);
- }
- if (parenCount > 0) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"The following expression needs more end parens: " + param.toString());
- }
- if (quoteOn) {
- throw new SolrException(ErrorCode.BAD_REQUEST,"Misplaced quotation marks in expression: " + expression);
- }
- if (paramStr.equals(varLengthParamName)) {
- for (String paramName : varLengthParamValues) {
- paramsList.add(paramName);
- }
- } else {
- paramsList.add(paramStr);
- }
- return paramsList.toArray(new String[paramsList.size()]);
- }
-
- /**
- * Add the natively supported functionality.
- */
- public void addSystemFunctions() {
- // Mapping Functions
- expressionCreators.put(AbsoluteValueFunction.name, AbsoluteValueFunction.creatorFunction);
- expressionCreators.put(AndFunction.name, AndFunction.creatorFunction);
- expressionCreators.put(AddFunction.name, AddFunction.creatorFunction);
- expressionCreators.put(BottomFunction.name, BottomFunction.creatorFunction);
- expressionCreators.put(CeilingFunction.name, CeilingFunction.creatorFunction);
- expressionCreators.put(ConcatFunction.name, ConcatFunction.creatorFunction);
- expressionCreators.put(SeparatedConcatFunction.name, SeparatedConcatFunction.creatorFunction);
- expressionCreators.put(DateMathFunction.name, DateMathFunction.creatorFunction);
- expressionCreators.put(DateParseFunction.name, DateParseFunction.creatorFunction);
- expressionCreators.put(DivideFunction.name, DivideFunction.creatorFunction);
- expressionCreators.put(EqualFunction.name,EqualFunction.creatorFunction);
- expressionCreators.put(ExistsFunction.name,ExistsFunction.creatorFunction);
- expressionCreators.put(FillMissingFunction.name, FillMissingFunction.creatorFunction);
- expressionCreators.put(FilterFunction.name, FilterFunction.creatorFunction);
- expressionCreators.put(FloorFunction.name, FloorFunction.creatorFunction);
- expressionCreators.put(GTFunction.name,GTFunction.creatorFunction);
- expressionCreators.put(GTEFunction.name,GTEFunction.creatorFunction);
- expressionCreators.put(IfFunction.name, IfFunction.creatorFunction);
- expressionCreators.put(LogFunction.name,LogFunction.creatorFunction);
- expressionCreators.put(LTFunction.name,LTFunction.creatorFunction);
- expressionCreators.put(LTEFunction.name,LTEFunction.creatorFunction);
- expressionCreators.put(MultFunction.name, MultFunction.creatorFunction);
- expressionCreators.put(NegateFunction.name, NegateFunction.creatorFunction);
- expressionCreators.put(OrFunction.name, OrFunction.creatorFunction);
- expressionCreators.put(PowerFunction.name, PowerFunction.creatorFunction);
- expressionCreators.put(ReplaceFunction.name, ReplaceFunction.creatorFunction);
- expressionCreators.put(RemoveFunction.name, RemoveFunction.creatorFunction);
- expressionCreators.put(RoundFunction.name, RoundFunction.creatorFunction);
- expressionCreators.put(StringCastFunction.name, StringCastFunction.creatorFunction);
- expressionCreators.put(SubtractFunction.name, SubtractFunction.creatorFunction);
- expressionCreators.put(TopFunction.name, TopFunction.creatorFunction);
-
- // Reduction Functions
- expressionCreators.put(CountFunction.name, CountFunction.creatorFunction);
- expressionCreators.put(DocCountFunction.name, DocCountFunction.creatorFunction);
- expressionCreators.put(MaxFunction.name, MaxFunction.creatorFunction);
- expressionCreators.put(MeanFunction.name, MeanFunction.creatorFunction);
- expressionCreators.put(MedianFunction.name, MedianFunction.creatorFunction);
- expressionCreators.put(MinFunction.name, MinFunction.creatorFunction);
- expressionCreators.put(MissingFunction.name, MissingFunction.creatorFunction);
- expressionCreators.put(OrdinalFunction.name, OrdinalFunction.creatorFunction);
- expressionCreators.put(PercentileFunction.name, PercentileFunction.creatorFunction);
- expressionCreators.put(SumFunction.name, SumFunction.creatorFunction);
- expressionCreators.put(UniqueFunction.name, UniqueFunction.creatorFunction);
-
- // Variables
- addSystemVariableFunction(WeightedMeanVariableFunction.name, WeightedMeanVariableFunction.params, WeightedMeanVariableFunction.function);
- addSystemVariableFunction(SumOfSquaresVariableFunction.name, SumOfSquaresVariableFunction.params, SumOfSquaresVariableFunction.function);
- addSystemVariableFunction(SquareRootVariableFunction.name, SquareRootVariableFunction.params, SquareRootVariableFunction.function);
- addSystemVariableFunction(VarianceVariableFunction.name, VarianceVariableFunction.params, VarianceVariableFunction.function);
- addSystemVariableFunction(SandardDeviationVariableFunction.name, SandardDeviationVariableFunction.params, SandardDeviationVariableFunction.function);
- addSystemVariableFunction(CSVVariableFunction.name, CSVVariableFunction.params, CSVVariableFunction.function);
- addSystemVariableFunction(CSVOutputVariableFunction.name, CSVOutputVariableFunction.params, CSVOutputVariableFunction.function);
- }
-
- /**
- * Used for system analytics functions for initialization. Should take in a list of expression parameters and return an expression.
- */
- @FunctionalInterface
- public static interface CreatorFunction {
- AnalyticsValueStream apply(AnalyticsValueStream[] t) throws SolrException;
- }
- /**
- * Used to initialize analytics constants.
- */
- @FunctionalInterface
- public static interface ConstantFunction {
- AnalyticsValueStream apply(String t) throws SolrException;
- }
-}
-class VariableFunctionInfo {
- public String[] params;
- public String returnSignature;
-}
-class WeightedMeanVariableFunction {
- public static final String name = "wmean";
- public static final String params = "a,b";
- public static final String function = DivideFunction.name+"("+SumFunction.name+"("+MultFunction.name+"(a,b)),"+SumFunction.name+"("+FilterFunction.name+"(b,"+ExistsFunction.name+"(a))))";
-}
-class SumOfSquaresVariableFunction {
- public static final String name = "sumofsquares";
- public static final String params = "a";
- public static final String function = SumFunction.name+"("+PowerFunction.name+"(a,2))";
-}
-class SquareRootVariableFunction {
- public static final String name = "sqrt";
- public static final String params = "a";
- public static final String function = PowerFunction.name+"(a,0.5)";
-}
-class VarianceVariableFunction {
- public static final String name = "variance";
- public static final String params = "a";
- public static final String function = SubtractFunction.name+"("+MeanFunction.name+"("+PowerFunction.name+"(a,2)),"+PowerFunction.name+"("+MeanFunction.name+"(a),2))";
-}
-class SandardDeviationVariableFunction {
- public static final String name = "stddev";
- public static final String params = "a";
- public static final String function = SquareRootVariableFunction.name+"("+VarianceVariableFunction.name+"(a))";
-}
-class CSVVariableFunction {
- public static final String name = "csv";
- public static final String params = "a"+ExpressionFactory.variableLengthParamSuffix;
- public static final String function = SeparatedConcatFunction.name+"(',',a)";
-}
-class CSVOutputVariableFunction {
- public static final String name = "csv_output";
- public static final String params = "a"+ExpressionFactory.variableLengthParamSuffix;
- public static final String function = "concat_sep(',',a"+ExpressionFactory.variableForEachSep+FillMissingFunction.name+"("+SeparatedConcatFunction.name+"(';',"+ExpressionFactory.variableForEachParam+"),''))";
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AbstractSolrQueryFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AbstractSolrQueryFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AbstractSolrQueryFacet.java
deleted file mode 100644
index 230884a..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AbstractSolrQueryFacet.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.
- */
-package org.apache.solr.analytics.facet;
-
-import java.io.IOException;
-import java.util.function.Consumer;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.ScoreMode;
-import org.apache.lucene.search.SimpleCollector;
-import org.apache.solr.analytics.AnalyticsDriver;
-import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
-import org.apache.solr.request.SolrQueryRequest;
-import org.apache.solr.search.Filter;
-import org.apache.solr.search.SolrIndexSearcher;
-
-/**
- * Solr Query Facets are AnalyticsFacets that are calculated after the document streaming phase has occurred in the {@link AnalyticsDriver}
- * (during which StreamingFacets and overall expressions are calculated). {@link AbstractSolrQueryFacet}s should not be confused with {@link QueryFacet}s,
- * which are a specific sub-type.
- *
- * <p>
- * The filtering for these facets is done through issuing additional Solr queries, and collecting on the resulting documents.
- * Unlike streaming facets, which have an unspecified amount of facet values (facet buckets), the amount of facet values is determined by the user and
- * a Solr query is issued for each requested facet value.
- */
-public abstract class AbstractSolrQueryFacet extends AnalyticsFacet {
-
- protected AbstractSolrQueryFacet(String name) {
- super(name);
- }
-
- /**
- * Returns the set of {@link FacetValueQueryExecuter}s, one for each facet value, through the given consumer.
- *
- * Each of these executors will be executed after the streaming phase in the {@link AnalyticsDriver}.
- *
- * @param filter the overall filter representing the documents being used for the analytics request
- * @param queryRequest the queryRequest
- * @param consumer the consumer of each facet value's executer
- */
- public abstract void createFacetValueExecuters(final Filter filter, SolrQueryRequest queryRequest, Consumer<FacetValueQueryExecuter> consumer);
-
- /**
- * This executer is in charge of issuing the Solr query for a facet value and collecting results as the query is processed.
- */
- public class FacetValueQueryExecuter extends SimpleCollector {
- private final ReductionDataCollection collection;
- private final Query query;
-
- /**
- * Create an executer to collect the given reduction data from the given Solr query.
- *
- * @param collection The reduction data to collect while querying
- * @param query The query used to filter for the facet value
- */
- public FacetValueQueryExecuter(ReductionDataCollection collection, Query query) {
- this.collection = collection;
- this.query = query;
- }
-
- @Override
- public ScoreMode scoreMode() {
- return ScoreMode.COMPLETE_NO_SCORES;
- }
-
- @Override
- public void doSetNextReader(LeafReaderContext context) throws IOException {
- collectionManager.doSetNextReader(context);
- }
-
- @Override
- public void collect(int doc) throws IOException {
- collectionManager.collect(doc);
- collectionManager.apply();
- }
-
- /**
- * Start the collection for this facet value.
- *
- * @param searcher the solr searcher
- * @throws IOException if an exception occurs during the querying
- */
- public void execute(SolrIndexSearcher searcher) throws IOException {
- collectionManager.clearLastingCollectTargets();
- collectionManager.addLastingCollectTarget(collection);
- searcher.search(query, this);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AnalyticsFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AnalyticsFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AnalyticsFacet.java
deleted file mode 100644
index d9c0f8c..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AnalyticsFacet.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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.
- */
-package org.apache.solr.analytics.facet;
-
-import java.io.DataInput;
-import java.io.DataOutput;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.Map;
-
-import org.apache.solr.analytics.function.ExpressionCalculator;
-import org.apache.solr.analytics.function.ReductionCollectionManager;
-import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
-import org.apache.solr.analytics.function.reduction.data.ReductionData;
-import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
-import org.apache.solr.common.util.NamedList;
-
-/**
- * An abstract Facet to break up Analytics data over.
- */
-public abstract class AnalyticsFacet {
- protected final Map<String,ReductionDataCollection> reductionData;
- protected ReductionCollectionManager collectionManager;
- protected ExpressionCalculator expressionCalculator;
-
- protected final String name;
-
- public AnalyticsFacet(String name) {
- this.reductionData = new LinkedHashMap<>();
- this.name = name;
- }
-
- /**
- * Set the {@link ReductionCollectionManager} that manages the collection of the expressions
- * calculated with this facet.
- *
- * @param collectionManager The manager for relevant expressions
- */
- public void setReductionCollectionManager(ReductionCollectionManager collectionManager) {
- this.collectionManager = collectionManager;
- }
-
- /**
- * Set the {@link ExpressionCalculator} that calculates the collection of the expressions
- * requested for this facet.
- *
- * @param expressionCalculator The calculator for relevant expressions
- */
- public void setExpressionCalculator(ExpressionCalculator expressionCalculator) {
- this.expressionCalculator = expressionCalculator;
- }
-
- /**
- * Import the shard data from a bit-stream, exported by the {@link #exportShardData} method
- * in the each of the collection's shards.
- *
- * @param input The bit-stream to import the data from
- * @throws IOException if an exception occurs while reading from the {@link DataInput}
- */
- public void importShardData(DataInput input) throws IOException {
- int size = input.readInt();
- for (int i = 0; i < size; ++i) {
- importFacetValue(input, input.readUTF());
- }
- }
- /**
- * Import the next facet value's set of {@link ReductionData}.
- *
- * @param input the bit-stream to import the reduction data from
- * @param facetValue the next facet value
- * @throws IOException if an exception occurs while reading from the input
- */
- protected void importFacetValue(DataInput input, String facetValue) throws IOException {
- ReductionDataCollection dataCollection = reductionData.get(facetValue);
- if (dataCollection == null) {
- reductionData.put(facetValue, collectionManager.newDataCollectionIO());
- } else {
- collectionManager.prepareReductionDataIO(dataCollection);
- }
-
- collectionManager.mergeData();
- }
-
- /**
- * Export the shard data through a bit-stream, to be imported by the {@link #importShardData} method
- * in the originating shard.
- *
- * @param output The bit-stream to output the data through
- * @throws IOException if an exception occurs while writing to the {@link DataOutput}
- */
- public void exportShardData(DataOutput output) throws IOException {
- output.writeInt(reductionData.size());
- for (String facetValue : reductionData.keySet()) {
- exportFacetValue(output, facetValue);
- }
- }
- /**
- * Export the next facet value's set of {@link ReductionData}.
- *
- * @param output the bit-stream to output the reduction data to
- * @param facetValue the next facet value
- * @throws IOException if an exception occurs while reading from the input
- */
- protected void exportFacetValue(DataOutput output, String facetValue) throws IOException {
- output.writeUTF(facetValue);
-
- collectionManager.prepareReductionDataIO(reductionData.get(facetValue));
- collectionManager.exportData();
- }
-
- /**
- * Create the old olap-style response of the facet to be returned in the overall analytics response.
- *
- * @return the response of the facet
- */
- public NamedList<Object> createOldResponse() {
- NamedList<Object> nl = new NamedList<>();
- reductionData.forEach((facetVal, dataCol) -> {
- collectionManager.setData(dataCol);
- nl.add(facetVal, new NamedList<>(expressionCalculator.getResults()));
- });
- return nl;
- }
-
- /**
- * Create the response of the facet to be returned in the overall analytics response.
- *
- * @return the response of the facet
- */
- public Iterable<Map<String,Object>> createResponse() {
- LinkedList<Map<String,Object>> list = new LinkedList<>();
- reductionData.forEach((facetVal, dataCol) -> {
- Map<String, Object> bucket = new HashMap<>();
- bucket.put(AnalyticsResponseHeadings.FACET_VALUE, facetVal);
- collectionManager.setData(dataCol);
- expressionCalculator.addResults(bucket);
- list.add(bucket);
- });
- return list;
- }
-
- /**
- * Get the name of the Facet. This is unique for the grouping.
- *
- * @return The name of the Facet
- */
- public String getName() {
- return name;
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotFacet.java
deleted file mode 100644
index f9e35f7..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotFacet.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.
- */
-package org.apache.solr.analytics.facet;
-
-import java.io.DataInput;
-import java.io.DataOutput;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.solr.analytics.function.ExpressionCalculator;
-import org.apache.solr.analytics.function.ReductionCollectionManager;
-import org.apache.solr.common.util.NamedList;
-
-/**
- * A facet that takes in multiple ValueFacet expressions and does analytics calculations over each dimension given.
- */
-public class PivotFacet extends AnalyticsFacet implements StreamingFacet {
- private final PivotHead<?> pivotHead;
-
- @SuppressWarnings({"unchecked", "rawtypes"})
- public PivotFacet(String name, PivotNode<?> topPivot) {
- super(name);
- this.pivotHead = new PivotHead(topPivot);
- }
-
- @Override
- public void setReductionCollectionManager(ReductionCollectionManager collectionManager) {
- pivotHead.setReductionCollectionManager(collectionManager);
- }
-
- @Override
- public void setExpressionCalculator(ExpressionCalculator expressionCalculator) {
- pivotHead.setExpressionCalculator(expressionCalculator);
- }
-
- @Override
- public void addFacetValueCollectionTargets() {
- pivotHead.addFacetValueCollectionTargets();
- }
-
- @Override
- public void importShardData(DataInput input) throws IOException {
- pivotHead.importShardData(input);
- }
-
- @Override
- public void exportShardData(DataOutput output) throws IOException {
- pivotHead.exportShardData(output);
- }
-
- @Override
- public NamedList<Object> createOldResponse() {
- return new NamedList<>();
- }
-
- @Override
- public Iterable<Map<String,Object>> createResponse() {
- return pivotHead.createResponse();
- }
-}
-/**
- * Typed Pivot class that stores the overall Pivot data and head of the Pivot node chain.
- *
- * This class exists so that the {@link PivotFacet} class doesn't have to be typed ( {@code <T>} ).
- */
-class PivotHead<T> implements StreamingFacet {
- private final PivotNode<T> topPivot;
- private final Map<String, T> pivotValues;
-
- public PivotHead(PivotNode<T> topPivot) {
- this.topPivot = topPivot;
- this.pivotValues = new HashMap<>();
- }
-
- public void setReductionCollectionManager(ReductionCollectionManager collectionManager) {
- topPivot.setReductionCollectionManager(collectionManager);
- }
-
- public void setExpressionCalculator(ExpressionCalculator expressionCalculator) {
- topPivot.setExpressionCalculator(expressionCalculator);
- }
-
- @Override
- public void addFacetValueCollectionTargets() {
- topPivot.addFacetValueCollectionTargets(pivotValues);
- }
-
- public void importShardData(DataInput input) throws IOException {
- topPivot.importPivot(input, pivotValues);
- }
-
- public void exportShardData(DataOutput output) throws IOException {
- topPivot.exportPivot(output, pivotValues);
- }
-
- public Iterable<Map<String,Object>> createResponse() {
- return topPivot.getPivotedResponse(pivotValues);
- }
-}
\ No newline at end of file