Browse Source

Allow serial_diff under min_doc_count aggs (#86401)

Before 6.6 we allowed the `serial_diff` agg in lots of places, including
under `date_histogram` aggregations with `min_doc_count: 1`. This
allowed you to take the difference of two adjacent buckets, skipping any
that don't have any value. So if you have a value at 10am, no value at
11am, and another at noon the `serial_diff` will diff the 10am and noon
values. In 6.6 we disabled support for this. We'd like it back.
Nik Everett 3 years ago
parent
commit
1ff0ce4567
16 changed files with 257 additions and 49 deletions
  1. 5 0
      docs/changelog/86401.yaml
  2. 163 0
      rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.aggregation/500_serial_diff.yml
  3. 23 0
      server/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java
  4. 20 18
      server/src/main/java/org/elasticsearch/search/aggregations/PipelineAggregationBuilder.java
  5. 9 0
      server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/AutoDateHistogramAggregationBuilder.java
  6. 11 0
      server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregationBuilder.java
  7. 11 0
      server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregationBuilder.java
  8. 1 1
      server/src/main/java/org/elasticsearch/search/aggregations/pipeline/CumulativeSumPipelineAggregationBuilder.java
  9. 1 1
      server/src/main/java/org/elasticsearch/search/aggregations/pipeline/DerivativePipelineAggregationBuilder.java
  10. 1 1
      server/src/main/java/org/elasticsearch/search/aggregations/pipeline/MovFnPipelineAggregationBuilder.java
  11. 2 5
      server/src/test/java/org/elasticsearch/search/aggregations/pipeline/CumulativeSumTests.java
  12. 2 5
      server/src/test/java/org/elasticsearch/search/aggregations/pipeline/DerivativeTests.java
  13. 2 5
      server/src/test/java/org/elasticsearch/search/aggregations/pipeline/MovFnPipelineAggregationBuilderSerializationTests.java
  14. 2 4
      server/src/test/java/org/elasticsearch/search/aggregations/pipeline/SerialDifferenceTests.java
  15. 2 4
      x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/cumulativecardinality/CumulativeCardinalityTests.java
  16. 2 5
      x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/movingPercentiles/MovingPercentilesTests.java

+ 5 - 0
docs/changelog/86401.yaml

@@ -0,0 +1,5 @@
+pr: 86401
+summary: Allow `serial_diff` under `min_doc_count` aggs
+area: Aggregations
+type: bug
+issues: []

+ 163 - 0
rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.aggregation/500_serial_diff.yml

@@ -0,0 +1,163 @@
+basic:
+  - do:
+      bulk:
+        index: test
+        refresh: true
+        body:
+          - { "index": { } }
+          - { "@timestamp": "2022-01-01T00:00:00", "v": 1 }
+          - { "index": { } }
+          - { "@timestamp": "2022-01-01T01:00:00", "v": 2 }
+          - { "index": { } }
+          - { "@timestamp": "2022-01-01T02:00:00", "v": 1 }
+
+  - do:
+      search:
+        body:
+          size: 0
+          aggs:
+            "@timestamp":
+              date_histogram:
+                field: "@timestamp"
+                fixed_interval: 1h
+              aggs:
+                v: {avg: {field: v}}
+                d: {serial_diff: {buckets_path: v}}
+  - match: { hits.total.value: 3 }
+  - length: { aggregations.@timestamp.buckets: 3 }
+  - match: { aggregations.@timestamp.buckets.0.key_as_string: 2022-01-01T00:00:00.000Z }
+  - match: { aggregations.@timestamp.buckets.1.key_as_string: 2022-01-01T01:00:00.000Z }
+  - match: { aggregations.@timestamp.buckets.2.key_as_string: 2022-01-01T02:00:00.000Z }
+  - match: { aggregations.@timestamp.buckets.0.v.value: 1 }
+  - match: { aggregations.@timestamp.buckets.1.v.value: 2 }
+  - match: { aggregations.@timestamp.buckets.2.v.value: 1 }
+  - is_false: aggregations.@timestamp.buckets.0.d
+  - match: { aggregations.@timestamp.buckets.1.d.value: 1 }
+  - match: { aggregations.@timestamp.buckets.2.d.value: -1 }
+
+---
+lag:
+      - do:
+          bulk:
+            index: test
+            refresh: true
+            body:
+              - { "index": { } }
+              - { "@timestamp": "2022-01-01T00:00:00", "v": 1 }
+              - { "index": { } }
+              - { "@timestamp": "2022-01-01T01:00:00", "v": 2 }
+              - { "index": { } }
+              - { "@timestamp": "2022-01-01T02:00:00", "v": 3 }
+              - { "index": { } }
+              - { "@timestamp": "2022-01-01T03:00:00", "v": 1 }
+
+      - do:
+          search:
+            body:
+              size: 0
+              aggs:
+                "@timestamp":
+                  date_histogram:
+                    field: "@timestamp"
+                    fixed_interval: 1h
+                  aggs:
+                    v: { avg: { field: v } }
+                    d: { serial_diff: { buckets_path: v, lag: 2 } }
+      - match: { hits.total.value: 4 }
+      - length: { aggregations.@timestamp.buckets: 4 }
+      - match: { aggregations.@timestamp.buckets.0.key_as_string: 2022-01-01T00:00:00.000Z }
+      - match: { aggregations.@timestamp.buckets.1.key_as_string: 2022-01-01T01:00:00.000Z }
+      - match: { aggregations.@timestamp.buckets.2.key_as_string: 2022-01-01T02:00:00.000Z }
+      - match: { aggregations.@timestamp.buckets.3.key_as_string: 2022-01-01T03:00:00.000Z }
+      - match: { aggregations.@timestamp.buckets.0.v.value: 1 }
+      - match: { aggregations.@timestamp.buckets.1.v.value: 2 }
+      - match: { aggregations.@timestamp.buckets.2.v.value: 3 }
+      - match: { aggregations.@timestamp.buckets.3.v.value: 1 }
+      - is_false: aggregations.@timestamp.buckets.0.d
+      - is_false: aggregations.@timestamp.buckets.1.d
+      - match: { aggregations.@timestamp.buckets.2.d.value: 2 }
+      - match: { aggregations.@timestamp.buckets.3.d.value: -1 }
+
+---
+parent has gap:
+  - do:
+      bulk:
+        index: test
+        refresh: true
+        body:
+          - { "index": { } }
+          - { "@timestamp": "2022-01-01T00:00:00", "v": 1 }
+          - { "index": { } }
+          - { "@timestamp": "2022-01-01T01:00:00", "v": 2 }
+          - { "index": { } }
+          - { "@timestamp": "2022-01-01T03:00:00", "v": 1 }
+
+  - do:
+      search:
+        body:
+          size: 0
+          aggs:
+            "@timestamp":
+              date_histogram:
+                field: "@timestamp"
+                fixed_interval: 1h
+              aggs:
+                v: {avg: {field: v}}
+                d: {serial_diff: {buckets_path: v}}
+  - match: { hits.total.value: 3 }
+  - length: { aggregations.@timestamp.buckets: 4 }
+  - match: { aggregations.@timestamp.buckets.0.key_as_string: 2022-01-01T00:00:00.000Z }
+  - match: { aggregations.@timestamp.buckets.1.key_as_string: 2022-01-01T01:00:00.000Z }
+  - match: { aggregations.@timestamp.buckets.2.key_as_string: 2022-01-01T02:00:00.000Z }
+  - match: { aggregations.@timestamp.buckets.3.key_as_string: 2022-01-01T03:00:00.000Z }
+  - match: { aggregations.@timestamp.buckets.0.v.value: 1 }
+  - match: { aggregations.@timestamp.buckets.1.v.value: 2 }
+  - is_false: aggregations.@timestamp.buckets.2.v.value
+  - match: { aggregations.@timestamp.buckets.3.v.value: 1 }
+  - is_false: aggregations.@timestamp.buckets.0.d
+  - match: { aggregations.@timestamp.buckets.1.d.value: 1 }
+  - is_false: aggregations.@timestamp.buckets.2.d
+  - is_false: aggregations.@timestamp.buckets.3.d
+
+---
+parent has min_doc_count:
+  - skip:
+      version: " - 8.2.99"
+      reason: allowed in 8.3.0
+
+  - do:
+      bulk:
+        index: test
+        refresh: true
+        body:
+          - { "index": { } }
+          - { "@timestamp": "2022-01-01T00:00:00", "v": 1 }
+          - { "index": { } }
+          - { "@timestamp": "2022-01-01T01:00:00", "v": 2 }
+          - { "index": { } }
+          - { "@timestamp": "2022-01-01T03:00:00", "v": 1 }
+
+  - do:
+      search:
+        body:
+          size: 0
+          aggs:
+            "@timestamp":
+              date_histogram:
+                field: "@timestamp"
+                fixed_interval: 1h
+                min_doc_count: 1
+              aggs:
+                v: {avg: {field: v}}
+                d: {serial_diff: {buckets_path: v}}
+  - match: { hits.total.value: 3 }
+  - length: { aggregations.@timestamp.buckets: 3 }
+  - match: { aggregations.@timestamp.buckets.0.key_as_string: 2022-01-01T00:00:00.000Z }
+  - match: { aggregations.@timestamp.buckets.1.key_as_string: 2022-01-01T01:00:00.000Z }
+  - match: { aggregations.@timestamp.buckets.2.key_as_string: 2022-01-01T03:00:00.000Z }
+  - match: { aggregations.@timestamp.buckets.0.v.value: 1 }
+  - match: { aggregations.@timestamp.buckets.1.v.value: 2 }
+  - match: { aggregations.@timestamp.buckets.2.v.value: 1 }
+  - is_false: aggregations.@timestamp.buckets.0.d
+  - match: { aggregations.@timestamp.buckets.1.d.value: 1 }
+  - match: { aggregations.@timestamp.buckets.2.d.value: -1 }

+ 23 - 0
server/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java

@@ -24,6 +24,7 @@ import java.util.Collection;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.function.Consumer;
 
 /**
  * A factory that knows how to create an {@link Aggregator} of a specific type.
@@ -215,4 +216,26 @@ public abstract class AggregationBuilder
         }
         return false;
     }
+
+    /**
+     * Called by aggregations whose parents must be sequentially ordered.
+     * @param type the type of the aggregation being validated
+     * @param name the name of the aggregation being validated
+     * @param addValidationError callback to add validation errors
+     */
+    protected void validateSequentiallyOrdered(String type, String name, Consumer<String> addValidationError) {
+        addValidationError.accept(
+            type + " aggregation [" + name + "] must have a histogram, date_histogram or auto_date_histogram as parent"
+        );
+    }
+
+    /**
+     * Called by aggregations whose parents must be sequentially ordered without any gaps.
+     * @param type the type of the aggregation being validated
+     * @param name the name of the aggregation being validated
+     * @param addValidationError callback to add validation errors
+     */
+    protected void validateSequentiallyOrderedWithoutGaps(String type, String name, Consumer<String> addValidationError) {
+        validateSequentiallyOrdered(type, name, addValidationError);
+    }
 }

