/** * Determines what type of value source the expression represents. * * @param expression The expression representing the desired ValueSource * @return NUMBER_TYPE, DATE_TYPE, STRING_TYPE or -1 */ private static int getSourceType(String expression) { int paren = expression.indexOf('('); if (paren < 0) { return FIELD_TYPE; } String operation = expression.substring(0, paren).trim(); if (AnalyticsParams.NUMERIC_OPERATION_SET.contains(operation)) { return NUMBER_TYPE; } else if (AnalyticsParams.DATE_OPERATION_SET.contains(operation)) { return DATE_TYPE; } else if (AnalyticsParams.STRING_OPERATION_SET.contains(operation)) { return STRING_TYPE; } else if (operation.equals(AnalyticsParams.FILTER)) { return FILTER_TYPE; } throw new SolrException( ErrorCode.BAD_REQUEST, "The operation \"" + operation + "\" in [" + expression + "] is not supported."); }
/** * Recursively parses and breaks down the expression string to build a date ValueSource. * * @param schema The schema to pull fields from. * @param expressionString The expression string to build a ValueSource from. * @return The value source represented by the given expressionString */ @SuppressWarnings("deprecation") private static ValueSource buildDateSource(IndexSchema schema, String expressionString) { int paren = expressionString.indexOf('('); String[] arguments; if (paren < 0) { return buildFieldSource(schema, expressionString, DATE_TYPE); } else { arguments = ExpressionFactory.getArguments( expressionString.substring(paren + 1, expressionString.lastIndexOf(')')).trim()); } String operands = arguments[0]; String operation = expressionString.substring(0, paren).trim(); if (operation.equals(AnalyticsParams.CONSTANT_DATE)) { if (arguments.length != 1) { throw new SolrException( ErrorCode.BAD_REQUEST, "The constant date declaration [" + expressionString + "] does not have exactly 1 argument."); } try { return new ConstDateSource(TrieDateField.parseDate(operands)); } catch (ParseException e) { throw new SolrException( ErrorCode.BAD_REQUEST, "The constant " + operands + " cannot be converted into a date.", e); } } else if (operation.equals(AnalyticsParams.FILTER)) { return buildFilterSource(schema, operands, DATE_TYPE); } if (operation.equals(AnalyticsParams.DATE_MATH)) { List<ValueSource> subExpressions = new ArrayList<>(); boolean first = true; for (String argument : arguments) { ValueSource argSource; if (first) { first = false; argSource = buildDateSource(schema, argument); if (argSource == null) { throw new SolrException( ErrorCode.BAD_REQUEST, "\"" + AnalyticsParams.DATE_MATH + "\" requires the first argument be a date operation or field. [" + argument + "] is not a date operation or field."); } } else { argSource = buildStringSource(schema, argument); if (argSource == null) { throw new SolrException( ErrorCode.BAD_REQUEST, "\"" + AnalyticsParams.DATE_MATH + "\" requires that all arguments except the first be string operations. [" + argument + "] is not a string operation."); } } subExpressions.add(argSource); } return new DateMathFunction(subExpressions.toArray(new ValueSource[0])); } if (AnalyticsParams.NUMERIC_OPERATION_SET.contains(operation) || AnalyticsParams.STRING_OPERATION_SET.contains(operation)) { return null; } throw new SolrException( ErrorCode.BAD_REQUEST, "The operation [" + expressionString + "] is not supported."); }
/** * Recursively parses and breaks down the expression string to build a numeric ValueSource. * * @param schema The schema to pull fields from. * @param expressionString The expression string to build a ValueSource from. * @return The value source represented by the given expressionString */ private static ValueSource buildNumericSource(IndexSchema schema, String expressionString) { int paren = expressionString.indexOf('('); String[] arguments; String operands; if (paren < 0) { return buildFieldSource(schema, expressionString, NUMBER_TYPE); } else { try { operands = expressionString.substring(paren + 1, expressionString.lastIndexOf(')')).trim(); } catch (Exception e) { throw new SolrException( ErrorCode.BAD_REQUEST, "Missing closing parenthesis in [" + expressionString + "]"); } arguments = ExpressionFactory.getArguments(operands); } String operation = expressionString.substring(0, paren).trim(); if (operation.equals(AnalyticsParams.CONSTANT_NUMBER)) { if (arguments.length != 1) { throw new SolrException( ErrorCode.BAD_REQUEST, "The constant number declaration [" + expressionString + "] does not have exactly 1 argument."); } return new ConstDoubleSource(Double.parseDouble(arguments[0])); } else if (operation.equals(AnalyticsParams.NEGATE)) { if (arguments.length != 1) { throw new SolrException( ErrorCode.BAD_REQUEST, "The negate operation [" + expressionString + "] does not have exactly 1 argument."); } ValueSource argSource = buildNumericSource(schema, arguments[0]); if (argSource == null) { throw new SolrException( ErrorCode.BAD_REQUEST, "The operation \"" + AnalyticsParams.NEGATE + "\" requires a numeric field or operation as argument. \"" + arguments[0] + "\" is not a numeric field or operation."); } return new NegateDoubleFunction(argSource); } else if (operation.equals(AnalyticsParams.ABSOLUTE_VALUE)) { if (arguments.length != 1) { throw new SolrException( ErrorCode.BAD_REQUEST, "The absolute value operation [" + expressionString + "] does not have exactly 1 argument."); } ValueSource argSource = buildNumericSource(schema, arguments[0]); if (argSource == null) { throw new SolrException( ErrorCode.BAD_REQUEST, "The operation \"" + AnalyticsParams.NEGATE + "\" requires a numeric field or operation as argument. \"" + arguments[0] + "\" is not a numeric field or operation."); } return new AbsoluteValueDoubleFunction(argSource); } else if (operation.equals(AnalyticsParams.FILTER)) { return buildFilterSource(schema, operands, NUMBER_TYPE); } List<ValueSource> subExpressions = new ArrayList<>(); for (String argument : arguments) { ValueSource argSource = buildNumericSource(schema, argument); if (argSource == null) { throw new SolrException( ErrorCode.BAD_REQUEST, "The operation \"" + operation + "\" requires numeric fields or operations as arguments. \"" + argument + "\" is not a numeric field or operation."); } subExpressions.add(argSource); } if (operation.equals(AnalyticsParams.ADD)) { return new AddDoubleFunction(subExpressions.toArray(new ValueSource[0])); } else if (operation.equals(AnalyticsParams.MULTIPLY)) { return new MultiplyDoubleFunction(subExpressions.toArray(new ValueSource[0])); } else if (operation.equals(AnalyticsParams.DIVIDE)) { if (subExpressions.size() != 2) { throw new SolrException( ErrorCode.BAD_REQUEST, "The divide operation [" + expressionString + "] does not have exactly 2 arguments."); } return new DivDoubleFunction(subExpressions.get(0), subExpressions.get(1)); } else if (operation.equals(AnalyticsParams.POWER)) { if (subExpressions.size() != 2) { throw new SolrException( ErrorCode.BAD_REQUEST, "The power operation [" + expressionString + "] does not have exactly 2 arguments."); } return new PowDoubleFunction(subExpressions.get(0), subExpressions.get(1)); } else if (operation.equals(AnalyticsParams.LOG)) { if (subExpressions.size() != 2) { throw new SolrException( ErrorCode.BAD_REQUEST, "The log operation [" + expressionString + "] does not have exactly 2 arguments."); } return new LogDoubleFunction(subExpressions.get(0), subExpressions.get(1)); } if (AnalyticsParams.DATE_OPERATION_SET.contains(operation) || AnalyticsParams.STRING_OPERATION_SET.contains(operation)) { return null; } throw new SolrException( ErrorCode.BAD_REQUEST, "The operation [" + expressionString + "] is not supported."); }