|
61 | 61 | import org.opensearch.index.mapper.DateFieldMapper; |
62 | 62 | import org.opensearch.index.mapper.DocCountFieldMapper; |
63 | 63 | import org.opensearch.index.mapper.MappedFieldType; |
| 64 | +import org.opensearch.index.mapper.NumberFieldMapper; |
64 | 65 | import org.opensearch.search.MultiValueMode; |
65 | 66 | import org.opensearch.search.aggregations.AggregationBuilder; |
66 | 67 | import org.opensearch.search.aggregations.BucketOrder; |
67 | 68 | import org.opensearch.search.aggregations.InternalAggregation; |
68 | 69 | import org.opensearch.search.aggregations.MultiBucketConsumerService; |
69 | 70 | import org.opensearch.search.aggregations.bucket.terms.StringTerms; |
70 | 71 | import org.opensearch.search.aggregations.bucket.terms.TermsAggregationBuilder; |
| 72 | +import org.opensearch.search.aggregations.metrics.MaxAggregationBuilder; |
71 | 73 | import org.opensearch.search.aggregations.pipeline.PipelineAggregator; |
72 | 74 | import org.opensearch.search.aggregations.support.AggregationInspectionHelper; |
73 | 75 |
|
@@ -257,12 +259,7 @@ public void testAsSubAgg() throws IOException { |
257 | 259 |
|
258 | 260 | public void testSkiplistWithSingleValueDates() throws IOException { |
259 | 261 | // Create index settings with an index sort. |
260 | | - Settings settings = Settings.builder() |
261 | | - .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT) |
262 | | - .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) |
263 | | - .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1) |
264 | | - .putList("index.sort.field", AGGREGABLE_DATE) |
265 | | - .build(); |
| 262 | + Settings settings = getSettingsWithIndexSort(); |
266 | 263 |
|
267 | 264 | IndexMetadata indexMetadata = new IndexMetadata.Builder("index").settings(settings).build(); |
268 | 265 | IndexSettings indexSettings = new IndexSettings(indexMetadata, settings); |
@@ -349,6 +346,132 @@ public void testSkiplistWithSingleValueDates() throws IOException { |
349 | 346 |
|
350 | 347 | } |
351 | 348 |
|
| 349 | + public void testSkiplistWithSingleValueDatesAndSubAggs() throws IOException { |
| 350 | + // Create index settings with an index sort. |
| 351 | + Settings settings = getSettingsWithIndexSort(); |
| 352 | + |
| 353 | + IndexMetadata indexMetadata = new IndexMetadata.Builder("index").settings(settings).build(); |
| 354 | + IndexSettings indexSettings = new IndexSettings(indexMetadata, settings); |
| 355 | + |
| 356 | + MappedFieldType dateType = new DateFieldMapper.DateFieldType(AGGREGABLE_DATE); |
| 357 | + String categoryField = "category"; |
| 358 | + NumberFieldMapper.NumberFieldType categoryType = new NumberFieldMapper.NumberFieldType(categoryField, NumberFieldMapper.NumberType.LONG); |
| 359 | + |
| 360 | + IndexNumericFieldData fieldData = (IndexNumericFieldData) dateType.fielddataBuilder("index", () -> { |
| 361 | + throw new UnsupportedOperationException(); |
| 362 | + }).build(null, null); |
| 363 | + SortField sortField = fieldData.sortField(null, MultiValueMode.MIN, null, false); |
| 364 | + try (Directory directory = newDirectory()) { |
| 365 | + IndexWriterConfig config = newIndexWriterConfig(); |
| 366 | + config.setMergePolicy(NoMergePolicy.INSTANCE); |
| 367 | + config.setIndexSort(new Sort(sortField)); |
| 368 | + String filterField = "type"; |
| 369 | + try (IndexWriter indexWriter = new IndexWriter(directory, config)) { |
| 370 | + |
| 371 | + // First commit - 5 dates with type 1 |
| 372 | + for (int i = 0; i < 5; i++) { |
| 373 | + Document doc = new Document(); |
| 374 | + long timestamp = DateFormatters.from(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parse(DATASET.get(i))) |
| 375 | + .toInstant() |
| 376 | + .toEpochMilli(); |
| 377 | + doc.add(SortedNumericDocValuesField.indexedField(AGGREGABLE_DATE, timestamp)); |
| 378 | + doc.add(new LongPoint(filterField, 1)); |
| 379 | + doc.add(new NumericDocValuesField(categoryField, i % 2)); // alternating categories |
| 380 | + indexWriter.addDocument(doc); |
| 381 | + } |
| 382 | + indexWriter.commit(); |
| 383 | + |
| 384 | + // Second commit - 3 more dates with type 2, skiplist |
| 385 | + for (int i = 5; i < 8; i++) { |
| 386 | + Document doc = new Document(); |
| 387 | + long timestamp = DateFormatters.from(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parse(DATASET.get(i))) |
| 388 | + .toInstant() |
| 389 | + .toEpochMilli(); |
| 390 | + doc.add(SortedNumericDocValuesField.indexedField(AGGREGABLE_DATE, timestamp)); |
| 391 | + doc.add(new LongPoint(filterField, 2)); |
| 392 | + doc.add(new NumericDocValuesField(categoryField, i % 2)); |
| 393 | + indexWriter.addDocument(doc); |
| 394 | + } |
| 395 | + indexWriter.commit(); |
| 396 | + |
| 397 | + // Third commit - 2 more dates with type 2 |
| 398 | + for (int i = 8; i < 10; i++) { |
| 399 | + Document doc = new Document(); |
| 400 | + long timestamp = DateFormatters.from(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parse(DATASET.get(i))) |
| 401 | + .toInstant() |
| 402 | + .toEpochMilli(); |
| 403 | + doc.add(SortedNumericDocValuesField.indexedField(AGGREGABLE_DATE, timestamp)); |
| 404 | + doc.add(new LongPoint(filterField, 2)); |
| 405 | + doc.add(new NumericDocValuesField(categoryField, i % 2)); |
| 406 | + indexWriter.addDocument(doc); |
| 407 | + } |
| 408 | + indexWriter.commit(); |
| 409 | + } |
| 410 | + |
| 411 | + try (IndexReader indexReader = DirectoryReader.open(directory)) { |
| 412 | + IndexSearcher indexSearcher = newSearcher(indexReader, true, true); |
| 413 | + |
| 414 | + // Create date histogram with terms sub-aggregation |
| 415 | + DateHistogramAggregationBuilder aggregationBuilder = new DateHistogramAggregationBuilder("test") |
| 416 | + .field(AGGREGABLE_DATE) |
| 417 | + .calendarInterval(DateHistogramInterval.YEAR) |
| 418 | + .subAggregation(new MaxAggregationBuilder(categoryField).field(categoryField)); |
| 419 | + |
| 420 | + Query query = LongPoint.newExactQuery(filterField, 2); |
| 421 | + |
| 422 | + InternalDateHistogram histogram = searchAndReduce( |
| 423 | + indexSettings, |
| 424 | + indexSearcher, |
| 425 | + query, |
| 426 | + aggregationBuilder, |
| 427 | + 1000, |
| 428 | + false, |
| 429 | + dateType, |
| 430 | + categoryType |
| 431 | + ); |
| 432 | + |
| 433 | + assertEquals(3, histogram.getBuckets().size()); // 2015, 2016, 2017 (only type 2 docs) |
| 434 | + |
| 435 | + // Verify first bucket (2015) with sub-aggregations |
| 436 | + InternalDateHistogram.Bucket bucket2015 = (InternalDateHistogram.Bucket) histogram.getBuckets().get(0); |
| 437 | + assertEquals("2015-01-01T00:00:00.000Z", bucket2015.getKeyAsString()); |
| 438 | + assertEquals(3, bucket2015.getDocCount()); |
| 439 | + |
| 440 | + // The key test: verify that sub-aggregations exist, proving skiplist collector supports them |
| 441 | + assertNotNull("Sub-aggregation should exist for 2015 bucket", bucket2015.getAggregations()); |
| 442 | + assertNotNull("Categories sub-agg should exist", bucket2015.getAggregations().get(categoryField)); |
| 443 | + assertTrue("Should have sub-aggregations", bucket2015.getAggregations().asList().size() > 0); |
| 444 | + |
| 445 | + // Verify second bucket (2016) |
| 446 | + InternalDateHistogram.Bucket bucket2016 = (InternalDateHistogram.Bucket) histogram.getBuckets().get(1); |
| 447 | + assertEquals("2016-01-01T00:00:00.000Z", bucket2016.getKeyAsString()); |
| 448 | + assertEquals(1, bucket2016.getDocCount()); |
| 449 | + |
| 450 | + assertNotNull("Sub-aggregation should exist for 2016 bucket", bucket2016.getAggregations()); |
| 451 | + assertNotNull("Categories sub-agg should exist", bucket2016.getAggregations().get(categoryField)); |
| 452 | + assertTrue("Should have sub-aggregations", bucket2016.getAggregations().asList().size() > 0); |
| 453 | + |
| 454 | + // Verify third bucket (2017) |
| 455 | + InternalDateHistogram.Bucket bucket2017 = (InternalDateHistogram.Bucket) histogram.getBuckets().get(2); |
| 456 | + assertEquals("2017-01-01T00:00:00.000Z", bucket2017.getKeyAsString()); |
| 457 | + assertEquals(1, bucket2017.getDocCount()); |
| 458 | + |
| 459 | + assertNotNull("Sub-aggregation should exist for 2017 bucket", bucket2017.getAggregations()); |
| 460 | + assertNotNull("Categories sub-agg should exist", bucket2017.getAggregations().get(categoryField)); |
| 461 | + assertTrue("Should have sub-aggregations", bucket2017.getAggregations().asList().size() > 0); |
| 462 | + } |
| 463 | + } |
| 464 | + } |
| 465 | + |
| 466 | + private static Settings getSettingsWithIndexSort() { |
| 467 | + return Settings.builder() |
| 468 | + .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT) |
| 469 | + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) |
| 470 | + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1) |
| 471 | + .putList("index.sort.field", AGGREGABLE_DATE) |
| 472 | + .build(); |
| 473 | + } |
| 474 | + |
352 | 475 | public void testNoDocsDeprecatedInterval() throws IOException { |
353 | 476 | Query query = new MatchNoDocsQuery(); |
354 | 477 | List<String> dates = Collections.emptyList(); |
|
0 commit comments