+ 20 - 18
server/src/main/java/org/elasticsearch/search/aggregations/PipelineAggregationBuilder.java

@@ -14,9 +14,6 @@ import org.elasticsearch.common.io.stream.VersionedNamedWriteable;
 import org.elasticsearch.index.query.QueryRewriteContext;
 import org.elasticsearch.index.query.Rewriteable;
 import org.elasticsearch.search.aggregations.AggregatorFactories.Builder;
-import org.elasticsearch.search.aggregations.bucket.histogram.AutoDateHistogramAggregationBuilder;
-import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
-import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder;
 import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
 import org.elasticsearch.xcontent.ToXContentFragment;
 
@@ -127,6 +124,15 @@ public abstract class PipelineAggregationBuilder
 
             @Override
             public void validateParentAggSequentiallyOrdered(String type, String name) {
+                noParentCantBeOrdered(type, name);
+            }
+
+            @Override
+            public void validateParentAggSequentiallyOrderedWithoutSkips(String type, String name) {
+                noParentCantBeOrdered(type, name);
+            }
+
+            private void noParentCantBeOrdered(String type, String name) {
                 addValidationError(
                     type
                         + " aggregation ["
@@ -161,21 +167,12 @@ public abstract class PipelineAggregationBuilder
 
             @Override
             public void validateParentAggSequentiallyOrdered(String type, String name) {
-                if (parent instanceof HistogramAggregationBuilder histoParent) {
-                    if (histoParent.minDocCount() != 0) {
-                        addValidationError("parent histogram of " + type + " aggregation [" + name + "] must have min_doc_count of 0");
-                    }
-                } else if (parent instanceof DateHistogramAggregationBuilder histoParent) {
-                    if (histoParent.minDocCount() != 0) {
-                        addValidationError("parent histogram of " + type + " aggregation [" + name + "] must have min_doc_count of 0");
-                    }
-                } else if (parent instanceof AutoDateHistogramAggregationBuilder) {
-                    // Nothing to check
-                } else {
-                    addValidationError(
-                        type + " aggregation [" + name + "] must have a histogram, date_histogram or auto_date_histogram as parent"
-                    );
-                }
+                parent.validateSequentiallyOrdered(type, name, this::addValidationError);
+            }
+
+            @Override
+            public void validateParentAggSequentiallyOrderedWithoutSkips(String type, String name) {
+                parent.validateSequentiallyOrderedWithoutGaps(type, name, this::addValidationError);
             }
         }
 
@@ -216,6 +213,11 @@ public abstract class PipelineAggregationBuilder
          */
         public abstract void validateParentAggSequentiallyOrdered(String type, String name);
 
+        /**
+         * Validates that the parent is sequentially ordered and doesn't have any gps.
+         */
+        public abstract void validateParentAggSequentiallyOrderedWithoutSkips(String type, String name);
+
         /**
          * The validation exception, if there is one. It'll be {@code null}
          * if the context wasn't provided with any exception on creation

+ 9 - 0
server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/AutoDateHistogramAggregationBuilder.java

@@ -34,6 +34,7 @@ import java.time.ZoneId;
 import java.util.Arrays;
 import java.util.Map;
 import java.util.Objects;
+import java.util.function.Consumer;
 
 import static java.util.Map.entry;
 
@@ -352,4 +353,12 @@ public class AutoDateHistogramAggregationBuilder extends ValuesSourceAggregation
             return "RoundingInfo[" + rounding + " " + Arrays.toString(innerIntervals) + "]";
         }
     }
+
+    @Override
+    protected void validateSequentiallyOrdered(String type, String name, Consumer<String> addValidationError) {}
+
+    @Override
+    protected void validateSequentiallyOrderedWithoutGaps(String type, String name, Consumer<String> addValidationError) {
+        // auto_date_histogram never has gaps
+    }
 }

+ 11 - 0
server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregationBuilder.java

@@ -35,6 +35,7 @@ import java.time.ZoneId;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.function.Consumer;
 
 import static java.util.Map.entry;
 
@@ -501,4 +502,14 @@ public class DateHistogramAggregationBuilder extends ValuesSourceAggregationBuil
     public Version getMinimalSupportedVersion() {
         return Version.V_EMPTY;
     }
+
+    @Override
+    protected void validateSequentiallyOrdered(String type, String name, Consumer<String> addValidationError) {}
+
+    @Override
+    protected void validateSequentiallyOrderedWithoutGaps(String type, String name, Consumer<String> addValidationError) {
+        if (minDocCount != 0) {
+            addValidationError.accept("parent histogram of " + type + " aggregation [" + name + "] must have min_doc_count of 0");
+        }
+    }
 }

+ 11 - 0
server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregationBuilder.java

@@ -32,6 +32,7 @@ import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.function.Consumer;
 
 /**
  * A builder for histograms on numeric fields.  This builder can operate on either base numeric fields, or numeric range fields.  IP range
@@ -443,4 +444,14 @@ public class HistogramAggregationBuilder extends ValuesSourceAggregationBuilder<
     public Version getMinimalSupportedVersion() {
         return Version.V_EMPTY;
     }
+
+    @Override
+    protected void validateSequentiallyOrdered(String type, String name, Consumer<String> addValidationError) {}
+
+    @Override
+    protected void validateSequentiallyOrderedWithoutGaps(String type, String name, Consumer<String> addValidationError) {
+        if (minDocCount != 0) {
+            addValidationError.accept("parent histogram of " + type + " aggregation [" + name + "] must have min_doc_count of 0");
+        }
+    }
 }

+ 1 - 1
server/src/main/java/org/elasticsearch/search/aggregations/pipeline/CumulativeSumPipelineAggregationBuilder.java

@@ -97,7 +97,7 @@ public class CumulativeSumPipelineAggregationBuilder extends AbstractPipelineAgg
         if (bucketsPaths.length != 1) {
             context.addBucketPathValidationError("must contain a single entry for aggregation [" + name + "]");
         }
-        context.validateParentAggSequentiallyOrdered(NAME, name);
+        context.validateParentAggSequentiallyOrderedWithoutSkips(NAME, name);
     }
 
     @Override

+ 1 - 1
server/src/main/java/org/elasticsearch/search/aggregations/pipeline/DerivativePipelineAggregationBuilder.java

@@ -145,7 +145,7 @@ public class DerivativePipelineAggregationBuilder extends AbstractPipelineAggreg
             );
         }
 
-        context.validateParentAggSequentiallyOrdered(NAME, name);
+        context.validateParentAggSequentiallyOrderedWithoutSkips(NAME, name);
     }
 
     @Override

+ 1 - 1
server/src/main/java/org/elasticsearch/search/aggregations/pipeline/MovFnPipelineAggregationBuilder.java

@@ -167,7 +167,7 @@ public class MovFnPipelineAggregationBuilder extends AbstractPipelineAggregation
         if (window <= 0) {
             context.addValidationError("[" + WINDOW.getPreferredName() + "] must be a positive, non-zero integer.");
         }
-        context.validateParentAggSequentiallyOrdered(NAME, name);
+        context.validateParentAggSequentiallyOrderedWithoutSkips(NAME, name);
     }
 
     @Override

+ 2 - 5
server/src/test/java/org/elasticsearch/search/aggregations/pipeline/CumulativeSumTests.java

@@ -10,14 +10,13 @@ package org.elasticsearch.search.aggregations.pipeline;
 
 import org.elasticsearch.search.aggregations.AggregationBuilder;
 import org.elasticsearch.search.aggregations.BasePipelineAggregationTestCase;
+import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
 
 import java.io.IOException;
 
 import static java.util.Collections.emptyList;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.nullValue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 public class CumulativeSumTests extends BasePipelineAggregationTestCase<CumulativeSumPipelineAggregationBuilder> {
     @Override
@@ -42,9 +41,7 @@ public class CumulativeSumTests extends BasePipelineAggregationTestCase<Cumulati
     }
 
     public void testInvalidParent() throws IOException {
-        AggregationBuilder parent = mock(AggregationBuilder.class);
-        when(parent.getName()).thenReturn("name");
-
+        AggregationBuilder parent = new TermsAggregationBuilder("name");
         assertThat(
             validate(parent, new CumulativeSumPipelineAggregationBuilder("name", "invalid_agg>metric")),
             equalTo(

+ 2 - 5
server/src/test/java/org/elasticsearch/search/aggregations/pipeline/DerivativeTests.java

@@ -11,6 +11,7 @@ package org.elasticsearch.search.aggregations.pipeline;
 import org.elasticsearch.search.aggregations.AggregationBuilder;
 import org.elasticsearch.search.aggregations.BasePipelineAggregationTestCase;
 import org.elasticsearch.search.aggregations.PipelineAggregationBuilder;
+import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
 import org.elasticsearch.search.aggregations.pipeline.BucketHelpers.GapPolicy;
 
 import java.io.IOException;
@@ -19,8 +20,6 @@ import java.util.Set;
 
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.nullValue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 public class DerivativeTests extends BasePipelineAggregationTestCase<DerivativePipelineAggregationBuilder> {
 
@@ -66,9 +65,7 @@ public class DerivativeTests extends BasePipelineAggregationTestCase<DerivativeP
     public void testValidateException() throws IOException {
         final Set<PipelineAggregationBuilder> aggBuilders = new HashSet<>();
         aggBuilders.add(new DerivativePipelineAggregationBuilder("deriv", "der"));
-        AggregationBuilder parent = mock(AggregationBuilder.class);
-        when(parent.getName()).thenReturn("name");
-
+        AggregationBuilder parent = new TermsAggregationBuilder("name");
         assertThat(
             validate(parent, new DerivativePipelineAggregationBuilder("name", "invalid_agg>metric")),
             equalTo(

+ 2 - 5
server/src/test/java/org/elasticsearch/search/aggregations/pipeline/MovFnPipelineAggregationBuilderSerializationTests.java

@@ -11,6 +11,7 @@ package org.elasticsearch.search.aggregations.pipeline;
 import org.elasticsearch.script.Script;
 import org.elasticsearch.search.aggregations.AggregationBuilder;
 import org.elasticsearch.search.aggregations.BasePipelineAggregationTestCase;
+import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
 
 import java.io.IOException;
 import java.util.Collections;
@@ -19,8 +20,6 @@ import static java.util.Collections.emptyList;
 import static java.util.Collections.emptyMap;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.nullValue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 public class MovFnPipelineAggregationBuilderSerializationTests extends BasePipelineAggregationTestCase<MovFnPipelineAggregationBuilder> {
     @Override
@@ -48,9 +47,7 @@ public class MovFnPipelineAggregationBuilderSerializationTests extends BasePipel
 
     public void testInvalidParent() throws IOException {
         Script script = new Script(Script.DEFAULT_SCRIPT_TYPE, "painless", "test", Collections.emptyMap());
-        AggregationBuilder parent = mock(AggregationBuilder.class);
-        when(parent.getName()).thenReturn("name");
-
+        AggregationBuilder parent = new TermsAggregationBuilder("name");
         assertThat(
             validate(parent, new MovFnPipelineAggregationBuilder("name", "invalid_agg>metric", script, 1)),
             equalTo(

+ 2 - 4
server/src/test/java/org/elasticsearch/search/aggregations/pipeline/SerialDifferenceTests.java

@@ -11,6 +11,7 @@ package org.elasticsearch.search.aggregations.pipeline;
 import org.elasticsearch.search.aggregations.AggregationBuilder;
 import org.elasticsearch.search.aggregations.BasePipelineAggregationTestCase;
 import org.elasticsearch.search.aggregations.PipelineAggregationBuilder;
+import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
 import org.elasticsearch.search.aggregations.pipeline.BucketHelpers.GapPolicy;
 
 import java.io.IOException;
@@ -20,8 +21,6 @@ import java.util.Set;
 import static java.util.Collections.emptyList;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.nullValue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 public class SerialDifferenceTests extends BasePipelineAggregationTestCase<SerialDiffPipelineAggregationBuilder> {
 
@@ -58,8 +57,7 @@ public class SerialDifferenceTests extends BasePipelineAggregationTestCase<Seria
     public void testInvalidParent() throws IOException {
         final Set<PipelineAggregationBuilder> aggBuilders = new HashSet<>();
         aggBuilders.add(createTestAggregatorFactory());
-        AggregationBuilder parent = mock(AggregationBuilder.class);
-        when(parent.getName()).thenReturn("name");
+        AggregationBuilder parent = new TermsAggregationBuilder("t");
         assertThat(
             validate(parent, new SerialDiffPipelineAggregationBuilder("name", "invalid_agg>metric")),
             equalTo(

+ 2 - 4
x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/cumulativecardinality/CumulativeCardinalityTests.java

@@ -13,6 +13,7 @@ import org.elasticsearch.search.aggregations.BasePipelineAggregationTestCase;
 import org.elasticsearch.search.aggregations.bucket.histogram.AutoDateHistogramAggregationBuilder;
 import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
 import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder;
+import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
 
 import java.io.IOException;
 import java.util.List;
@@ -21,8 +22,6 @@ import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.nullValue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 public class CumulativeCardinalityTests extends BasePipelineAggregationTestCase<CumulativeCardinalityPipelineAggregationBuilder> {
     @Override
@@ -63,8 +62,7 @@ public class CumulativeCardinalityTests extends BasePipelineAggregationTestCase<
         assertThat(validate(new AutoDateHistogramAggregationBuilder("name"), builder), nullValue());
 
         // Mocked "test" agg, should fail validation
-        AggregationBuilder stubParent = mock(AggregationBuilder.class);
-        when(stubParent.getName()).thenReturn("name");
+        AggregationBuilder stubParent = new TermsAggregationBuilder("name");
         assertThat(
             validate(stubParent, builder),
             equalTo(

+ 2 - 5
x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/movingPercentiles/MovingPercentilesTests.java

@@ -14,6 +14,7 @@ import org.elasticsearch.search.aggregations.BasePipelineAggregationTestCase;
 import org.elasticsearch.search.aggregations.bucket.histogram.AutoDateHistogramAggregationBuilder;
 import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
 import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder;
+import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
 
 import java.io.IOException;
 import java.util.List;
@@ -22,8 +23,6 @@ import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.nullValue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 public class MovingPercentilesTests extends BasePipelineAggregationTestCase<MovingPercentilesPipelineAggregationBuilder> {
     @Override
@@ -68,9 +67,7 @@ public class MovingPercentilesTests extends BasePipelineAggregationTestCase<Movi
         assertThat(validate(new DateHistogramAggregationBuilder("name"), builder), nullValue());
         assertThat(validate(new AutoDateHistogramAggregationBuilder("name"), builder), nullValue());
 
-        // Mocked "test" agg, should fail validation
-        AggregationBuilder stubParent = mock(AggregationBuilder.class);
-        when(stubParent.getName()).thenReturn("name");
+        AggregationBuilder stubParent = new TermsAggregationBuilder("name");
         assertThat(
             validate(stubParent, builder),
             equalTo(