summaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/search/grouping/request
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /container-search/src/main/java/com/yahoo/search/grouping/request
Publish
Diffstat (limited to 'container-search/src/main/java/com/yahoo/search/grouping/request')
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AddFunction.java42
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AggregatorNode.java53
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AllOperation.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AndFunction.java42
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ArrayAtLookup.java51
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AttributeFunction.java32
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AttributeValue.java32
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AvgAggregator.java20
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/AvgFunction.java42
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/BooleanValue.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/BucketResolver.java121
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/BucketValue.java54
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/CatFunction.java42
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ConstantValue.java29
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ConstantValueComparator.java24
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/CountAggregator.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DateFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DayOfMonthFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DayOfWeekFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DayOfYearFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DebugWaitFunction.java43
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DivFunction.java43
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DocIdNsSpecificValue.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DocumentValue.java24
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DoubleBucket.java41
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DoublePredefined.java48
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/DoubleValue.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/EachOperation.java26
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ExpressionVisitor.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/FixedWidthFunction.java34
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/FunctionNode.java78
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/GroupingExpression.java100
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/GroupingNode.java44
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/GroupingOperation.java582
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/HourOfDayFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/Infinite.java37
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/InfiniteValue.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/InterpolatedLookup.java51
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/LongBucket.java42
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/LongPredefined.java48
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/LongValue.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathACosFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathACosHFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathASinFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathASinHFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathATanFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathATanHFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathCbrtFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathCosFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathCosHFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathExpFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathFloorFunction.java16
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathFunctions.java69
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathHypotFunction.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathLog10Function.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathLog1pFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathLogFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathPowFunction.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathResolver.java121
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathSinFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathSinHFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathSqrtFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathTanFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MathTanHFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MaxAggregator.java20
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MaxFunction.java43
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/Md5Function.java33
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MinAggregator.java20
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MinFunction.java43
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MinuteOfHourFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ModFunction.java43
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MonthOfYearFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/MulFunction.java42
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/NegFunction.java23
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/NormalizeSubjectFunction.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/NowFunction.java21
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/OrFunction.java43
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/PredefinedFunction.java58
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/RawBucket.java40
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/RawBuffer.java123
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/RawPredefined.java48
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/RawValue.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/RelevanceValue.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ReverseFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/SecondOfMinuteFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/SizeFunction.java23
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/SortFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/StrCatFunction.java43
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/StrLenFunction.java23
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/StringBucket.java40
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/StringPredefined.java48
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/StringValue.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/SubFunction.java43
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/SumAggregator.java20
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/SummaryValue.java40
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/TimeFunctions.java148
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ToDoubleFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ToLongFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ToRawFunction.java23
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ToStringFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/UcaFunction.java63
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/XorAggregator.java20
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/XorBitFunction.java33
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/XorFunction.java43
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/YearFunction.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/YmumValue.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ZCurveXFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/ZCurveYFunction.java18
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/package-info.java7
-rw-r--r--container-search/src/main/java/com/yahoo/search/grouping/request/parser/GroupingParserInput.java14
110 files changed, 4213 insertions, 0 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AddFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AddFunction.java
new file mode 100644
index 00000000000..2f321a5854d
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AddFunction.java
@@ -0,0 +1,42 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents an add-function in a {@link GroupingExpression}. It evaluates to a number that equals the
+ * result of adding the results of all arguments together in the order they were given to the constructor.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class AddFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument, must evaluate to a number.
+ * @param arg2 The second compulsory argument, must evaluate to a number.
+ * @param argN The optional arguments, must evaluate to a number.
+ */
+ public AddFunction(GroupingExpression arg1, GroupingExpression arg2, GroupingExpression... argN) {
+ this(asList(arg1, arg2, argN));
+ }
+
+ private AddFunction(List<GroupingExpression> args) {
+ super("add", args);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param args The arguments to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the number of arguments is less than 2.
+ */
+ public static AddFunction newInstance(List<GroupingExpression> args) {
+ if (args.size() < 2) {
+ throw new IllegalArgumentException("Expected 2 or more arguments, got " + args.size() + ".");
+ }
+ return new AddFunction(args);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AggregatorNode.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AggregatorNode.java
new file mode 100644
index 00000000000..0df204506c1
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AggregatorNode.java
@@ -0,0 +1,53 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents an aggregated value in a {@link GroupingExpression}. Because it operates on a list of data, it
+ * can not be used as a document-level expression (i.e. level 0, see {@link GroupingExpression#resolveLevel(int)}). The
+ * contained expression is evaluated at the level of the aggregator minus 1.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public abstract class AggregatorNode extends GroupingExpression {
+
+ private final GroupingExpression exp;
+
+ protected AggregatorNode(String image) {
+ super(image + "()");
+ this.exp = null;
+ }
+
+ protected AggregatorNode(String image, GroupingExpression exp) {
+ super(image + "(" + exp.toString() + ")");
+ this.exp = exp;
+ }
+
+ /**
+ * Returns the expression that this node aggregates on.
+ *
+ * @return The expression.
+ */
+ public GroupingExpression getExpression() {
+ return exp;
+ }
+
+ @Override
+ public void resolveLevel(int level) {
+ super.resolveLevel(level);
+ if (level < 1) {
+ throw new IllegalArgumentException("Expression '" + this + "' not applicable for " +
+ GroupingOperation.getLevelDesc(level) + ".");
+ }
+ if (exp != null) {
+ exp.resolveLevel(level - 1);
+ }
+ }
+
+ @Override
+ public void visit(ExpressionVisitor visitor) {
+ super.visit(visitor);
+ if (exp != null) {
+ exp.visit(visitor);
+ }
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AllOperation.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AllOperation.java
new file mode 100644
index 00000000000..e78be0c1c1a
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AllOperation.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This is a grouping operation that processes the input list as a whole, as opposed to {@link EachOperation} which
+ * processes each element of that list separately.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class AllOperation extends GroupingOperation {
+
+ /**
+ * Constructs a new instance of this class.
+ */
+ public AllOperation() {
+ super("all");
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AndFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AndFunction.java
new file mode 100644
index 00000000000..3053153e5a3
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AndFunction.java
@@ -0,0 +1,42 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents an and-function in a {@link GroupingExpression}. It evaluates to a long that equals the result
+ * of and'ing the results of all arguments together in the order they were given to the constructor.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class AndFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument, must evaluate to a long.
+ * @param arg2 The second compulsory argument, must evaluate to a long.
+ * @param argN The optional arguments, must evaluate to a long.
+ */
+ public AndFunction(GroupingExpression arg1, GroupingExpression arg2, GroupingExpression... argN) {
+ this(asList(arg1, arg2, argN));
+ }
+
+ private AndFunction(List<GroupingExpression> args) {
+ super("and", args);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param args The arguments to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the number of arguments is less than 2.
+ */
+ public static AndFunction newInstance(List<GroupingExpression> args) {
+ if (args.size() < 2) {
+ throw new IllegalArgumentException("Expected 2 or more arguments, got " + args.size() + ".");
+ }
+ return new AndFunction(args);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ArrayAtLookup.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ArrayAtLookup.java
new file mode 100644
index 00000000000..1e613066bd4
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ArrayAtLookup.java
@@ -0,0 +1,51 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Represents access of array element in a document attribute in a {@link GroupingExpression}.
+ *
+ * The first argument should be the name of an array attribute in the
+ * input {@link com.yahoo.search.result.Hit}, while the second
+ * argument is evaluated as an integer and used as the index in that array.
+ * If the index argument is less than 0 returns the first array element;
+ * if the index is greater than or equal to size(array) returns the last array element;
+ * if the array is empty returns 0 (or NaN?).
+ * @author arnej27959
+ */
+@Beta
+public class ArrayAtLookup extends DocumentValue {
+
+ private final String attributeName;
+ private final GroupingExpression arg2;
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param attributeName The attribute name to assign to this.
+ */
+ public ArrayAtLookup(String attributeName, GroupingExpression indexArg) {
+ super("array.at(" + attributeName + ", " + indexArg + ")");
+ this.attributeName = attributeName;
+ this.arg2 = indexArg;
+ }
+
+ /**
+ * Returns the name of the attribute to retrieve from the input hit.
+ *
+ * @return The attribute name.
+ */
+ public String getAttributeName() {
+ return attributeName;
+ }
+
+ /**
+ * get the expression to evaluate before indexing
+ * @return grouping expression argument
+ */
+ public GroupingExpression getIndexArgument() {
+ return arg2;
+ }
+
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeFunction.java
new file mode 100644
index 00000000000..c16903ddca8
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeFunction.java
@@ -0,0 +1,32 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a document attribute function in a {@link GroupingExpression}. It evaluates to the value of the
+ * named attribute in the input {@link com.yahoo.search.result.Hit}.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class AttributeFunction extends DocumentValue {
+
+ private final String name;
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param attributeName The attribute name to assign to this.
+ */
+ public AttributeFunction(String attributeName) {
+ super("attribute(" + attributeName + ")");
+ name = attributeName;
+ }
+
+ /**
+ * Returns the name of the attribute to retrieve from the input hit.
+ *
+ * @return The attribute name.
+ */
+ public String getAttributeName() {
+ return name;
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeValue.java
new file mode 100644
index 00000000000..135463bf108
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AttributeValue.java
@@ -0,0 +1,32 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a document attribute value in a {@link GroupingExpression}. It evaluates to the value of the
+ * named attribute in the input {@link com.yahoo.search.result.Hit}.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class AttributeValue extends DocumentValue {
+
+ private final String name;
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param attributeName The attribute name to assign to this.
+ */
+ public AttributeValue(String attributeName) {
+ super(attributeName);
+ name = attributeName;
+ }
+
+ /**
+ * Returns the name of the attribute to retrieve from the input hit.
+ *
+ * @return The attribute name.
+ */
+ public String getAttributeName() {
+ return name;
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AvgAggregator.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AvgAggregator.java
new file mode 100644
index 00000000000..749b419488f
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AvgAggregator.java
@@ -0,0 +1,20 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents an average-aggregator in a {@link GroupingExpression}. It evaluates to the average value that
+ * the contained expression evaluated to over all the inputs.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class AvgAggregator extends AggregatorNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to aggregate on.
+ */
+ public AvgAggregator(GroupingExpression exp) {
+ super("avg", exp);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/AvgFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/AvgFunction.java
new file mode 100644
index 00000000000..c0474064741
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/AvgFunction.java
@@ -0,0 +1,42 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents a min-function in a {@link GroupingExpression}. It evaluates to a number that equals the
+ * average of the results of all arguments.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class AvgFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument, must evaluate to a number.
+ * @param arg2 The second compulsory argument, must evaluate to a number.
+ * @param argN The optional arguments, must evaluate to a number.
+ */
+ public AvgFunction(GroupingExpression arg1, GroupingExpression arg2, GroupingExpression... argN) {
+ this(asList(arg1, arg2, argN));
+ }
+
+ private AvgFunction(List<GroupingExpression> args) {
+ super("avg", args);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param args The arguments to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the number of arguments is less than 2.
+ */
+ public static AvgFunction newInstance(List<GroupingExpression> args) {
+ if (args.size() < 2) {
+ throw new IllegalArgumentException("Expected 2 or more arguments, got " + args.size() + ".");
+ }
+ return new AvgFunction(args);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/BooleanValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/BooleanValue.java
new file mode 100644
index 00000000000..c41cfa4c4f2
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/BooleanValue.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a constant {@link Boolean} value in a {@link GroupingExpression}.
+ *
+ * @author <a href="mailto:lulf@yahoo-inc.com">Ulf Lilleengen</a>
+ */
+public class BooleanValue extends ConstantValue<Boolean> {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param value The immutable value to assign to this.
+ */
+ public BooleanValue(Boolean value) {
+ super(value);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/BucketResolver.java b/container-search/src/main/java/com/yahoo/search/grouping/request/BucketResolver.java
new file mode 100644
index 00000000000..735347cde87
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/BucketResolver.java
@@ -0,0 +1,121 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This is a helper class for resolving buckets to a list of
+ * {@link GroupingExpression} objects. To resolve a list simply
+ * {@link #push(ConstantValue, boolean)} onto it, before calling
+ * {@link #resolve(GroupingExpression)} to retrieve the list of corresponding
+ * grouping expression object.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class BucketResolver {
+
+ private final List<BucketValue> buckets = new LinkedList<>();
+ private ConstantValue<?> prev = null;
+ private boolean previnclusive = false;
+ private int idx = 0;
+
+ /**
+ * Pushes the given expression onto this bucket resolver. Once all buckets have been pushed using this method, call
+ * {@link #resolve(GroupingExpression)} to retrieve to combined grouping expression.
+ *
+ * @param val The expression to push.
+ * @param inclusive Whether or not the value is inclusive or not.
+ * @throws IllegalArgumentException Thrown if the expression is incompatible.
+ */
+ public BucketResolver push(ConstantValue<?> val, boolean inclusive) {
+ if (prev == null) {
+ prev = val;
+ } else if (!(prev instanceof InfiniteValue || val instanceof InfiniteValue)
+ && !prev.getClass().equals(val.getClass())) {
+ throw new IllegalArgumentException("Bucket type mismatch, expected '" + prev.getClass().getSimpleName() +
+ "' got '" + val.getClass().getSimpleName() + "'.");
+ } else if (prev instanceof InfiniteValue && val instanceof InfiniteValue) {
+ throw new IllegalArgumentException("Bucket type mismatch, cannot both be infinity.");
+ }
+ if ((++idx % 2) == 0) {
+ ConstantValue<?> begin = previnclusive ? prev : nextValue(prev);
+ ConstantValue<?> end = inclusive ? nextValue(val) : val;
+ if (begin instanceof DoubleValue || end instanceof DoubleValue) {
+ buckets.add(new DoubleBucket(begin, end));
+ } else if (begin instanceof LongValue || end instanceof LongValue) {
+ buckets.add(new LongBucket(begin, end));
+ } else if (begin instanceof StringValue || end instanceof StringValue) {
+ buckets.add(new StringBucket(begin, end));
+ } else if (begin instanceof RawValue || end instanceof RawValue) {
+ buckets.add(new RawBucket(begin, end));
+ } else {
+ throw new UnsupportedOperationException("Bucket type '" + val.getClass() + "' not supported.");
+ }
+ }
+ prev = val;
+ previnclusive = inclusive;
+ return this;
+ }
+
+ /**
+ * Resolves and returns the list of grouping expressions that correspond to the previously pushed buckets.
+ *
+ * @param exp The expression to assign to the function.
+ * @return The list corresponding to the pushed buckets.
+ */
+ public PredefinedFunction resolve(GroupingExpression exp) {
+ if ((idx % 2) == 1) {
+ throw new IllegalStateException("Missing to-limit of last bucket.");
+ }
+ int len = buckets.size();
+ if (len == 0) {
+ throw new IllegalStateException("Expected at least one bucket, got none.");
+ }
+ ConstantValue<?> begin = buckets.get(0).getFrom();
+ ConstantValue<?> end = buckets.get(0).getTo();
+ if (begin instanceof DoubleValue || end instanceof DoubleValue) {
+ if (len == 1) {
+ return new DoublePredefined(exp, (DoubleBucket)buckets.get(0));
+ } else {
+ return new DoublePredefined(exp, (DoubleBucket)buckets.get(0),
+ buckets.subList(1, len).toArray(new DoubleBucket[len - 1]));
+ }
+ } else if (begin instanceof LongValue || end instanceof LongValue) {
+ if (len == 1) {
+ return new LongPredefined(exp, (LongBucket)buckets.get(0));
+ } else {
+ return new LongPredefined(exp, (LongBucket)buckets.get(0),
+ buckets.subList(1, len).toArray(new LongBucket[len - 1]));
+ }
+ } else if (begin instanceof StringValue || end instanceof StringValue) {
+ if (len == 1) {
+ return new StringPredefined(exp, (StringBucket)buckets.get(0));
+ } else {
+ return new StringPredefined(exp, (StringBucket)buckets.get(0),
+ buckets.subList(1, len).toArray(new StringBucket[len - 1]));
+ }
+ } else if (begin instanceof RawValue || end instanceof RawValue) {
+ if (len == 1) {
+ return new RawPredefined(exp, (RawBucket)buckets.get(0));
+ } else {
+ return new RawPredefined(exp, (RawBucket)buckets.get(0),
+ buckets.subList(1, len).toArray(new RawBucket[len - 1]));
+ }
+ }
+ throw new UnsupportedOperationException("Bucket type '" + begin.getClass() + "' not supported.");
+ }
+
+ private ConstantValue<?> nextValue(ConstantValue<?> value) {
+ if (value instanceof LongValue) {
+ return LongBucket.nextValue((LongValue)value);
+ } else if (value instanceof DoubleValue) {
+ return DoubleBucket.nextValue((DoubleValue)value);
+ } else if (value instanceof StringValue) {
+ return StringBucket.nextValue((StringValue)value);
+ } else if (value instanceof RawValue) {
+ return RawBucket.nextValue((RawValue)value);
+ }
+ return value;
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/BucketValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/BucketValue.java
new file mode 100644
index 00000000000..858a44e2fe8
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/BucketValue.java
@@ -0,0 +1,54 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a bucket in a {@link PredefinedFunction}. The generic T is the data type of the range values
+ * 'from' and 'to'. The range is inclusive-from and exclusive-to. All supported data types are represented as subclasses
+ * of this.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class BucketValue extends GroupingExpression implements Comparable<BucketValue> {
+
+ private final ConstantValue<?> from;
+ private final ConstantValue<?> to;
+ private final ConstantValueComparator comparator = new ConstantValueComparator();
+
+ protected BucketValue(ConstantValue<?> inclusiveFrom, ConstantValue<?> exclusiveTo) {
+ super("bucket[" + asImage(inclusiveFrom) + ", " + asImage(exclusiveTo) + ">");
+ if (comparator.compare(exclusiveTo, inclusiveFrom) < 0) {
+ throw new IllegalArgumentException("Bucket to-value can not be less than from-value.");
+ }
+ from = inclusiveFrom;
+ to = exclusiveTo;
+ }
+
+ /**
+ * Returns the inclusive-from value of this bucket.
+ *
+ * @return The from-value.
+ */
+ public ConstantValue<?> getFrom() {
+ return from;
+ }
+
+ /**
+ * Returns the exclusive-to value of this bucket.
+ *
+ * @return The to-value.
+ */
+ public ConstantValue<?> getTo() {
+ return to;
+ }
+
+ @Override
+ public int compareTo(BucketValue rhs) {
+ if (comparator.compare(to, rhs.from) <= 0) {
+ return -1;
+ }
+ if (comparator.compare(from, rhs.to) >= 0) {
+ return 1;
+ }
+ return 0;
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/CatFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/CatFunction.java
new file mode 100644
index 00000000000..9bc276bda92
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/CatFunction.java
@@ -0,0 +1,42 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents a cat-function in a {@link GroupingExpression}. It evaluates to a byte array that equals the
+ * concatenation of the binary result of all arguments in the order they were given to the constructor.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class CatFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument.
+ * @param arg2 The second compulsory argument.
+ * @param argN The optional arguments.
+ */
+ public CatFunction(GroupingExpression arg1, GroupingExpression arg2, GroupingExpression... argN) {
+ this(asList(arg1, arg2, argN));
+ }
+
+ private CatFunction(List<GroupingExpression> args) {
+ super("cat", args);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param args The arguments to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the number of arguments is less than 2.
+ */
+ public static CatFunction newInstance(List<GroupingExpression> args) {
+ if (args.size() < 2) {
+ throw new IllegalArgumentException("Expected 2 or more arguments, got " + args.size() + ".");
+ }
+ return new CatFunction(args);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ConstantValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ConstantValue.java
new file mode 100644
index 00000000000..8b8d92b5ae8
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ConstantValue.java
@@ -0,0 +1,29 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a constant value in a {@link GroupingExpression}. Because it does not operate on any input,
+ * this expression type can be used at any input level (see {@link GroupingExpression#resolveLevel(int)}). All supported
+ * data types are represented as subclasses of this.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+@SuppressWarnings("rawtypes")
+public abstract class ConstantValue<T extends Comparable> extends GroupingExpression {
+
+ private final T value;
+
+ protected ConstantValue(T value) {
+ super(asImage(value));
+ this.value = value;
+ }
+
+ /**
+ * Returns the constant value of this.
+ *
+ * @return The value.
+ */
+ public T getValue() {
+ return value;
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ConstantValueComparator.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ConstantValueComparator.java
new file mode 100644
index 00000000000..e8017bbb796
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ConstantValueComparator.java
@@ -0,0 +1,24 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Comparator;
+
+/**
+ * This class compares two constant values, and takes into account that one of
+ * the arguments may be the very special infinity value.
+ *
+ * @author <a href="mailto:lulf@yahoo-inc.com">Ulf Lilleengen</a>
+ */
+@SuppressWarnings("rawtypes")
+public class ConstantValueComparator implements Comparator<ConstantValue> {
+ @SuppressWarnings("unchecked")
+ @Override
+ public int compare(ConstantValue lhs, ConstantValue rhs) {
+ // Run infinite comparison method if one of the arguments are infinite.
+ if (rhs instanceof InfiniteValue) {
+ return (-1 * rhs.getValue().compareTo(lhs));
+ }
+ return (lhs.getValue().compareTo(rhs.getValue()));
+ }
+
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/CountAggregator.java b/container-search/src/main/java/com/yahoo/search/grouping/request/CountAggregator.java
new file mode 100644
index 00000000000..f54d92cdbf5
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/CountAggregator.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents an count-aggregator in a {@link GroupingExpression}. It evaluates to the number of elements
+ * there are in the input.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class CountAggregator extends AggregatorNode {
+
+ /**
+ * Constructs a new instance of this class.
+ */
+ public CountAggregator() {
+ super("count");
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DateFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DateFunction.java
new file mode 100644
index 00000000000..3d416b31d95
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DateFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a timestamp-formatter function in a {@link GroupingExpression}. It evaluates to a string on the
+ * form "YYYY-MM-DD" of the result of the argument.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class DateFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ */
+ public DateFunction(GroupingExpression exp) {
+ super("time.date", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfMonthFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfMonthFunction.java
new file mode 100644
index 00000000000..4ead68cc8f1
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfMonthFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a day-of-month timestamp-function in a {@link GroupingExpression}. It evaluates to a long that
+ * equals the day of month (1-31) of the result of the argument.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class DayOfMonthFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ */
+ public DayOfMonthFunction(GroupingExpression exp) {
+ super("time.dayofmonth", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfWeekFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfWeekFunction.java
new file mode 100644
index 00000000000..f91344e2e7b
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfWeekFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a day-of-week timestamp-function in a {@link GroupingExpression}. It evaluates to a long that
+ * equals the day of week (0 - 6) of the result of the argument, Monday being 0.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class DayOfWeekFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ */
+ public DayOfWeekFunction(GroupingExpression exp) {
+ super("time.dayofweek", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfYearFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfYearFunction.java
new file mode 100644
index 00000000000..20313864493
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DayOfYearFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a day-of-year timestamp-function in a {@link GroupingExpression}. It evaluates to a long that
+ * equals the day of year (0-365) of the result of the argument.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class DayOfYearFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ */
+ public DayOfYearFunction(GroupingExpression exp) {
+ super("time.dayofyear", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DebugWaitFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DebugWaitFunction.java
new file mode 100644
index 00000000000..c2f26e6b3b0
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DebugWaitFunction.java
@@ -0,0 +1,43 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents debug_wait function in a {@link GroupingExpression}. For each hit evaluated,
+ * it waits for the time specified as the second argument. The third argument specifies if the wait
+ * should be a busy-wait or not. The first argument is then evaluated.
+ *
+ * @author <a href="mailto:lulf@yahoo-inc.com">Ulf Lilleengen</a>
+ */
+public class DebugWaitFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument, the expression to proxy.
+ * @param arg2 The second compulsory argument, must evaluate to a positive number.
+ * @param arg3 The third compulsory argument, specifying busy wait or not.
+ */
+ public DebugWaitFunction(GroupingExpression arg1, DoubleValue arg2, BooleanValue arg3) {
+ super("debugwait", Arrays.asList(arg1, arg2, arg3));
+ }
+
+ /**
+ * Returns the time to wait when evaluating this function.
+ *
+ * @return the number of seconds to wait.
+ */
+ public double getWaitTime() {
+ return ((DoubleValue)getArg(1)).getValue();
+ }
+
+ /**
+ * Returns whether or not the debug node should busy-wait.
+ *
+ * @return true if busy-wait, false if not.
+ */
+ public boolean getBusyWait() {
+ return ((BooleanValue)getArg(2)).getValue();
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DivFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DivFunction.java
new file mode 100644
index 00000000000..9ed263362fa
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DivFunction.java
@@ -0,0 +1,43 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents a div-function in a {@link GroupingExpression}. It evaluates to a number that equals the result
+ * of dividing the results of all arguments in the order they were given to the constructor (divide first argument by
+ * second, result by third, ...).
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class DivFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument, must evaluate to a number.
+ * @param arg2 The second compulsory argument, must evaluate to a number.
+ * @param argN The optional arguments, must evaluate to a number.
+ */
+ public DivFunction(GroupingExpression arg1, GroupingExpression arg2, GroupingExpression... argN) {
+ this(asList(arg1, arg2, argN));
+ }
+
+ private DivFunction(List<GroupingExpression> args) {
+ super("div", args);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param args The arguments to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the number of arguments is less than 2.
+ */
+ public static DivFunction newInstance(List<GroupingExpression> args) {
+ if (args.size() < 2) {
+ throw new IllegalArgumentException("Expected 2 or more arguments, got " + args.size() + ".");
+ }
+ return new DivFunction(args);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DocIdNsSpecificValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DocIdNsSpecificValue.java
new file mode 100644
index 00000000000..02c8d66be5d
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DocIdNsSpecificValue.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a document id specific value in a {@link GroupingExpression}. It evaluates to the namespace-
+ * specific value of the document id of the input {@link com.yahoo.search.result.Hit}.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class DocIdNsSpecificValue extends DocumentValue {
+
+ /**
+ * Constructs a new instance of this class.
+ */
+ public DocIdNsSpecificValue() {
+ super("docidnsspecific()");
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DocumentValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DocumentValue.java
new file mode 100644
index 00000000000..98d5a6fe21f
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DocumentValue.java
@@ -0,0 +1,24 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a document value in a {@link GroupingExpression}. As such, the subclasses of this can only be
+ * used as document-level expressions (i.e. level 0, see {@link GroupingExpression#resolveLevel(int)}).
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public abstract class DocumentValue extends GroupingExpression {
+
+ protected DocumentValue(String image) {
+ super(image);
+ }
+
+ @Override
+ public void resolveLevel(int level) {
+ if (level != 0) {
+ throw new IllegalArgumentException("Expression '" + this + "' not applicable for " +
+ GroupingOperation.getLevelDesc(level) + ".");
+ }
+ super.resolveLevel(level);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleBucket.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleBucket.java
new file mode 100644
index 00000000000..4e12e96272e
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleBucket.java
@@ -0,0 +1,41 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+import java.text.ChoiceFormat;
+
+/**
+ * This class represents a {@link Double} bucket in a {@link PredefinedFunction}.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class DoubleBucket extends BucketValue {
+
+ /**
+ * Returns the next distinct value.
+ *
+ * @param value The base value.
+ * @return the next value.
+ */
+ public static DoubleValue nextValue(DoubleValue value) {
+ return (new DoubleValue(ChoiceFormat.nextDouble(value.getValue())));
+ }
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param from The from-value to assign to this.
+ * @param to The to-value to assign to this.
+ */
+ public DoubleBucket(double from, double to) {
+ super(new DoubleValue(from), new DoubleValue(to));
+ }
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param from The from-value to assign to this.
+ * @param to The to-value to assign to this.
+ */
+ public DoubleBucket(ConstantValue<?> from, ConstantValue<?> to) {
+ super(from, to);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DoublePredefined.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DoublePredefined.java
new file mode 100644
index 00000000000..59265359715
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DoublePredefined.java
@@ -0,0 +1,48 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents a predefined bucket-function in a {@link GroupingExpression} for expressions that evaluate to a
+ * double.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class DoublePredefined extends PredefinedFunction {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a double.
+ * @param arg1 The compulsory bucket.
+ * @param argN The optional buckets.
+ */
+ public DoublePredefined(GroupingExpression exp, DoubleBucket arg1, DoubleBucket... argN) {
+ this(exp, asList(arg1, argN));
+ }
+
+ private DoublePredefined(GroupingExpression exp, List<DoubleBucket> args) {
+ super(exp, args);
+ }
+
+ @Override
+ public DoubleBucket getBucket(int i) {
+ return (DoubleBucket)getArg(i + 1);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param exp The expression to evaluate, must evaluate to a double.
+ * @param args The buckets to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the list of buckets is empty.
+ */
+ public static DoublePredefined newInstance(GroupingExpression exp, List<DoubleBucket> args) {
+ if (args.isEmpty()) {
+ throw new IllegalArgumentException("Expected at least one bucket, got none.");
+ }
+ return new DoublePredefined(exp, args);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleValue.java
new file mode 100644
index 00000000000..682102533ff
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/DoubleValue.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a constant {@link Double} value in a {@link GroupingExpression}.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class DoubleValue extends ConstantValue<Double> {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param value The immutable value to assign to this.
+ */
+ public DoubleValue(double value) {
+ super(value);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/EachOperation.java b/container-search/src/main/java/com/yahoo/search/grouping/request/EachOperation.java
new file mode 100644
index 00000000000..12f6df1f497
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/EachOperation.java
@@ -0,0 +1,26 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This is a grouping operation that processes each element of the input list separately, as opposed to {@link
+ * AllOperation} which processes that list as a whole.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class EachOperation extends GroupingOperation {
+
+ /**
+ * Constructs a new instance of this class.
+ */
+ public EachOperation() {
+ super("each");
+ }
+
+ @Override
+ public void resolveLevel(int level) {
+ if (level == 0) {
+ throw new IllegalArgumentException("Operation '" + this + "' can not operate on " + getLevelDesc(level) + ".");
+ }
+ super.resolveLevel(level - 1);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ExpressionVisitor.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ExpressionVisitor.java
new file mode 100644
index 00000000000..ba411ac45ce
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ExpressionVisitor.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This interface defines the necessary callback to recursively visit all {@link GroupingExpression} objects in a {@link
+ * GroupingOperation}. It is used by the {@link com.yahoo.search.grouping.GroupingValidator} to ensure that all
+ * referenced attributes are valid for the cluster being queried.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public interface ExpressionVisitor {
+
+ /**
+ * This method is called for every {@link GroupingExpression} object in the targeted {@link GroupingOperation}.
+ *
+ * @param exp The expression being visited.
+ */
+ public void visitExpression(GroupingExpression exp);
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/FixedWidthFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/FixedWidthFunction.java
new file mode 100644
index 00000000000..9ac3870718b
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/FixedWidthFunction.java
@@ -0,0 +1,34 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a fixed-width bucket-function in a {@link GroupingExpression}. It maps the input into the given
+ * number of buckets by the result of the argument expression.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class FixedWidthFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate.
+ * @param width The width of each bucket.
+ */
+ public FixedWidthFunction(GroupingExpression exp, Number width) {
+ super("fixedwidth", Arrays.asList(exp, width instanceof Double ? new DoubleValue(width.doubleValue()) : new LongValue(width.longValue())));
+ }
+
+ /**
+ * Returns the number of buckets to divide the result into.
+ *
+ * @return The bucket count.
+ */
+ public Number getWidth() {
+ GroupingExpression w = getArg(1);
+ return (w instanceof LongValue) ? ((LongValue)w).getValue() : ((DoubleValue)w).getValue();
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/FunctionNode.java b/container-search/src/main/java/com/yahoo/search/grouping/request/FunctionNode.java
new file mode 100644
index 00000000000..3003ce69abe
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/FunctionNode.java
@@ -0,0 +1,78 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.*;
+
+/**
+ * This class represents a function in a {@link GroupingExpression}. Because it operate on other expressions (as opposed
+ * to {@link AggregatorNode} and {@link DocumentValue} that operate on inputs), this expression type can be used at any
+ * input level (see {@link GroupingExpression#resolveLevel(int)}).
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public abstract class FunctionNode extends GroupingExpression implements Iterable<GroupingExpression> {
+
+ private final List<GroupingExpression> args = new ArrayList<>();
+
+ protected FunctionNode(String image, List<GroupingExpression> args) {
+ super(image + "(" + asString(args) + ")");
+ this.args.addAll(args);
+ }
+
+ /**
+ * Returns the number of arguments that were given to this function at construction.
+ *
+ * @return The argument count.
+ */
+ public int getNumArgs() {
+ return args.size();
+ }
+
+ /**
+ * Returns the argument at the given index.
+ *
+ * @param i The index of the argument to return.
+ * @return The argument at the given index.
+ * @throws IndexOutOfBoundsException If the index is out of range.
+ */
+ public GroupingExpression getArg(int i) {
+ return args.get(i);
+ }
+
+ @Override
+ public Iterator<GroupingExpression> iterator() {
+ return Collections.unmodifiableList(args).iterator();
+ }
+
+ @Override
+ public void resolveLevel(int level) {
+ super.resolveLevel(level);
+ for (GroupingExpression arg : args) {
+ arg.resolveLevel(level);
+ }
+ }
+
+ @Override
+ public void visit(ExpressionVisitor visitor) {
+ super.visit(visitor);
+ for (GroupingExpression arg : args) {
+ arg.visit(visitor);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected static <T> List<T> asList(T arg1, T... argN) {
+ return asList(Arrays.asList(arg1), Arrays.asList(argN));
+ }
+
+ @SuppressWarnings("unchecked")
+ protected static <T> List<T> asList(T arg1, T arg2, T... argN) {
+ return asList(Arrays.asList(arg1, arg2), Arrays.asList(argN));
+ }
+
+ protected static <T> List<T> asList(List<T> foo, List<T> bar) {
+ List<T> ret = new LinkedList<>(foo);
+ ret.addAll(bar);
+ return ret;
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingExpression.java b/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingExpression.java
new file mode 100644
index 00000000000..6015557f81e
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingExpression.java
@@ -0,0 +1,100 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import com.yahoo.javacc.UnicodeUtilities;
+
+import java.util.List;
+
+/**
+ * This class represents an expression in a {@link GroupingOperation}. You may manually construct this expression, or
+ * you may use the {@link com.yahoo.search.grouping.request.parser.GroupingParser} to generate one from a query-string.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public abstract class GroupingExpression extends GroupingNode {
+
+ private Integer level = null;
+
+ protected GroupingExpression(String image) {
+ super(image);
+ }
+
+ /**
+ * Resolves the conceptual level of this expression. This level represents the type of data that is consumed by this
+ * expression, where level 0 is a single hit, level 1 is a group, level 2 is a list of groups, and so forth. This
+ * method verifies the input level against the expression type, and recursively resolves the level of all argument
+ * expressions.
+ *
+ * @param level The level of the input data.
+ * @throws IllegalArgumentException Thrown if the level of this expression could not be resolved.
+ * @throws IllegalStateException Thrown if type failed to accept the number of arguments provided.
+ */
+ public void resolveLevel(int level) {
+ if (level < 0) {
+ throw new IllegalArgumentException("Expression '" + this + "' recurses through a single hit.");
+ }
+ this.level = level;
+ }
+
+ /**
+ * Returns the conceptual level of this expression.
+ *
+ * @return The level.
+ * @throws IllegalArgumentException Thrown if the level of this expression has not been resolved.
+ * @see #resolveLevel(int)
+ */
+ public int getLevel() {
+ if (level == null) {
+ throw new IllegalStateException("Level for expression '" + this + "' has not been resolved.");
+ }
+ return level;
+ }
+
+ /**
+ * Recursively calls {@link ExpressionVisitor#visitExpression(GroupingExpression)} for this expression and all of
+ * its argument expressions.
+ *
+ * @param visitor The visitor to call.
+ */
+ public void visit(ExpressionVisitor visitor) {
+ visitor.visitExpression(this);
+ }
+
+ /**
+ * Returns a string description of the given list of expressions. This is a comma-separated list of the expressions
+ * own {@link GroupingExpression#toString()} output.
+ *
+ * @param lst The list of expressions to output.
+ * @return The string description.
+ */
+ public static String asString(List<GroupingExpression> lst) {
+ StringBuilder ret = new StringBuilder();
+ for (int i = 0, len = lst.size(); i < len; ++i) {
+ ret.append(lst.get(i));
+ if (i < len - 1) {
+ ret.append(", ");
+ }
+ }
+ return ret.toString();
+ }
+
+ /**
+ * Returns a string representation of an object that can be used in the 'image' constructor argument of {@link
+ * GroupingNode}. This method ensures that strings are quoted, and that all complex characters are escaped.
+ *
+ * @param obj The object to output.
+ * @return The string representation.
+ */
+ public static String asImage(Object obj) {
+ if (!(obj instanceof String)) {
+ return obj.toString();
+ }
+ return UnicodeUtilities.quote((String)obj, '"');
+ }
+
+ @Override
+ public GroupingExpression setLabel(String label) {
+ super.setLabel(label);
+ return this;
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingNode.java b/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingNode.java
new file mode 100644
index 00000000000..b400dfe5737
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingNode.java
@@ -0,0 +1,44 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This is the abstract super class of both {@link GroupingOperation} and {@link GroupingExpression}. All nodes can be
+ * assigned a {@link String} label which in turn can be used to identify the corresponding result objects.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public abstract class GroupingNode {
+
+ private final String image;
+ private String label = null;
+
+ protected GroupingNode(String image) {
+ this.image = image;
+ }
+
+ /**
+ * Returns the label assigned to this grouping expression.
+ *
+ * @return The label string.
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * Assigns a label to this grouping expression. The label is applied to the results of this expression so that they
+ * can be identified by the caller when processing the output.
+ *
+ * @param str The label to assign to this.
+ * @return This, to allow chaining.
+ */
+ public GroupingNode setLabel(String str) {
+ label = str;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return image;
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingOperation.java b/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingOperation.java
new file mode 100644
index 00000000000..d49713ba9f2
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/GroupingOperation.java
@@ -0,0 +1,582 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import com.yahoo.collections.LazyMap;
+import com.yahoo.collections.LazySet;
+import com.yahoo.search.grouping.request.parser.GroupingParser;
+import com.yahoo.search.grouping.request.parser.GroupingParserInput;
+import com.yahoo.search.grouping.request.parser.ParseException;
+import com.yahoo.search.grouping.request.parser.TokenMgrError;
+
+import java.util.*;
+
+/**
+ * This class represents a single node in a grouping operation tree. You may manually construct this tree, or you may
+ * use the {@link #fromString(String)} method to generate one from a query-string. To execute, assign it to a {@link
+ * com.yahoo.search.grouping.GroupingRequest} using the {@link com.yahoo.search.grouping.GroupingRequest#setRootOperation(GroupingOperation)}
+ * method.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public abstract class GroupingOperation extends GroupingNode {
+
+ private final List<GroupingExpression> orderBy = new ArrayList<>();
+ private final List<GroupingExpression> outputs = new ArrayList<>();
+ private final List<GroupingOperation> children = new ArrayList<>();
+ private final Map<String, GroupingExpression> alias = LazyMap.newHashMap();
+ private final Set<String> hints = LazySet.newHashSet();
+
+ private GroupingExpression groupBy = null;
+ private GroupingOperation parent = null;
+ private String where = null;
+ private boolean forceSinglePass = false;
+ private double accuracy = 0.95;
+ private int precision = 0;
+ private int level = -1;
+ private int max = -1;
+
+ protected GroupingOperation(String image) {
+ super(image);
+ }
+
+ /**
+ * Registers an alias with this operation. An alias is made available to expressions in both this node and all child
+ * nodes.
+ *
+ * @param id The id of the alias to put.
+ * @param exp The expression to associate with the id.
+ * @return This, to allow chaining.
+ */
+ public GroupingOperation putAlias(String id, GroupingExpression exp) {
+ alias.put(id, exp);
+ return this;
+ }
+
+ /**
+ * Returns the alias associated with the given name. If no alias can be found in this node, this method queries its
+ * parent grouping node. If the alias still can not be found, this method returns null.
+ *
+ * @param id The id of the alias to return.
+ * @return The expression associated with the id.
+ */
+ public GroupingExpression getAlias(String id) {
+ if (alias.containsKey(id)) {
+ return alias.get(id);
+ } else if (parent != null) {
+ return parent.getAlias(id);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Adds a hint to this.
+ *
+ * @param hint The hint to add.
+ * @return This, to allow chaining.
+ */
+ public GroupingOperation addHint(String hint) {
+ hints.add(hint);
+ return this;
+ }
+
+ /**
+ * Returns whether or not the given hint has been added to this.
+ *
+ * @param hint The hint to check for.
+ * @return True if the hint has been added.
+ */
+ public boolean containsHint(String hint) {
+ return hints.contains(hint);
+ }
+
+ /**
+ * Returns an immutable view to the hint list of this node.
+ *
+ * @return The list.
+ */
+ public Set<String> getHints() {
+ return Collections.unmodifiableSet(hints);
+ }
+
+ /**
+ * Adds a child grouping node to this. This will also set the parent of the child so that it points to this node.
+ *
+ * @param op The child node to add.
+ * @return This, to allow chaining.
+ */
+ public GroupingOperation addChild(GroupingOperation op) {
+ op.parent = this;
+ children.add(op);
+ return this;
+ }
+
+ /**
+ * Convenience method to call {@link #addChild(GroupingOperation)} for each element in the given list.
+ *
+ * @param lst The list of operations to add.
+ * @return This, to allow chaining.
+ */
+ public GroupingOperation addChildren(List<GroupingOperation> lst) {
+ for (GroupingOperation op : lst) {
+ addChild(op);
+ }
+ return this;
+ }
+
+ /**
+ * Returns the number of child operations of this.
+ *
+ * @return The child count.
+ */
+ public int getNumChildren() {
+ return children.size();
+ }
+
+ /**
+ * Returns the child operation at the given index.
+ *
+ * @param i The index of the child to return.
+ * @return The child at the given index.
+ * @throws IndexOutOfBoundsException If the index is out of range.
+ */
+ public GroupingOperation getChild(int i) {
+ return children.get(i);
+ }
+
+ /**
+ * Returns an immutable view to the child list of this node.
+ *
+ * @return The list.
+ */
+ public List<GroupingOperation> getChildren() {
+ return Collections.unmodifiableList(children);
+ }
+
+ /**
+ * Assigns an expressions as the group-by clause of this operation.
+ *
+ * @param exp The expression to assign to this.
+ * @return This, to allow chaining.
+ */
+ public GroupingOperation setGroupBy(GroupingExpression exp) {
+ groupBy = exp;
+ return this;
+ }
+
+ /**
+ * Returns the expression assigned as the group-by clause of this.
+ *
+ * @return The expression.
+ */
+ public GroupingExpression getGroupBy() {
+ return groupBy;
+ }
+
+ /**
+ * Returns the conceptual level of this node.
+ *
+ * @return The level, or -1 if not resolved.
+ * @see #resolveLevel(int)
+ */
+ public int getLevel() {
+ return level;
+ }
+
+ /**
+ * Resolves the conceptual level of this operation. This level represents the type of data that is consumed by this
+ * operation, where level 0 is a single hit, level 1 is a group, level 2 is a list of groups, and so forth. This
+ * method verifies the input level against the operation type, and recursively resolves the level of all argument
+ * expressions.
+ *
+ * @param level The level of the input data.
+ * @throws IllegalArgumentException Thrown if a contained expression is invalid for the given level.
+ */
+ public void resolveLevel(int level) {
+ if (groupBy != null) {
+ if (level == 0) {
+ throw new IllegalArgumentException(
+ "Operation '" + this + "' can not group " + getLevelDesc(level) + ".");
+ }
+ groupBy.resolveLevel(level - 1);
+ ++level;
+ }
+ if (hasMax()) {
+ if (level == 0) {
+ throw new IllegalArgumentException(
+ "Operation '" + this + "' can not apply max to " + getLevelDesc(level) + ".");
+ }
+ }
+ this.level = level;
+ for (GroupingExpression exp : outputs) {
+ exp.resolveLevel(level);
+ }
+ if (!orderBy.isEmpty()) {
+ if (level == 0) {
+ throw new IllegalArgumentException(
+ "Operation '" + this + "' can not order " + getLevelDesc(level) + ".");
+ }
+ for (GroupingExpression exp : orderBy) {
+ exp.resolveLevel(level - 1);
+ }
+ }
+ for (GroupingOperation child : children) {
+ child.resolveLevel(level);
+ }
+ }
+
+ public GroupingOperation setForceSinglePass(boolean forceSinglePass) {
+ this.forceSinglePass = forceSinglePass;
+ return this;
+ }
+
+ public boolean getForceSinglePass() {
+ return forceSinglePass;
+ }
+
+ /**
+ * Assigns the max clause of this. This is the maximum number of groups to return for this operation.
+ *
+ * @param max The expression to assign to this.
+ * @return This, to allow chaining.
+ * @see #setPrecision(int)
+ */
+ public GroupingOperation setMax(int max) {
+ this.max = max;
+ return this;
+ }
+
+ /**
+ * Returns the max clause of this.
+ *
+ * @return The expression.
+ * @see #setMax(int)
+ */
+ public int getMax() {
+ return max;
+ }
+
+ /**
+ * Indicates if the 'max' value has been set.
+ *
+ * @return true if max value is set.
+ */
+ public boolean hasMax() { return max >= 0; }
+
+ /**
+ * Assigns an accuracy value for this. This is a number between 0 and 1 describing the accuracy of the result, which
+ * again determines the speed of the grouping request. A low value will make sure the grouping operation runs fast,
+ * at the sacrifice if a (possible) imprecise result.
+ *
+ * @param accuracy The accuracy to assign to this.
+ * @return This, to allow chaining.
+ * @throws IllegalArgumentException If the accuracy is outside the allowed value range.
+ */
+ public GroupingOperation setAccuracy(double accuracy) {
+ if (accuracy > 1.0 || accuracy < 0.0) {
+ throw new IllegalArgumentException("Illegal accuracy '" + accuracy + "'. Must be between 0 and 1.");
+ }
+ this.accuracy = accuracy;
+ return this;
+ }
+
+ /**
+ * Return the accuracy of this.
+ *
+ * @return The accuracy value.
+ * @see #setAccuracy(double)
+ */
+ public double getAccuracy() {
+ return accuracy;
+ }
+
+ /**
+ * Adds an expression to the order-by clause of this operation.
+ *
+ * @param exp The expressions to add to this.
+ * @return This, to allow chaining.
+ */
+ public GroupingOperation addOrderBy(GroupingExpression exp) {
+ orderBy.add(exp);
+ return this;
+ }
+
+ /**
+ * Convenience method to call {@link #addOrderBy(GroupingExpression)} for each element in the given list.
+ *
+ * @param lst The list of expressions to add.
+ * @return This, to allow chaining.
+ */
+ public GroupingOperation addOrderBy(List<GroupingExpression> lst) {
+ for (GroupingExpression exp : lst) {
+ addOrderBy(exp);
+ }
+ return this;
+ }
+
+ /**
+ * Returns the number of expressions in the order-by clause of this.
+ *
+ * @return The expression count.
+ */
+ public int getNumOrderBy() {
+ return orderBy.size();
+ }
+
+ /**
+ * Returns the group-by expression at the given index.
+ *
+ * @param i The index of the expression to return.
+ * @return The expression at the given index.
+ * @throws IndexOutOfBoundsException If the index is out of range.
+ */
+ public GroupingExpression getOrderBy(int i) {
+ return orderBy.get(i);
+ }
+
+ /**
+ * Returns an immutable view to the order-by clause of this.
+ *
+ * @return The expression list.
+ */
+ public List<GroupingExpression> getOrderBy() {
+ return Collections.unmodifiableList(orderBy);
+ }
+
+ /**
+ * Adds an expression to the output clause of this operation.
+ *
+ * @param exp The expressions to add to this.
+ * @return This, to allow chaining.
+ */
+ public GroupingOperation addOutput(GroupingExpression exp) {
+ outputs.add(exp);
+ return this;
+ }
+
+ /**
+ * Convenience method to call {@link #addOutput(GroupingExpression)} for each element in the given list.
+ *
+ * @param lst The list of expressions to add.
+ * @return This, to allow chaining.
+ */
+ public GroupingOperation addOutputs(List<GroupingExpression> lst) {
+ for (GroupingExpression exp : lst) {
+ addOutput(exp);
+ }
+ return this;
+ }
+
+ /**
+ * Returns the number of expressions in the output clause of this.
+ *
+ * @return The expression count.
+ */
+ public int getNumOutputs() {
+ return outputs.size();
+ }
+
+ /**
+ * Returns the output expression at the given index.
+ *
+ * @param i The index of the expression to return.
+ * @return The expression at the given index.
+ * @throws IndexOutOfBoundsException If the index is out of range.
+ */
+ public GroupingExpression getOutput(int i) {
+ return outputs.get(i);
+ }
+
+ /**
+ * Returns an immutable view to the output clause of this.
+ *
+ * @return The expression list.
+ */
+ public List<GroupingExpression> getOutputs() {
+ return Collections.unmodifiableList(outputs);
+ }
+
+ /**
+ * Assigns the precision clause of this. This is the number of intermediate groups returned from each search-node
+ * during expression evaluation to give the dispatch-node more data to consider when selecting the N groups that are
+ * to be evaluated further.
+ *
+ * @param precision The precision to set.
+ * @return This, to allow chaining.
+ * @see #setMax(int)
+ */
+ public GroupingOperation setPrecision(int precision) {
+ this.precision = precision;
+ return this;
+ }
+
+ /**
+ * Returns the precision clause of this.
+ *
+ * @return The precision.
+ */
+ public int getPrecision() {
+ return precision;
+ }
+
+ /**
+ * Assigns a string as the where clause of this operation.
+ *
+ * @param str The string to assign to this.
+ * @return This, to allow chaining.
+ */
+ public GroupingOperation setWhere(String str) {
+ where = str;
+ return this;
+ }
+
+ /**
+ * Returns the where clause assigned to this operation.
+ *
+ * @return The where clause.
+ */
+ public String getWhere() {
+ return where;
+ }
+
+ /**
+ * Recursively calls {@link GroupingExpression#visit(ExpressionVisitor)} on all {@link GroupingExpression} objects
+ * in this operation and in all of its child operations.
+ *
+ * @param visitor The visitor to call.
+ */
+ public void visitExpressions(ExpressionVisitor visitor) {
+ for (GroupingExpression exp : alias.values()) {
+ exp.visit(visitor);
+ }
+ for (GroupingExpression exp : outputs) {
+ exp.visit(visitor);
+ }
+ for (GroupingExpression exp : orderBy) {
+ exp.visit(visitor);
+ }
+ if (groupBy != null) {
+ groupBy.visit(visitor);
+ }
+ for (GroupingOperation op : children) {
+ op.visitExpressions(visitor);
+ }
+ }
+
+ @Override
+ public GroupingOperation setLabel(String label) {
+ super.setLabel(label);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder ret = new StringBuilder();
+ ret.append(super.toString()).append("(");
+ if (groupBy != null) {
+ ret.append("group(").append(groupBy).append(") ");
+ }
+ for (String hint : hints) {
+ ret.append("hint(").append(hint).append(") ");
+ }
+ if (hasMax()) {
+ ret.append("max(").append(max).append(") ");
+ }
+ if (!orderBy.isEmpty()) {
+ ret.append("order(");
+ ret.append(GroupingExpression.asString(orderBy));
+ ret.append(") ");
+ }
+ if (!outputs.isEmpty()) {
+ ret.append("output(");
+ for (int i = 0, len = outputs.size(); i < len; ++i) {
+ GroupingExpression exp = outputs.get(i);
+ ret.append(exp);
+ String label = exp.getLabel();
+ if (label != null) {
+ ret.append(" as(").append(label).append(")");
+ }
+ if (i < len - 1) {
+ ret.append(", ");
+ }
+ }
+ ret.append(") ");
+ }
+ if (precision != 0) {
+ ret.append("precision(").append(precision).append(") ");
+ }
+ if (where != null) {
+ ret.append("where(").append(where).append(") ");
+ }
+ for (GroupingOperation child : children) {
+ ret.append(child).append(" ");
+ }
+ int len = ret.length();
+ if (ret.charAt(len - 1) == ' ') {
+ ret.setLength(len - 1);
+ }
+ ret.append(")");
+ String label = getLabel();
+ if (label != null) {
+ ret.append(" as(").append(label).append(")");
+ }
+ return ret.toString();
+ }
+
+ /**
+ * Returns a description of the given level. This allows for more descriptive errors being passed back to the user.
+ *
+ * @param level The level to describe.
+ * @return A description of the given level.
+ */
+ public static String getLevelDesc(int level) {
+ if (level <= 0) {
+ return "single hit";
+ } else if (level == 1) {
+ return "single group";
+ } else {
+ StringBuilder ret = new StringBuilder();
+ for (int i = 1; i < level; ++i) {
+ ret.append("list of ");
+ }
+ ret.append("groups");
+ return ret.toString();
+ }
+ }
+
+ /**
+ * Convenience method to call {@link #fromStringAsList(String)} and assert that the list contains exactly one
+ * grouping operation.
+ *
+ * @param str The string to parse.
+ * @return A grouping operation that corresponds to the string.
+ * @throws IllegalArgumentException Thrown if the string could not be parsed as a single operation.
+ */
+ public static GroupingOperation fromString(String str) {
+ List<GroupingOperation> lst = fromStringAsList(str);
+ if (lst.size() != 1) {
+ throw new IllegalArgumentException("Expected 1 operation, got " + lst.size() + ".");
+ }
+ return lst.get(0);
+ }
+
+ /**
+ * Parses the given string as a list of grouping operations. This method never returns null, it either returns a
+ * list of valid grouping requests or it throws an exception.
+ *
+ * @param str The string to parse.
+ * @return A list of grouping operations that corresponds to the string.
+ * @throws IllegalArgumentException Thrown if the string could not be parsed.
+ */
+ public static List<GroupingOperation> fromStringAsList(String str) {
+ if (str == null || str.trim().length() == 0) {
+ return Collections.emptyList();
+ }
+ GroupingParserInput input = new GroupingParserInput(str);
+ try {
+ return new GroupingParser(input).requestList();
+ } catch (ParseException | TokenMgrError e) {
+ throw new IllegalArgumentException(input.formatException(e.getMessage()), e);
+ }
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/HourOfDayFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/HourOfDayFunction.java
new file mode 100644
index 00000000000..5410ada6cf5
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/HourOfDayFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents an hour-of-day timestamp-function in a {@link GroupingExpression}. It evaluates to a long that
+ * equals the hour of day (0-23) of the result of the argument.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class HourOfDayFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ */
+ public HourOfDayFunction(GroupingExpression exp) {
+ super("time.hourofday", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/Infinite.java b/container-search/src/main/java/com/yahoo/search/grouping/request/Infinite.java
new file mode 100644
index 00000000000..dfee7d0e48a
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/Infinite.java
@@ -0,0 +1,37 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents an Infinite value that may be used as a bucket
+ * size specifier.
+ *
+ * @author <a href="mailto:lulf@yahoo-inc.com">Ulf Lilleengen</a>
+ */
+@SuppressWarnings("rawtypes")
+public class Infinite implements Comparable {
+ private final boolean negative;
+
+ /**
+ * Create an Infinite object with positive or negative sign.
+ * @param negative the signedness.
+ */
+ public Infinite(boolean negative) {
+ this.negative = negative;
+ }
+
+ /**
+ * Override the toString method in order to be re-parseable.
+ */
+ @Override
+ public String toString() {
+ return (negative ? "-inf" : "inf");
+ }
+
+ /**
+ * An infinity value is always less than or greater than.
+ */
+ @Override
+ public int compareTo(Object rhs) {
+ return (negative ? -1 : 1);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/InfiniteValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/InfiniteValue.java
new file mode 100644
index 00000000000..d20a9eb63f8
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/InfiniteValue.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents an infinite value in a {@link GroupingExpression}.
+ *
+ * @author <a href="mailto:lulf@yahoo-inc.com">Ulf Lilleengen</a>
+ */
+public class InfiniteValue extends ConstantValue<Infinite> {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param value The immutable value to assign to this.
+ */
+ public InfiniteValue(Infinite value) {
+ super(value);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/InterpolatedLookup.java b/container-search/src/main/java/com/yahoo/search/grouping/request/InterpolatedLookup.java
new file mode 100644
index 00000000000..a49ccdddbbc
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/InterpolatedLookup.java
@@ -0,0 +1,51 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * This class represents a lookup in a multivalue document
+ * attribute in a {@link GroupingExpression}. It takes the
+ * attribute (assumed to contain a sorted array) from the input
+ * {@link com.yahoo.search.result.Hit} and finds the index that
+ * the second (lookup) argument expression would have, with linear
+ * interpolation when the lookup argument is between two array
+ * element values.
+ *
+ * @author arnej27959
+ */
+@Beta
+public class InterpolatedLookup extends DocumentValue {
+
+ private final String attributeName;
+ private final GroupingExpression arg2;
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param attributeName The attribute name the lookup should happen in
+ * @param lookupArg Expression giving a floating-point value for the lookup argument
+ */
+ public InterpolatedLookup(String attributeName, GroupingExpression lookupArg) {
+ super("interpolatedlookup(" + attributeName + ", " + lookupArg + ")");
+ this.attributeName = attributeName;
+ this.arg2 = lookupArg;
+ }
+
+ /**
+ * Get the name of the attribute to be retrieved from the input hit.
+ * @return The attribute name.
+ */
+ public String getAttributeName() {
+ return attributeName;
+ }
+
+ /**
+ * Get the expression that will be evaluated before lookup.
+ * @return grouping expression argument
+ */
+ public GroupingExpression getLookupArgument() {
+ return arg2;
+ }
+
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/LongBucket.java b/container-search/src/main/java/com/yahoo/search/grouping/request/LongBucket.java
new file mode 100644
index 00000000000..566ca31cb2e
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/LongBucket.java
@@ -0,0 +1,42 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a {@link Long} bucket in a {@link PredefinedFunction}.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class LongBucket extends BucketValue {
+
+ /**
+ * Gives the next distinct long value.
+ *
+ * @param value the base value.
+ * @return the nextt value.
+ */
+ public static LongValue nextValue(LongValue value) {
+ long v = value.getValue();
+ return new LongValue(v < Long.MAX_VALUE ? v + 1 : v);
+ }
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param from The from-value to assign to this.
+ * @param to The to-value to assign to this.
+ */
+ public LongBucket(long from, long to) {
+ super(new LongValue(from), new LongValue(to));
+ }
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param from The from-value to assign to this.
+ * @param to The to-value to assign to this.
+ */
+ @SuppressWarnings("rawtypes")
+ public LongBucket(ConstantValue from, ConstantValue to) {
+ super(from, to);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/LongPredefined.java b/container-search/src/main/java/com/yahoo/search/grouping/request/LongPredefined.java
new file mode 100644
index 00000000000..486c8a9ddde
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/LongPredefined.java
@@ -0,0 +1,48 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents a predefined bucket-function in a {@link GroupingExpression} for expressions that evaluate to a
+ * long.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class LongPredefined extends PredefinedFunction {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ * @param arg1 The compulsory bucket.
+ * @param argN The optional buckets.
+ */
+ public LongPredefined(GroupingExpression exp, LongBucket arg1, LongBucket... argN) {
+ this(exp, asList(arg1, argN));
+ }
+
+ private LongPredefined(GroupingExpression exp, List<LongBucket> args) {
+ super(exp, args);
+ }
+
+ @Override
+ public LongBucket getBucket(int i) {
+ return (LongBucket)getArg(i + 1);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ * @param args The buckets to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the list of buckets is empty.
+ */
+ public static LongPredefined newInstance(GroupingExpression exp, List<LongBucket> args) {
+ if (args.isEmpty()) {
+ throw new IllegalArgumentException("Expected at least one bucket, got none.");
+ }
+ return new LongPredefined(exp, args);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/LongValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/LongValue.java
new file mode 100644
index 00000000000..62a0cb01f08
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/LongValue.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a constant {@link Long} value in a {@link GroupingExpression}.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class LongValue extends ConstantValue<Long> {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param value The immutable value to assign to this.
+ */
+ public LongValue(long value) {
+ super(value);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathACosFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathACosFunction.java
new file mode 100644
index 00000000000..637e0fdf57e
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathACosFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathACosFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathACosFunction(GroupingExpression exp) {
+ super("math.acos", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathACosHFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathACosHFunction.java
new file mode 100644
index 00000000000..aa5677d90d4
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathACosHFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathACosHFunction extends FunctionNode {
+/**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathACosHFunction(GroupingExpression exp) {
+ super("math.acosh", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathASinFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathASinFunction.java
new file mode 100644
index 00000000000..c4b9c7a62d6
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathASinFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathASinFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathASinFunction(GroupingExpression exp) {
+ super("math.asin", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathASinHFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathASinHFunction.java
new file mode 100644
index 00000000000..f368aefe88a
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathASinHFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathASinHFunction extends FunctionNode {
+/**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathASinHFunction(GroupingExpression exp) {
+ super("math.asinh", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathATanFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathATanFunction.java
new file mode 100644
index 00000000000..ed9349c86e6
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathATanFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathATanFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathATanFunction(GroupingExpression exp) {
+ super("math.atan", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathATanHFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathATanHFunction.java
new file mode 100644
index 00000000000..ebcfd1895fa
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathATanHFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathATanHFunction extends FunctionNode {
+/**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathATanHFunction(GroupingExpression exp) {
+ super("math.atanh", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathCbrtFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathCbrtFunction.java
new file mode 100644
index 00000000000..78e2c3c9aa5
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathCbrtFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathCbrtFunction extends FunctionNode {
+/**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathCbrtFunction(GroupingExpression exp) {
+ super("math.cbrt", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathCosFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathCosFunction.java
new file mode 100644
index 00000000000..0ab35653607
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathCosFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathCosFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathCosFunction(GroupingExpression exp) {
+ super("math.cos", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathCosHFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathCosHFunction.java
new file mode 100644
index 00000000000..f4137c302e8
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathCosHFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathCosHFunction extends FunctionNode {
+/**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathCosHFunction(GroupingExpression exp) {
+ super("math.cosh", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathExpFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathExpFunction.java
new file mode 100644
index 00000000000..4be93d77c41
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathExpFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathExpFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathExpFunction(GroupingExpression exp) {
+ super("math.exp", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathFloorFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathFloorFunction.java
new file mode 100644
index 00000000000..f105332e352
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathFloorFunction.java
@@ -0,0 +1,16 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/** represents the math.floor(expression) function */
+public class MathFloorFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathFloorFunction(GroupingExpression exp) {
+ super("math.floor", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathFunctions.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathFunctions.java
new file mode 100644
index 00000000000..5fe5a971be9
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathFunctions.java
@@ -0,0 +1,69 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * @author <a href="mailto:balder@yahoo-inc.com">Henning Baldersheim</a>
+ */
+public abstract class MathFunctions {
+ /**
+ * Defines the different types of math functions that are available.
+ */
+ public enum Function {
+ EXP, // 0
+ POW, // 1
+ LOG, // 2
+ LOG1P, // 3
+ LOG10, // 4
+ SIN, // 5
+ ASIN, // 6
+ COS, // 7
+ ACOS, // 8
+ TAN, // 9
+ ATAN, // 10
+ SQRT, // 11
+ SINH, // 12
+ ASINH, // 13
+ COSH, // 14
+ ACOSH, // 15
+ TANH, // 16
+ ATANH, // 17
+ CBRT, // 18
+ HYPOT, // 19
+ FLOOR; // 20
+
+ static Function create(int tid) {
+ for(Function p : values()) {
+ if (tid == p.ordinal()) {
+ return p;
+ }
+ }
+ return null;
+ }
+ }
+ public static FunctionNode newInstance(Function type, GroupingExpression x, GroupingExpression y) {
+ switch (type) {
+ case EXP: return new MathExpFunction(x);
+ case POW: return new MathPowFunction(x, y);
+ case LOG: return new MathLogFunction(x);
+ case LOG1P: return new MathLog1pFunction(x);
+ case LOG10: return new MathLog10Function(x);
+ case SIN: return new MathSinFunction(x);
+ case ASIN: return new MathASinFunction(x);
+ case COS: return new MathCosFunction(x);
+ case ACOS: return new MathACosFunction(x);
+ case TAN: return new MathTanFunction(x);
+ case ATAN: return new MathATanFunction(x);
+ case SQRT: return new MathSqrtFunction(x);
+ case SINH: return new MathSinHFunction(x);
+ case ASINH: return new MathASinHFunction(x);
+ case COSH: return new MathCosHFunction(x);
+ case ACOSH: return new MathACosHFunction(x);
+ case TANH: return new MathTanHFunction(x);
+ case ATANH: return new MathATanHFunction(x);
+ case CBRT: return new MathCbrtFunction(x);
+ case HYPOT: return new MathHypotFunction(x, y);
+ case FLOOR: return new MathFloorFunction(x);
+ }
+ return null;
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathHypotFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathHypotFunction.java
new file mode 100644
index 00000000000..777a94f9107
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathHypotFunction.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathHypotFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param x The expression to evaluate for x, double value will be requested.
+ * @param y The expression to evaluate for y exponent, double value will be requested.
+ */
+ public MathHypotFunction(GroupingExpression x, GroupingExpression y) {
+ super("math.hypot", Arrays.asList(x, y));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathLog10Function.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathLog10Function.java
new file mode 100644
index 00000000000..444ea7a7349
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathLog10Function.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathLog10Function extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathLog10Function(GroupingExpression exp) {
+ super("math.log10", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathLog1pFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathLog1pFunction.java
new file mode 100644
index 00000000000..3be6c799bf2
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathLog1pFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathLog1pFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathLog1pFunction(GroupingExpression exp) {
+ super("math.log1p", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathLogFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathLogFunction.java
new file mode 100644
index 00000000000..4d3b43d45b0
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathLogFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathLogFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathLogFunction(GroupingExpression exp) {
+ super("math.log", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathPowFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathPowFunction.java
new file mode 100644
index 00000000000..09a9a28cbb0
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathPowFunction.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathPowFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param x The expression to evaluate for base, double value will be requested.
+ * @param y The expression to evaluate for the exponent, double value will be requested.
+ */
+ public MathPowFunction(GroupingExpression x, GroupingExpression y) {
+ super("math.pow", Arrays.asList(x,y));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathResolver.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathResolver.java
new file mode 100644
index 00000000000..9410c6ea347
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathResolver.java
@@ -0,0 +1,121 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Stack;
+
+/**
+ * This is a helper class for resolving arithmetic operations over {@link GroupingExpression} objects. To resolve an
+ * operation simply push operator-expression pairs onto it, before calling {@link #resolve()} to retrieve the single
+ * corresponding grouping expression object.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class MathResolver {
+
+ public enum Type {
+
+ ADD(0, "+"),
+ SUB(1, "-"),
+ DIV(2, "/"),
+ MOD(3, "%"),
+ MUL(4, "*");
+
+ private final int pre;
+ private final String image;
+
+ private Type(int pre, String image) {
+ this.pre = pre;
+ this.image = image;
+ }
+ }
+
+ private final List<Item> items = new LinkedList<>();
+
+ /**
+ * Pushes the given operator-expression pair onto this math resolver. Once all pairs have been pushed using this
+ * method, call {@link #resolve()} to retrieve to combined grouping expression.
+ *
+ * @param type The operator that appears before the expression being pushed.
+ * @param exp The expression to push.
+ */
+ public void push(Type type, GroupingExpression exp) {
+ if (items.isEmpty() && type != Type.ADD) {
+ throw new IllegalArgumentException("First item in an arithmetic operation must be an addition.");
+ }
+ items.add(new Item(type, exp));
+ }
+
+ /**
+ * Converts the internal list of operator-expression pairs into a corresponding combined grouping expression. When
+ * this method returns there is no residue of the conversion, and this object can be reused.
+ *
+ * @return The grouping expression corresponding to the pushed arithmetic operations.
+ */
+ public GroupingExpression resolve() {
+ if (items.size() == 1) {
+ return items.remove(0).exp; // optimize common case
+ }
+ Stack<Item> stack = new Stack<>();
+ stack.push(items.remove(0));
+ while (!items.isEmpty()) {
+ Item item = items.remove(0);
+ while (stack.size() > 1 && stack.peek().type.pre >= item.type.pre) {
+ pop(stack);
+ }
+ stack.push(item);
+ }
+ while (stack.size() > 1) {
+ pop(stack);
+ }
+ return stack.remove(0).exp;
+ }
+
+ private void pop(Stack<Item> stack) {
+ Item rhs = stack.pop();
+ Item lhs = stack.peek();
+ switch (rhs.type) {
+ case ADD:
+ lhs.exp = new AddFunction(lhs.exp, rhs.exp);
+ break;
+ case DIV:
+ lhs.exp = new DivFunction(lhs.exp, rhs.exp);
+ break;
+ case MOD:
+ lhs.exp = new ModFunction(lhs.exp, rhs.exp);
+ break;
+ case MUL:
+ lhs.exp = new MulFunction(lhs.exp, rhs.exp);
+ break;
+ case SUB:
+ lhs.exp = new SubFunction(lhs.exp, rhs.exp);
+ break;
+ default:
+ throw new UnsupportedOperationException("Operator " + rhs.type + " not supported.");
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder ret = new StringBuilder();
+ for (int i = 0, len = items.size(); i < len; ++i) {
+ Item item = items.get(i);
+ if (i != 0) {
+ ret.append(" ").append(item.type.image).append(" ");
+ }
+ ret.append(item.exp.toString());
+ }
+ return ret.toString();
+ }
+
+ private static class Item {
+ final Type type;
+ GroupingExpression exp;
+
+ Item(Type type, GroupingExpression exp) {
+ this.type = type;
+ this.exp = exp;
+ }
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathSinFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathSinFunction.java
new file mode 100644
index 00000000000..66612e9d80a
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathSinFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathSinFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathSinFunction(GroupingExpression exp) {
+ super("math.sin", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathSinHFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathSinHFunction.java
new file mode 100644
index 00000000000..79d260f51a0
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathSinHFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathSinHFunction extends FunctionNode {
+/**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathSinHFunction(GroupingExpression exp) {
+ super("math.sinh", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathSqrtFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathSqrtFunction.java
new file mode 100644
index 00000000000..18c9396dd12
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathSqrtFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathSqrtFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathSqrtFunction(GroupingExpression exp) {
+ super("math.sqrt", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathTanFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathTanFunction.java
new file mode 100644
index 00000000000..67db7a9d834
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathTanFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathTanFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathTanFunction(GroupingExpression exp) {
+ super("math.tan", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MathTanHFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MathTanHFunction.java
new file mode 100644
index 00000000000..e111c1199d7
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MathTanHFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author balder
+ */
+public class MathTanHFunction extends FunctionNode {
+/**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, double value will be requested.
+ */
+ public MathTanHFunction(GroupingExpression exp) {
+ super("math.tanh", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MaxAggregator.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MaxAggregator.java
new file mode 100644
index 00000000000..93f9e3c068e
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MaxAggregator.java
@@ -0,0 +1,20 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents an maximum-aggregator in a {@link GroupingExpression}. It evaluates to the maximum value that
+ * the contained expression evaluated to over all the inputs.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class MaxAggregator extends AggregatorNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to aggregate on.
+ */
+ public MaxAggregator(GroupingExpression exp) {
+ super("max", exp);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MaxFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MaxFunction.java
new file mode 100644
index 00000000000..da80a627c27
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MaxFunction.java
@@ -0,0 +1,43 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents a max-function in a {@link GroupingExpression}. It evaluates to a number that equals the
+ * largest of the results of all arguments.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class MaxFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument, must evaluate to a number.
+ * @param arg2 The second compulsory argument, must evaluate to a number.
+ * @param argN The optional arguments, must evaluate to a number.
+ */
+ public MaxFunction(GroupingExpression arg1, GroupingExpression arg2, GroupingExpression... argN) {
+ this(asList(arg1, arg2, argN));
+ }
+
+ private MaxFunction(List<GroupingExpression> args) {
+ super("max", args);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param args The arguments to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the number of arguments is less than 2.
+ */
+ public static MaxFunction newInstance(List<GroupingExpression> args) {
+ if (args.size() < 2) {
+ throw new IllegalArgumentException("Expected 2 or more arguments, got " + args.size() + ".");
+ }
+ return new MaxFunction(args);
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/Md5Function.java b/container-search/src/main/java/com/yahoo/search/grouping/request/Md5Function.java
new file mode 100644
index 00000000000..b2bd503c52f
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/Md5Function.java
@@ -0,0 +1,33 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents an md5-function in a {@link GroupingExpression}. It evaluates to a long that equals the md5 of
+ * the result of the argument.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class Md5Function extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate.
+ * @param numBits The number of bits of the md5 to include.
+ */
+ public Md5Function(GroupingExpression exp, int numBits) {
+ super("md5", Arrays.asList(exp, new LongValue(numBits)));
+ }
+
+ /**
+ * Returns the number of bits of the md5 to include in the evaluated result.
+ *
+ * @return The bit count.
+ */
+ public int getNumBits() {
+ return ((LongValue)getArg(1)).getValue().intValue();
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MinAggregator.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MinAggregator.java
new file mode 100644
index 00000000000..5bb2f6675c8
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MinAggregator.java
@@ -0,0 +1,20 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents an minimum-aggregator in a {@link GroupingExpression}. It evaluates to the minimum value that
+ * the contained expression evaluated to over all the inputs.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class MinAggregator extends AggregatorNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to aggregate on.
+ */
+ public MinAggregator(GroupingExpression exp) {
+ super("min", exp);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MinFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MinFunction.java
new file mode 100644
index 00000000000..f66e23b87c0
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MinFunction.java
@@ -0,0 +1,43 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents a min-function in a {@link GroupingExpression}. It evaluates to a number that equals the
+ * smallest of the results of all arguments.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class MinFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument, must evaluate to a number.
+ * @param arg2 The second compulsory argument, must evaluate to a number.
+ * @param argN The optional arguments, must evaluate to a number.
+ */
+ public MinFunction(GroupingExpression arg1, GroupingExpression arg2, GroupingExpression... argN) {
+ this(asList(arg1, arg2, argN));
+ }
+
+ private MinFunction(List<GroupingExpression> args) {
+ super("min", args);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param args The arguments to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the number of arguments is less than 2.
+ */
+ public static MinFunction newInstance(List<GroupingExpression> args) {
+ if (args.size() < 2) {
+ throw new IllegalArgumentException("Expected 2 or more arguments, got " + args.size() + ".");
+ }
+ return new MinFunction(args);
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MinuteOfHourFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MinuteOfHourFunction.java
new file mode 100644
index 00000000000..cb4b65f20b8
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MinuteOfHourFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a minute-of-hour timestamp-function in a {@link GroupingExpression}. It evaluates to a long
+ * that equals the minute of hour (0-59) of the result of the argument.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class MinuteOfHourFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ */
+ public MinuteOfHourFunction(GroupingExpression exp) {
+ super("time.minuteofhour", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ModFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ModFunction.java
new file mode 100644
index 00000000000..d3d2502b714
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ModFunction.java
@@ -0,0 +1,43 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents a mod-function in a {@link GroupingExpression}. It evaluates to a number that equals the result
+ * of mod'ing the results of all arguments in the order they were given to the constructor (modulo first argument by
+ * second, result by third, ...).
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class ModFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument, must evaluate to a number.
+ * @param arg2 The second compulsory argument, must evaluate to a number.
+ * @param argN The optional arguments, must evaluate to a number.
+ */
+ public ModFunction(GroupingExpression arg1, GroupingExpression arg2, GroupingExpression... argN) {
+ this(asList(arg1, arg2, argN));
+ }
+
+ private ModFunction(List<GroupingExpression> args) {
+ super("mod", args);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param args The arguments to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the number of arguments is less than 2.
+ */
+ public static ModFunction newInstance(List<GroupingExpression> args) {
+ if (args.size() < 2) {
+ throw new IllegalArgumentException("Expected 2 or more arguments, got " + args.size() + ".");
+ }
+ return new ModFunction(args);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MonthOfYearFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MonthOfYearFunction.java
new file mode 100644
index 00000000000..25f39892ee1
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MonthOfYearFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a month-of-year timestamp-function in a {@link GroupingExpression}. It evaluates to a long that
+ * equals the month of year (1-12) of the result of the argument.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class MonthOfYearFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ */
+ public MonthOfYearFunction(GroupingExpression exp) {
+ super("time.monthofyear", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/MulFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/MulFunction.java
new file mode 100644
index 00000000000..d66361888b0
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/MulFunction.java
@@ -0,0 +1,42 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents a mul-function in a {@link GroupingExpression}. It evaluates to a number that equals the result
+ * of multiplying the results of all arguments together in the order they were given to the constructor.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class MulFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument, must evaluate to a number.
+ * @param arg2 The second compulsory argument, must evaluate to a number.
+ * @param argN The optional arguments, must evaluate to a number.
+ */
+ public MulFunction(GroupingExpression arg1, GroupingExpression arg2, GroupingExpression... argN) {
+ this(asList(arg1, arg2, argN));
+ }
+
+ private MulFunction(List<GroupingExpression> args) {
+ super("mul", args);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param args The arguments to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the number of arguments is less than 2.
+ */
+ public static MulFunction newInstance(List<GroupingExpression> args) {
+ if (args.size() < 2) {
+ throw new IllegalArgumentException("Expected 2 or more arguments, got " + args.size() + ".");
+ }
+ return new MulFunction(args);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/NegFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/NegFunction.java
new file mode 100644
index 00000000000..7ea2b3a788b
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/NegFunction.java
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a negate-function in a {@link GroupingExpression}. It evaluates to a number that equals the
+ * negative of the results of the argument.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class NegFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a number.
+ */
+ public NegFunction(GroupingExpression exp) {
+ super("neg", Arrays.asList(exp));
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/NormalizeSubjectFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/NormalizeSubjectFunction.java
new file mode 100644
index 00000000000..1eaad713383
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/NormalizeSubjectFunction.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ */
+public class NormalizeSubjectFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a string.
+ */
+ public NormalizeSubjectFunction(GroupingExpression exp) {
+ super("normalizesubject", Arrays.asList(exp));
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/NowFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/NowFunction.java
new file mode 100644
index 00000000000..f876ee9a1df
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/NowFunction.java
@@ -0,0 +1,21 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Collections;
+
+/**
+ * This class represents a now-function in a {@link GroupingExpression}. It evaluates to a long that equals the number
+ * of seconds since midnight, January 1, 1970 UTC.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class NowFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ */
+ public NowFunction() {
+ super("now", Collections.<GroupingExpression>emptyList());
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/OrFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/OrFunction.java
new file mode 100644
index 00000000000..0a7ec7ecc06
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/OrFunction.java
@@ -0,0 +1,43 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents an or-function in a {@link GroupingExpression}. It evaluates to a long that equals the result
+ * of or'ing the results of all arguments together in the order they were given to the constructor.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class OrFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument, must evaluate to a long.
+ * @param arg2 The second compulsory argument, must evaluate to a long.
+ * @param argN The optional arguments, must evaluate to a long.
+ */
+ public OrFunction(GroupingExpression arg1, GroupingExpression arg2, GroupingExpression... argN) {
+ this(asList(arg1, arg2, argN));
+ }
+
+ private OrFunction(List<GroupingExpression> args) {
+ super("or", args);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param args The arguments to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the number of arguments is less than 2.
+ */
+ public static OrFunction newInstance(List<GroupingExpression> args) {
+ if (args.size() < 2) {
+ throw new IllegalArgumentException("Expected 2 or more arguments, got " + args.size() + ".");
+ }
+ return new OrFunction(args);
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/PredefinedFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/PredefinedFunction.java
new file mode 100644
index 00000000000..b00ee97452c
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/PredefinedFunction.java
@@ -0,0 +1,58 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This class represents a predefined bucket-function in a {@link GroupingExpression}. It maps the input into one of the
+ * given buckets by the result of the argument expression.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public abstract class PredefinedFunction extends FunctionNode {
+
+ protected PredefinedFunction(GroupingExpression exp, List<? extends BucketValue> args) {
+ super("predefined", asList(exp, args));
+ Iterator<? extends BucketValue> it = args.iterator();
+ BucketValue prev = it.next();
+ while (it.hasNext()) {
+ BucketValue arg = it.next();
+ if (prev.compareTo(arg) >= 0) {
+ throw new IllegalArgumentException("Buckets must be monotonically increasing, got " + prev +
+ " before " + arg + ".");
+ }
+ prev = arg;
+ }
+ }
+
+ /**
+ * Returns the number of buckets to divide the result into.
+ *
+ * @return The bucket count.
+ */
+ public int getNumBuckets() {
+ return getNumArgs() - 1;
+ }
+
+ /**
+ * Returns the bucket at the given index.
+ *
+ * @param i The index of the bucket to return.
+ * @return The bucket at the given index.
+ * @throws IndexOutOfBoundsException If the index is out of range.
+ */
+ public BucketValue getBucket(int i) {
+ return (BucketValue)getArg(i + 1);
+ }
+
+ private static
+ List<GroupingExpression> asList(GroupingExpression exp, List<? extends BucketValue> args) {
+ List<GroupingExpression> ret = new LinkedList<>();
+ ret.add(exp);
+ ret.addAll(args);
+ return ret;
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/RawBucket.java b/container-search/src/main/java/com/yahoo/search/grouping/request/RawBucket.java
new file mode 100644
index 00000000000..d13b8b6ca67
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/RawBucket.java
@@ -0,0 +1,40 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a {@link RawValue} bucket in a {@link PredefinedFunction}.
+ *
+ * @author <a href="mailto:lulf@yahoo-inc.com">Ulf Lilleengen</a>
+ */
+public class RawBucket extends BucketValue {
+
+ /**
+ * Get the next distinct value.
+ *
+ * @param value The base value.
+ * @return the next value.
+ */
+ public static RawValue nextValue(RawValue value) {
+ return new RawValue(value.getValue().clone().put((byte)0));
+ }
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param from The from-value to assign to this.
+ * @param to The to-value to assign to this.
+ */
+ public RawBucket(RawBuffer from, RawBuffer to) {
+ super(new RawValue(from), new RawValue(to));
+ }
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param from The from-value to assign to this.
+ * @param to The to-value to assign to this.
+ */
+ public RawBucket(ConstantValue<?> from, ConstantValue<?> to) {
+ super(from, to);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/RawBuffer.java b/container-search/src/main/java/com/yahoo/search/grouping/request/RawBuffer.java
new file mode 100644
index 00000000000..00b9c899263
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/RawBuffer.java
@@ -0,0 +1,123 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.ArrayList;
+
+/**
+ * This class represents a buffer of byte values to be used as a backing buffer
+ * for raw buckets.
+ *
+ * @author <a href="mailto:lulf@yahoo-inc.com">Ulf Lilleengen</a>
+ */
+public class RawBuffer implements Comparable<RawBuffer>, Cloneable {
+ private final ArrayList<Byte> buffer;
+
+ /**
+ * Create an empty buffer.
+ */
+ public RawBuffer() {
+ this.buffer = new ArrayList<>();
+ }
+
+ /**
+ * Create a buffer with initial content.
+ *
+ * @param buffer A buffer of values to be assigned this buffer.
+ */
+ public RawBuffer(ArrayList<Byte> buffer) {
+ this.buffer = buffer;
+ }
+
+ /**
+ * Create a buffer with initial content.
+ *
+ * @param bytes A buffer of bytes to be assigned this buffer.
+ */
+ public RawBuffer(byte[] bytes) {
+ buffer = new ArrayList<>();
+ put(bytes);
+ }
+
+ /**
+ * Insert a byte value into this buffer.
+ *
+ * @param value The value to add to the buffer.
+ * @return Reference to this.
+ */
+ public RawBuffer put(byte value) {
+ buffer.add(value);
+ return this;
+ }
+
+ /**
+ * Insert an array of byte values into this buffer.
+ *
+ * @param values The array to add to the buffer.
+ * @return Reference to this.
+ */
+ public RawBuffer put(byte[] values) {
+ for (int i = 0; i < values.length; i++) {
+ buffer.add(values[i]);
+ }
+ return this;
+ }
+
+ /**
+ * Create a copy of data in the internal buffer.
+ *
+ * @return A copy of the data.
+ */
+ public byte[] getBytes() {
+ byte[] ret = new byte[buffer.size()];
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = buffer.get(i);
+ }
+ return ret;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("{");
+ for (int i = 0; i < buffer.size(); i++) {
+ s.append(buffer.get(i));
+ if (i < buffer.size() - 1) {
+ s.append(",");
+ }
+ }
+ s.append("}");
+ return s.toString();
+ }
+
+ @Override
+ public RawBuffer clone() {
+ return new RawBuffer(new ArrayList<>(buffer));
+ }
+
+ @Override
+ public int compareTo(RawBuffer rhs) {
+ Byte[] my = buffer.toArray(new Byte[0]);
+ Byte[] their = rhs.buffer.toArray(new Byte[0]);
+ for (int i = 0; i < my.length && i < their.length; i++) {
+ if (my[i] < their[i]) {
+ return -1;
+ } else if (my[i] > their[i]) {
+ return 1;
+ }
+ }
+ return (my.length < their.length ? -1 : (my.length > their.length ? 1 : 0));
+ }
+
+ @Override
+ public int hashCode() {
+ return buffer.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object rhs) {
+ if (rhs instanceof RawBuffer) {
+ return (compareTo((RawBuffer)rhs) == 0);
+ }
+ return false;
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/RawPredefined.java b/container-search/src/main/java/com/yahoo/search/grouping/request/RawPredefined.java
new file mode 100644
index 00000000000..c2650346231
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/RawPredefined.java
@@ -0,0 +1,48 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents a predefined bucket-function in a {@link GroupingExpression} for expressions that evaluate to a
+ * raw.
+ *
+ * @author <a href="mailto:lulf@yahoo-inc.com">Ulf Lilleengen</a>
+ */
+public class RawPredefined extends PredefinedFunction {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a string.
+ * @param arg1 The compulsory bucket.
+ * @param argN The optional buckets.
+ */
+ public RawPredefined(GroupingExpression exp, RawBucket arg1, RawBucket... argN) {
+ this(exp, asList(arg1, argN));
+ }
+
+ private RawPredefined(GroupingExpression exp, List<RawBucket> args) {
+ super(exp, args);
+ }
+
+ @Override
+ public RawBucket getBucket(int i) {
+ return (RawBucket)getArg(i + 1);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param exp The expression to evaluate, must evaluate to a string.
+ * @param args The buckets to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the list of buckets is empty.
+ */
+ public static RawPredefined newInstance(GroupingExpression exp, List<RawBucket> args) {
+ if (args.isEmpty()) {
+ throw new IllegalArgumentException("Expected at least one bucket, got none.");
+ }
+ return new RawPredefined(exp, args);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/RawValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/RawValue.java
new file mode 100644
index 00000000000..a04944d7897
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/RawValue.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a raw value in a {@link GroupingExpression}.
+ *
+ * @author <a href="mailto:lulf@yahoo-inc.com">Ulf Lilleengen</a>
+ */
+public class RawValue extends ConstantValue<RawBuffer> {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param value The immutable value to assign to this.
+ */
+ public RawValue(RawBuffer value) {
+ super(value);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/RelevanceValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/RelevanceValue.java
new file mode 100644
index 00000000000..8a5d4dc75d1
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/RelevanceValue.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a document relevance score in a {@link GroupingExpression}. It evaluates to the relevance of
+ * the input {@link com.yahoo.search.result.Hit}.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class RelevanceValue extends DocumentValue {
+
+ /**
+ * Constructs a new instance of this class.
+ */
+ public RelevanceValue() {
+ super("relevance()");
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ReverseFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ReverseFunction.java
new file mode 100644
index 00000000000..274bb20c9f7
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ReverseFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a reverse-function in a {@link GroupingExpression}. It evaluates to a list that equals the list
+ * result of the argument, sorted in descending order.
+ *
+ * @author <a href="mailto:balder@yahoo-inc.com">Henning Baldersheim</a>
+ */
+public class ReverseFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a list.
+ */
+ public ReverseFunction(GroupingExpression exp) {
+ super("reverse", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/SecondOfMinuteFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/SecondOfMinuteFunction.java
new file mode 100644
index 00000000000..9443f862a16
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/SecondOfMinuteFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a second-of-minute timestamp-function in a {@link GroupingExpression}. It evaluates to a long
+ * that equals the second of minute (0-59) of the result of the argument.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class SecondOfMinuteFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ */
+ public SecondOfMinuteFunction(GroupingExpression exp) {
+ super("time.secondofminute", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/SizeFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/SizeFunction.java
new file mode 100644
index 00000000000..d445007a039
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/SizeFunction.java
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a size-function in a {@link GroupingExpression}. It evaluates to a number that equals the
+ * number of elements in the result of the argument (e.g. the number of elements in an array).
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class SizeFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate.
+ */
+ public SizeFunction(GroupingExpression exp) {
+ super("size", Arrays.asList(exp));
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/SortFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/SortFunction.java
new file mode 100644
index 00000000000..2a8845f9847
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/SortFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a sort-function in a {@link GroupingExpression}. It evaluates to a list that equals the list
+ * result of the argument, sorted in ascending order.
+ *
+ * @author <a href="mailto:balder@yahoo-inc.com">Henning Baldersheim</a>
+ */
+public class SortFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a list.
+ */
+ public SortFunction(GroupingExpression exp) {
+ super("sort", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/StrCatFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/StrCatFunction.java
new file mode 100644
index 00000000000..455f9dee917
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/StrCatFunction.java
@@ -0,0 +1,43 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents a strcat-function in a {@link GroupingExpression}. It evaluates to a string that equals the
+ * contatenation of the string results of all arguments in the order they were given to the constructor.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class StrCatFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument, must evaluate to a string.
+ * @param arg2 The second compulsory argument, must evaluate to a string.
+ * @param argN The optional arguments, must evaluate to a string.
+ */
+ public StrCatFunction(GroupingExpression arg1, GroupingExpression arg2, GroupingExpression... argN) {
+ this(asList(arg1, arg2, argN));
+ }
+
+ private StrCatFunction(List<GroupingExpression> args) {
+ super("strcat", args);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param args The arguments to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the number of arguments is less than 2.
+ */
+ public static StrCatFunction newInstance(List<GroupingExpression> args) {
+ if (args.size() < 2) {
+ throw new IllegalArgumentException("Expected 2 or more arguments, got " + args.size() + ".");
+ }
+ return new StrCatFunction(args);
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/StrLenFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/StrLenFunction.java
new file mode 100644
index 00000000000..2ef53f53bf2
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/StrLenFunction.java
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a strcat-function in a {@link GroupingExpression}. It evaluates to a long that equals the
+ * number of bytes in the string result of the argument.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class StrLenFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a string.
+ */
+ public StrLenFunction(GroupingExpression exp) {
+ super("strlen", Arrays.asList(exp));
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/StringBucket.java b/container-search/src/main/java/com/yahoo/search/grouping/request/StringBucket.java
new file mode 100644
index 00000000000..34c7b9f526a
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/StringBucket.java
@@ -0,0 +1,40 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a {@link String} bucket in a {@link PredefinedFunction}.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class StringBucket extends BucketValue {
+
+ /**
+ * Get the next distinct value.
+ *
+ * @param value The base value.
+ * @return the next value.
+ */
+ public static StringValue nextValue(StringValue value) {
+ return new StringValue(value.getValue() + " ");
+ }
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param from The from-value to assign to this.
+ * @param to The to-value to assign to this.
+ */
+ public StringBucket(String from, String to) {
+ super(new StringValue(from), new StringValue(to));
+ }
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param from The from-value to assign to this.
+ * @param to The to-value to assign to this.
+ */
+ public StringBucket(ConstantValue<?> from, ConstantValue<?> to) {
+ super(from, to);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/StringPredefined.java b/container-search/src/main/java/com/yahoo/search/grouping/request/StringPredefined.java
new file mode 100644
index 00000000000..d3a469fdd7e
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/StringPredefined.java
@@ -0,0 +1,48 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents a predefined bucket-function in a {@link GroupingExpression} for expressions that evaluate to a
+ * string.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class StringPredefined extends PredefinedFunction {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a string.
+ * @param arg1 The compulsory bucket.
+ * @param argN The optional buckets.
+ */
+ public StringPredefined(GroupingExpression exp, StringBucket arg1, StringBucket... argN) {
+ this(exp, asList(arg1, argN));
+ }
+
+ private StringPredefined(GroupingExpression exp, List<StringBucket> args) {
+ super(exp, args);
+ }
+
+ @Override
+ public StringBucket getBucket(int i) {
+ return (StringBucket)getArg(i + 1);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param exp The expression to evaluate, must evaluate to a string.
+ * @param args The buckets to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the list of buckets is empty.
+ */
+ public static StringPredefined newInstance(GroupingExpression exp, List<StringBucket> args) {
+ if (args.isEmpty()) {
+ throw new IllegalArgumentException("Expected at least one bucket, got none.");
+ }
+ return new StringPredefined(exp, args);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/StringValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/StringValue.java
new file mode 100644
index 00000000000..87e818368d6
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/StringValue.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a constant {@link String} value in a {@link GroupingExpression}.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class StringValue extends ConstantValue<String> {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param value The immutable value to assign to this.
+ */
+ public StringValue(String value) {
+ super(value);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/SubFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/SubFunction.java
new file mode 100644
index 00000000000..15e05c50f63
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/SubFunction.java
@@ -0,0 +1,43 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents a div-function in a {@link GroupingExpression}. It evaluates to a number that equals the result
+ * of subtracting the results of all arguments in the order they were given to the constructor (subtract second argument
+ * from first, third from result, ...).
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class SubFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument, must evaluate to a number.
+ * @param arg2 The second compulsory argument, must evaluate to a number.
+ * @param argN The optional arguments, must evaluate to a number.
+ */
+ public SubFunction(GroupingExpression arg1, GroupingExpression arg2, GroupingExpression... argN) {
+ this(asList(arg1, arg2, argN));
+ }
+
+ private SubFunction(List<GroupingExpression> args) {
+ super("sub", args);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param args The arguments to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the number of arguments is less than 2.
+ */
+ public static SubFunction newInstance(List<GroupingExpression> args) {
+ if (args.size() < 2) {
+ throw new IllegalArgumentException("Expected 2 or more arguments, got " + args.size() + ".");
+ }
+ return new SubFunction(args);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/SumAggregator.java b/container-search/src/main/java/com/yahoo/search/grouping/request/SumAggregator.java
new file mode 100644
index 00000000000..1ace1cfbba2
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/SumAggregator.java
@@ -0,0 +1,20 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents an sum-aggregator in a {@link GroupingExpression}. It evaluates to the sum of the values that
+ * the contained expression evaluated to over all the inputs.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class SumAggregator extends AggregatorNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to aggregate on.
+ */
+ public SumAggregator(GroupingExpression exp) {
+ super("sum", exp);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/SummaryValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/SummaryValue.java
new file mode 100644
index 00000000000..72e4c6662d3
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/SummaryValue.java
@@ -0,0 +1,40 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a document summary in a {@link GroupingExpression}. It evaluates to the summary of the input
+ * {@link com.yahoo.search.result.Hit} that corresponds to the named summary class.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class SummaryValue extends DocumentValue {
+
+ private final String name;
+
+ /**
+ * Constructs a new instance of this class, using the default summary class.
+ */
+ public SummaryValue() {
+ super("summary()");
+ name = null;
+ }
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param summaryName The name of the summary class to assign to this.
+ */
+ public SummaryValue(String summaryName) {
+ super("summary(" + summaryName + ")");
+ name = summaryName;
+ }
+
+ /**
+ * Returns the name of the summary class used to retrieve the hit from the search node.
+ *
+ * @return The summary name.
+ */
+ public String getSummaryName() {
+ return name;
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/TimeFunctions.java b/container-search/src/main/java/com/yahoo/search/grouping/request/TimeFunctions.java
new file mode 100644
index 00000000000..bde1c5831b5
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/TimeFunctions.java
@@ -0,0 +1,148 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This abstract class is a factory for timestamp functions in a {@link GroupingExpression}. Apart from offering
+ * per-function factory methods, this class also contains a {@link #newInstance(com.yahoo.search.grouping.request.TimeFunctions.Type,
+ * GroupingExpression)} method which is useful for runtime construction of grouping requests.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public abstract class TimeFunctions {
+
+ /**
+ * Defines the different types of timestamps-functions that are available.
+ */
+ public enum Type {
+ DATE,
+ DAY_OF_MONTH,
+ DAY_OF_WEEK,
+ DAY_OF_YEAR,
+ HOUR_OF_DAY,
+ MINUTE_OF_HOUR,
+ MONTH_OF_YEAR,
+ SECOND_OF_MINUTE,
+ YEAR
+ }
+
+ /**
+ * Creates a new timestamp-function of the specified type for the given {@link GroupingExpression}.
+ *
+ * @param type The type of function to create.
+ * @param exp The expression to evaluate, must evaluate to a long.
+ * @return The created function node.
+ */
+ public static FunctionNode newInstance(Type type, GroupingExpression exp) {
+ switch (type) {
+ case DATE:
+ return newDate(exp);
+ case DAY_OF_MONTH:
+ return newDayOfMonth(exp);
+ case DAY_OF_WEEK:
+ return newDayOfWeek(exp);
+ case DAY_OF_YEAR:
+ return newDayOfYear(exp);
+ case HOUR_OF_DAY:
+ return newHourOfDay(exp);
+ case MINUTE_OF_HOUR:
+ return newMinuteOfHour(exp);
+ case MONTH_OF_YEAR:
+ return newMonthOfYear(exp);
+ case SECOND_OF_MINUTE:
+ return newSecondOfMinute(exp);
+ case YEAR:
+ return newYear(exp);
+ }
+ throw new UnsupportedOperationException("Time function '" + type + "' not supported.");
+ }
+
+ /**
+ * Creates a new instance of {@link DateFunction} for the given {@link GroupingExpression}.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ * @return The created function node.
+ */
+ public static DateFunction newDate(GroupingExpression exp) {
+ return new DateFunction(exp);
+ }
+
+ /**
+ * Creates a new instance of {@link DayOfMonthFunction} for the given {@link GroupingExpression}.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ * @return The created function node.
+ */
+ public static DayOfMonthFunction newDayOfMonth(GroupingExpression exp) {
+ return new DayOfMonthFunction(exp);
+ }
+
+ /**
+ * Creates a new instance of {@link DayOfWeekFunction} for the given {@link GroupingExpression}.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ * @return The created function node.
+ */
+ public static DayOfWeekFunction newDayOfWeek(GroupingExpression exp) {
+ return new DayOfWeekFunction(exp);
+ }
+
+ /**
+ * Creates a new instance of {@link DayOfYearFunction} for the given {@link GroupingExpression}.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ * @return The created function node.
+ */
+ public static DayOfYearFunction newDayOfYear(GroupingExpression exp) {
+ return new DayOfYearFunction(exp);
+ }
+
+ /**
+ * Creates a new instance of {@link HourOfDayFunction} for the given {@link GroupingExpression}.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ * @return The created function node.
+ */
+ public static HourOfDayFunction newHourOfDay(GroupingExpression exp) {
+ return new HourOfDayFunction(exp);
+ }
+
+ /**
+ * Creates a new instance of {@link MinuteOfHourFunction} for the given {@link GroupingExpression}.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ * @return The created function node.
+ */
+ public static MinuteOfHourFunction newMinuteOfHour(GroupingExpression exp) {
+ return new MinuteOfHourFunction(exp);
+ }
+
+ /**
+ * Creates a new instance of {@link MonthOfYearFunction} for the given {@link GroupingExpression}.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ * @return The created function node.
+ */
+ public static MonthOfYearFunction newMonthOfYear(GroupingExpression exp) {
+ return new MonthOfYearFunction(exp);
+ }
+
+ /**
+ * Creates a new instance of {@link SecondOfMinuteFunction} for the given {@link GroupingExpression}.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ * @return The created function node.
+ */
+ public static SecondOfMinuteFunction newSecondOfMinute(GroupingExpression exp) {
+ return new SecondOfMinuteFunction(exp);
+ }
+
+ /**
+ * Creates a new instance of {@link YearFunction} for the given {@link GroupingExpression}.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ * @return The created function node.
+ */
+ public static YearFunction newYear(GroupingExpression exp) {
+ return new YearFunction(exp);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ToDoubleFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ToDoubleFunction.java
new file mode 100644
index 00000000000..8eab2af8691
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ToDoubleFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a todouble-function in a {@link GroupingExpression}. It converts the result of the argument to
+ * a double. If the argument can not be converted, this function returns 0.
+ *
+ * @author <a href="mailto:balder@yahoo-inc.com">Henning Baldersheim</a>
+ */
+public class ToDoubleFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate.
+ */
+ public ToDoubleFunction(GroupingExpression exp) {
+ super("todouble", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ToLongFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ToLongFunction.java
new file mode 100644
index 00000000000..c47a043eea0
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ToLongFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a tolong-function in a {@link GroupingExpression}. It converts the result of the argument to a
+ * long. If the argument can not be converted, this function returns 0.
+ *
+ * @author <a href="mailto:balder@yahoo-inc.com">Henning Baldersheim</a>
+ */
+public class ToLongFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate.
+ */
+ public ToLongFunction(GroupingExpression exp) {
+ super("tolong", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ToRawFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ToRawFunction.java
new file mode 100644
index 00000000000..d1ba3afa28c
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ToRawFunction.java
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a toraw-function in a {@link GroupingExpression}. It
+ * converts the result of the argument to a raw type. If the argument can not
+ * be converted, this function returns null.
+ *
+ * @author <a href="mailto:lulf@yahoo-inc.com">Ulf Lilleengen</a>
+ */
+public class ToRawFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate.
+ */
+ public ToRawFunction(GroupingExpression exp) {
+ super("toraw", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ToStringFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ToStringFunction.java
new file mode 100644
index 00000000000..364d9e5064d
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ToStringFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a tolong-function in a {@link GroupingExpression}. It converts the result of the argument to a
+ * long. If the argument can not be converted, this function returns 0.
+ *
+ * @author <a href="mailto:balder@yahoo-inc.com">Henning Baldersheim</a>
+ */
+public class ToStringFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate.
+ */
+ public ToStringFunction(GroupingExpression exp) {
+ super("tostring", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/UcaFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/UcaFunction.java
new file mode 100644
index 00000000000..2e23f41f139
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/UcaFunction.java
@@ -0,0 +1,63 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents an uca-function in a {@link GroupingExpression}.
+ *
+ * @author <a href="mailto:lulf@yahoo-inc.com">Ulf Lilleengen</a>
+ */
+public class UcaFunction extends FunctionNode {
+
+ private final String locale;
+ private final String strength;
+
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate.
+ * @param locale The locale to used for sorting.
+ */
+ public UcaFunction(GroupingExpression exp, String locale) {
+ super("uca", Arrays.asList(exp, new StringValue(locale)));
+ this.locale = locale;
+ this.strength = "TERTIARY";
+ }
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate.
+ * @param locale The locale to used for sorting.
+ * @param strength The strength level to use.
+ */
+ public UcaFunction(GroupingExpression exp, String locale, String strength) {
+ super("uca", Arrays.asList(exp, new StringValue(locale), new StringValue(strength)));
+ if (!validStrength(strength)) {
+ throw new IllegalArgumentException("Not a valid UCA strength: " + strength);
+ }
+ this.locale = locale;
+ this.strength = strength;
+ }
+
+ private boolean validStrength(String strength) {
+ return (strength.equals("PRIMARY") ||
+ strength.equals("SECONDARY") ||
+ strength.equals("TERTIARY") ||
+ strength.equals("QUATERNARY") ||
+ strength.equals("IDENTICAL"));
+ }
+
+ public String getLocale() {
+ return locale;
+ }
+
+ public String getStrength() {
+ return strength;
+ }
+}
+
+
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/XorAggregator.java b/container-search/src/main/java/com/yahoo/search/grouping/request/XorAggregator.java
new file mode 100644
index 00000000000..be0f092b929
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/XorAggregator.java
@@ -0,0 +1,20 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents an xor-aggregator in a {@link GroupingExpression}. It evaluates to the xor of the values that
+ * the contained expression evaluated to over all the inputs.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class XorAggregator extends AggregatorNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to aggregate on.
+ */
+ public XorAggregator(GroupingExpression exp) {
+ super("xor", exp);
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/XorBitFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/XorBitFunction.java
new file mode 100644
index 00000000000..304917bf905
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/XorBitFunction.java
@@ -0,0 +1,33 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents an xor-function in a {@link GroupingExpression}. It evaluates to a long that equals the xor of
+ * 'width' bits over the binary representation of the result of the argument.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class XorBitFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate.
+ * @param numBits The number of bits of the expression value to xor.
+ */
+ public XorBitFunction(GroupingExpression exp, int numBits) {
+ super("xorbit", Arrays.asList(exp, new LongValue(numBits)));
+ }
+
+ /**
+ * Returns the number of bits of the expression value to xor.
+ *
+ * @return The bit count.
+ */
+ public int getNumBits() {
+ return ((LongValue)getArg(1)).getValue().intValue();
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/XorFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/XorFunction.java
new file mode 100644
index 00000000000..dc47926ea51
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/XorFunction.java
@@ -0,0 +1,43 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.List;
+
+/**
+ * This class represents an xor-function in a {@link GroupingExpression}. It evaluates to a long that equals the result
+ * of and'ing the results of all arguments together in the order they were given to the constructor.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class XorFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param arg1 The first compulsory argument, must evaluate to a long.
+ * @param arg2 The second compulsory argument, must evaluate to a long.
+ * @param argN The optional arguments, must evaluate to a long.
+ */
+ public XorFunction(GroupingExpression arg1, GroupingExpression arg2, GroupingExpression... argN) {
+ this(asList(arg1, arg2, argN));
+ }
+
+ private XorFunction(List<GroupingExpression> args) {
+ super("xor", args);
+ }
+
+ /**
+ * Constructs a new instance of this class from a list of arguments.
+ *
+ * @param args The arguments to pass to the constructor.
+ * @return The created instance.
+ * @throws IllegalArgumentException Thrown if the number of arguments is less than 2.
+ */
+ public static XorFunction newInstance(List<GroupingExpression> args) {
+ if (args.size() < 2) {
+ throw new IllegalArgumentException("Expected 2 or more arguments, got " + args.size() + ".");
+ }
+ return new XorFunction(args);
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/YearFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/YearFunction.java
new file mode 100644
index 00000000000..2115d99140d
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/YearFunction.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * This class represents a year timestamp-function in a {@link GroupingExpression}. It evaluates to a long that equals
+ * the full year (e.g. 2010) of the result of the argument.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class YearFunction extends FunctionNode {
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long.
+ */
+ public YearFunction(GroupingExpression exp) {
+ super("time.year", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/YmumValue.java b/container-search/src/main/java/com/yahoo/search/grouping/request/YmumValue.java
new file mode 100644
index 00000000000..5754edd8155
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/YmumValue.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+/**
+ * This class represents a document checksum in a {@link GroupingExpression}. It evaluates to the YMUM checksum of the
+ * input {@link com.yahoo.search.result.Hit}.
+ *
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class YmumValue extends DocumentValue {
+
+ /**
+ * Constructs a new instance of this class.
+ */
+ public YmumValue() {
+ super("ymum()");
+ }
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ZCurveXFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ZCurveXFunction.java
new file mode 100644
index 00000000000..b4790b912e7
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ZCurveXFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author <a href="mailto:balder@yahoo-inc.com">Henning Baldersheim</a>
+ */
+public class ZCurveXFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long or long[].
+ */
+ public ZCurveXFunction(GroupingExpression exp) {
+ super("zcurve.x", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/ZCurveYFunction.java b/container-search/src/main/java/com/yahoo/search/grouping/request/ZCurveYFunction.java
new file mode 100644
index 00000000000..e9a011f2193
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/ZCurveYFunction.java
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request;
+
+import java.util.Arrays;
+
+/**
+ * @author <a href="mailto:balder@yahoo-inc.com">Henning Baldersheim</a>
+ */
+public class ZCurveYFunction extends FunctionNode {
+ /**
+ * Constructs a new instance of this class.
+ *
+ * @param exp The expression to evaluate, must evaluate to a long or long[].
+ */
+ public ZCurveYFunction(GroupingExpression exp) {
+ super("zcurve.y", Arrays.asList(exp));
+ }
+}
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/package-info.java b/container-search/src/main/java/com/yahoo/search/grouping/request/package-info.java
new file mode 100644
index 00000000000..ff30ef2b939
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/package-info.java
@@ -0,0 +1,7 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+@ExportPackage
+@PublicApi
+package com.yahoo.search.grouping.request;
+
+import com.yahoo.api.annotations.PublicApi;
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/container-search/src/main/java/com/yahoo/search/grouping/request/parser/GroupingParserInput.java b/container-search/src/main/java/com/yahoo/search/grouping/request/parser/GroupingParserInput.java
new file mode 100644
index 00000000000..e87291fba18
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/grouping/request/parser/GroupingParserInput.java
@@ -0,0 +1,14 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.grouping.request.parser;
+
+import com.yahoo.javacc.FastCharStream;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class GroupingParserInput extends FastCharStream implements CharStream {
+
+ public GroupingParserInput(String input) {
+ super(input);
+ }
+}