From c633293f1dab303425969269bc93b9cc52bef96c Mon Sep 17 00:00:00 2001
From: "slav.babanin"
Date: Mon, 5 Jun 2023 17:54:40 -0700
Subject: [PATCH 1/8] Improve Time-Series Bucketing Scalability
Introduce customizable bucketMaxSpanSeconds and bucketRoundingSeconds options for Time-Series collections, providing users with more control over bucketing behaviors. These options allow for setting the maximum time difference between timestamps within a bucket and adjusting the rounding precision. Enhancing flexibility in managing Time-Series data.
JAVA-4888
---
.../client/model/TimeSeriesOptions.java | 95 +++++++++++++++++++
.../operation/CreateCollectionOperation.java | 10 ++
.../timeseries-collection.json | 65 +++++++++++++
.../client/unified/UnifiedCrudHelper.java | 6 ++
4 files changed, 176 insertions(+)
diff --git a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
index 82e6356ab1e..d144d18ff29 100644
--- a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
@@ -18,6 +18,9 @@
import com.mongodb.lang.Nullable;
+import java.util.concurrent.TimeUnit;
+
+import static com.mongodb.assertions.Assertions.isTrueArgument;
import static com.mongodb.assertions.Assertions.notNull;
/**
@@ -31,6 +34,8 @@ public final class TimeSeriesOptions {
private final String timeField;
private String metaField;
private TimeSeriesGranularity granularity;
+ private Long bucketMaxSpanSeconds;
+ private Long bucketRoundingSeconds;
/**
* Construct a new instance.
@@ -104,12 +109,102 @@ public TimeSeriesOptions granularity(@Nullable final TimeSeriesGranularity granu
return this;
}
+ /**
+ * Returns the maximum time span between measurements in a bucket.
+ *
+ * @param timeUnit the time unit.
+ * @return time span between measurements.
+ * @mongodb.server.release 6.3
+ * @mongodb.driver.manual core/timeseries-collections/ Time-series collections
+ * @see #bucketMaxSpan(Long, TimeUnit)
+ * @since 4.10
+ */
+ @Nullable
+ public Long getBucketMaxSpan(final TimeUnit timeUnit) {
+ if (bucketMaxSpanSeconds == null) {
+ return null;
+ }
+ return timeUnit.convert(bucketMaxSpanSeconds, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Sets the maximum time span between measurements in a bucket.
+ *
+ * The value of {@code bucketMaxSpanSeconds} must be the same as {@code bucketRoundingSeconds}.
+ * If you set the bucketMaxSpanSeconds, parameter, you can't set the granularity parameter
+ *
+ *
+ * @param bucketMaxSpan - time span between measurements. After conversion to seconds using
+ * {@link TimeUnit#convert(long, java.util.concurrent.TimeUnit)}, the value must be >= 1.
+ * @param timeUnit - the time unit.
+ * @return this
+ * @mongodb.server.release 6.3
+ * @mongodb.driver.manual core/timeseries-collections/ Time-series collections
+ * @see #getBucketMaxSpan(TimeUnit)
+ * @since 4.10
+ */
+ public TimeSeriesOptions bucketMaxSpan(@Nullable final Long bucketMaxSpan, final TimeUnit timeUnit) {
+ if (bucketMaxSpan == null) {
+ this.bucketMaxSpanSeconds = null;
+ } else {
+ this.bucketMaxSpanSeconds = TimeUnit.SECONDS.convert(bucketMaxSpan, timeUnit);
+ isTrueArgument("bucketMaxSpan, after conversion to seconds, must be >= 1", bucketMaxSpanSeconds > 0);
+ }
+ return this;
+ }
+
+ /**
+ * Returns the time interval that determines the starting timestamp for a new bucket.
+ *
+ * @param timeUnit the time unit.
+ * @return the time interval.
+ * @mongodb.server.release 6.3
+ * @mongodb.driver.manual core/timeseries-collections/ Time-series collections
+ * @see #bucketRounding(Long, TimeUnit)
+ * @since 4.10
+ */
+ @Nullable
+ public Long getBucketRounding(final TimeUnit timeUnit) {
+ if (bucketRoundingSeconds == null) {
+ return null;
+ }
+ return timeUnit.convert(bucketRoundingSeconds, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Specifies the time interval that determines the starting timestamp for a new bucket.
+ *
+ * The value of {@code bucketRounding} must be the same as {@code bucketMaxSpan}. If you set the {@code bucketRounding}, parameter,
+ * you can't set the granularity parameter.
+ *
+ *
+ * @param bucketRounding - time interval. After conversion to seconds using
+ * {@link TimeUnit#convert(long, java.util.concurrent.TimeUnit)}, the value must be >= 1.
+ * @param timeUnit - the time unit.
+ * @return this
+ * @mongodb.server.release 6.3
+ * @mongodb.driver.manual core/timeseries-collections/ Time-series collections
+ * @see #getBucketRounding(TimeUnit)
+ * @since 4.10
+ */
+ public TimeSeriesOptions bucketRounding(@Nullable final Long bucketRounding, final TimeUnit timeUnit) {
+ if (bucketRounding == null) {
+ this.bucketRoundingSeconds = null;
+ } else {
+ this.bucketRoundingSeconds = TimeUnit.SECONDS.convert(bucketRounding, timeUnit);
+ isTrueArgument("bucketRounding, after conversion to seconds, must be >= 1", bucketMaxSpanSeconds > 0);
+ }
+ return this;
+ }
+
@Override
public String toString() {
return "TimeSeriesOptions{"
+ "timeField='" + timeField + '\''
+ ", metaField='" + metaField + '\''
+ ", granularity=" + granularity
+ + ", bucketMaxSpanSeconds=" + bucketMaxSpanSeconds
+ + ", bucketRoundingSeconds=" + bucketRoundingSeconds
+ '}';
}
}
diff --git a/driver-core/src/main/com/mongodb/internal/operation/CreateCollectionOperation.java b/driver-core/src/main/com/mongodb/internal/operation/CreateCollectionOperation.java
index ef2e1f706e9..feb296d6558 100644
--- a/driver-core/src/main/com/mongodb/internal/operation/CreateCollectionOperation.java
+++ b/driver-core/src/main/com/mongodb/internal/operation/CreateCollectionOperation.java
@@ -34,11 +34,13 @@
import org.bson.BsonArray;
import org.bson.BsonBoolean;
import org.bson.BsonDocument;
+import org.bson.BsonInt64;
import org.bson.BsonString;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import static com.mongodb.assertions.Assertions.notNull;
@@ -347,6 +349,14 @@ private BsonDocument getCreateCollectionCommand() {
if (granularity != null) {
timeSeriesDocument.put("granularity", new BsonString(getGranularityAsString(granularity)));
}
+ Long bucketMaxSpan = timeSeriesOptions.getBucketMaxSpan(TimeUnit.SECONDS);
+ if (bucketMaxSpan != null){
+ timeSeriesDocument.put("bucketMaxSpanSeconds", new BsonInt64(bucketMaxSpan));
+ }
+ Long bucketRounding = timeSeriesOptions.getBucketRounding(TimeUnit.SECONDS);
+ if (bucketRounding != null){
+ timeSeriesDocument.put("bucketRoundingSeconds", new BsonInt64(bucketRounding));
+ }
document.put("timeseries", timeSeriesDocument);
}
if (changeStreamPreAndPostImagesOptions != null) {
diff --git a/driver-core/src/test/resources/unified-test-format/collection-management/timeseries-collection.json b/driver-core/src/test/resources/unified-test-format/collection-management/timeseries-collection.json
index b5638fd36e9..8525056fd1a 100644
--- a/driver-core/src/test/resources/unified-test-format/collection-management/timeseries-collection.json
+++ b/driver-core/src/test/resources/unified-test-format/collection-management/timeseries-collection.json
@@ -250,6 +250,71 @@
]
}
]
+ },
+ {
+ "description": "createCollection with bucketing options",
+ "runOnRequirements": [
+ {
+ "minServerVersion": "7.0"
+ }
+ ],
+ "operations": [
+ {
+ "name": "dropCollection",
+ "object": "database0",
+ "arguments": {
+ "collection": "test"
+ }
+ },
+ {
+ "name": "createCollection",
+ "object": "database0",
+ "arguments": {
+ "collection": "test",
+ "timeseries": {
+ "timeField": "time",
+ "bucketMaxSpanSeconds": 3600,
+ "bucketRoundingSeconds": 3600
+ }
+ }
+ },
+ {
+ "name": "assertCollectionExists",
+ "object": "testRunner",
+ "arguments": {
+ "databaseName": "ts-tests",
+ "collectionName": "test"
+ }
+ }
+ ],
+ "expectEvents": [
+ {
+ "client": "client0",
+ "events": [
+ {
+ "commandStartedEvent": {
+ "command": {
+ "drop": "test"
+ },
+ "databaseName": "ts-tests"
+ }
+ },
+ {
+ "commandStartedEvent": {
+ "command": {
+ "create": "test",
+ "timeseries": {
+ "timeField": "time",
+ "bucketMaxSpanSeconds": 3600,
+ "bucketRoundingSeconds": 3600
+ }
+ },
+ "databaseName": "ts-tests"
+ }
+ }
+ ]
+ }
+ ]
}
]
}
diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java
index c162039c6b8..b99710eec7e 100644
--- a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java
+++ b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java
@@ -1202,6 +1202,12 @@ private TimeSeriesOptions createTimeSeriesOptions(final BsonDocument timeSeriesD
case "metaField":
options.metaField(cur.getValue().asString().getValue());
break;
+ case "bucketMaxSpanSeconds":
+ options.bucketMaxSpan(cur.getValue().asInt32().longValue(), TimeUnit.SECONDS);
+ break;
+ case "bucketRoundingSeconds":
+ options.bucketRounding(cur.getValue().asInt32().longValue(), TimeUnit.SECONDS);
+ break;
case "granularity":
options.granularity(createTimeSeriesGranularity(cur.getValue().asString().getValue()));
break;
From 91af5e2ac69429a5a4a4dacfb3305906f2e28762 Mon Sep 17 00:00:00 2001
From: "slav.babanin"
Date: Tue, 6 Jun 2023 13:33:39 -0700
Subject: [PATCH 2/8] Add expected class name to the tests.
JAVA-4888
---
.../mongodb/kotlin/client/coroutine/ExtensionMethodsTest.kt | 3 ++-
.../kotlin/com/mongodb/kotlin/client/ExtensionMethodsTest.kt | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/ExtensionMethodsTest.kt b/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/ExtensionMethodsTest.kt
index 39780fb8d6c..9243748f1af 100644
--- a/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/ExtensionMethodsTest.kt
+++ b/driver-kotlin-coroutine/src/test/kotlin/com/mongodb/kotlin/client/coroutine/ExtensionMethodsTest.kt
@@ -35,7 +35,8 @@ class ExtensionMethodsTest {
"FindOneAndReplaceOptions",
"FindOneAndUpdateOptions",
"IndexOptions",
- "TransactionOptions")
+ "TransactionOptions",
+ "TimeSeriesOptions")
ClassGraph().enableClassInfo().enableMethodInfo().acceptPackages("com.mongodb").scan().use { scanResult ->
val optionsClassesWithTimeUnit =
diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ExtensionMethodsTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ExtensionMethodsTest.kt
index ad8571e5696..f0e7698124b 100644
--- a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ExtensionMethodsTest.kt
+++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ExtensionMethodsTest.kt
@@ -35,7 +35,8 @@ class ExtensionMethodsTest {
"FindOneAndReplaceOptions",
"FindOneAndUpdateOptions",
"IndexOptions",
- "TransactionOptions")
+ "TransactionOptions",
+ "TimeSeriesOptions")
ClassGraph().enableClassInfo().enableMethodInfo().acceptPackages("com.mongodb").scan().use { scanResult ->
val optionsClassesWithTimeUnit =
From 132a840fced6b0ed2fb227310bc426ae3502c8ff Mon Sep 17 00:00:00 2001
From: "slav.babanin"
Date: Tue, 6 Jun 2023 13:51:09 -0700
Subject: [PATCH 3/8] Change javadoc.
JAVA-4888
---
.../client/model/TimeSeriesOptions.java | 26 +++++++++----------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
index d144d18ff29..aad59a76d41 100644
--- a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
@@ -114,10 +114,10 @@ public TimeSeriesOptions granularity(@Nullable final TimeSeriesGranularity granu
*
* @param timeUnit the time unit.
* @return time span between measurements.
+ * @since 4.10
* @mongodb.server.release 6.3
* @mongodb.driver.manual core/timeseries-collections/ Time-series collections
* @see #bucketMaxSpan(Long, TimeUnit)
- * @since 4.10
*/
@Nullable
public Long getBucketMaxSpan(final TimeUnit timeUnit) {
@@ -131,17 +131,17 @@ public Long getBucketMaxSpan(final TimeUnit timeUnit) {
* Sets the maximum time span between measurements in a bucket.
*
* The value of {@code bucketMaxSpanSeconds} must be the same as {@code bucketRoundingSeconds}.
- * If you set the bucketMaxSpanSeconds, parameter, you can't set the granularity parameter
+ * If you set the {@code bucketMaxSpanSeconds}, parameter, you can't set the granularity parameter.
*
*
- * @param bucketMaxSpan - time span between measurements. After conversion to seconds using
- * {@link TimeUnit#convert(long, java.util.concurrent.TimeUnit)}, the value must be >= 1.
- * @param timeUnit - the time unit.
+ * @param bucketMaxSpan time span between measurements. After conversion to seconds using {@link TimeUnit#convert(long, java.util.concurrent.TimeUnit)},
+ * the value must be >= 1.
+ * @param timeUnit the time unit.
* @return this
+ * @since 4.10
* @mongodb.server.release 6.3
* @mongodb.driver.manual core/timeseries-collections/ Time-series collections
* @see #getBucketMaxSpan(TimeUnit)
- * @since 4.10
*/
public TimeSeriesOptions bucketMaxSpan(@Nullable final Long bucketMaxSpan, final TimeUnit timeUnit) {
if (bucketMaxSpan == null) {
@@ -158,10 +158,10 @@ public TimeSeriesOptions bucketMaxSpan(@Nullable final Long bucketMaxSpan, final
*
* @param timeUnit the time unit.
* @return the time interval.
+ * @since 4.10
* @mongodb.server.release 6.3
* @mongodb.driver.manual core/timeseries-collections/ Time-series collections
* @see #bucketRounding(Long, TimeUnit)
- * @since 4.10
*/
@Nullable
public Long getBucketRounding(final TimeUnit timeUnit) {
@@ -174,18 +174,18 @@ public Long getBucketRounding(final TimeUnit timeUnit) {
/**
* Specifies the time interval that determines the starting timestamp for a new bucket.
*
- * The value of {@code bucketRounding} must be the same as {@code bucketMaxSpan}. If you set the {@code bucketRounding}, parameter,
- * you can't set the granularity parameter.
+ * The value of {@code bucketRoundingSeconds} must be the same as {@code bucketMaxSpanSeconds}.
+ * If you set the {@code bucketRoundingSeconds}, parameter, you can't set the granularity parameter.
*
*
- * @param bucketRounding - time interval. After conversion to seconds using
- * {@link TimeUnit#convert(long, java.util.concurrent.TimeUnit)}, the value must be >= 1.
- * @param timeUnit - the time unit.
+ * @param bucketRounding time interval. After conversion to seconds using {@link TimeUnit#convert(long, java.util.concurrent.TimeUnit)},
+ * the value must be >= 1.
+ * @param timeUnit the time unit.
* @return this
+ * @since 4.10
* @mongodb.server.release 6.3
* @mongodb.driver.manual core/timeseries-collections/ Time-series collections
* @see #getBucketRounding(TimeUnit)
- * @since 4.10
*/
public TimeSeriesOptions bucketRounding(@Nullable final Long bucketRounding, final TimeUnit timeUnit) {
if (bucketRounding == null) {
From 2a86297b38937b076d1bc38270cc4624b5e62a02 Mon Sep 17 00:00:00 2001
From: "slav.babanin"
Date: Wed, 7 Jun 2023 22:35:22 -0700
Subject: [PATCH 4/8] Update Java doc to clarify null return value.
JAVA-4888
---
.../main/com/mongodb/client/model/TimeSeriesOptions.java | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
index aad59a76d41..29e68f685b2 100644
--- a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
@@ -113,7 +113,7 @@ public TimeSeriesOptions granularity(@Nullable final TimeSeriesGranularity granu
* Returns the maximum time span between measurements in a bucket.
*
* @param timeUnit the time unit.
- * @return time span between measurements.
+ * @return time span between measurements, or {@code null} if not set.
* @since 4.10
* @mongodb.server.release 6.3
* @mongodb.driver.manual core/timeseries-collections/ Time-series collections
@@ -135,7 +135,7 @@ public Long getBucketMaxSpan(final TimeUnit timeUnit) {
*
*
* @param bucketMaxSpan time span between measurements. After conversion to seconds using {@link TimeUnit#convert(long, java.util.concurrent.TimeUnit)},
- * the value must be >= 1.
+ * the value must be >= 1. {@code null} can be provided to unset any previously set value.
* @param timeUnit the time unit.
* @return this
* @since 4.10
@@ -157,7 +157,7 @@ public TimeSeriesOptions bucketMaxSpan(@Nullable final Long bucketMaxSpan, final
* Returns the time interval that determines the starting timestamp for a new bucket.
*
* @param timeUnit the time unit.
- * @return the time interval.
+ * @return the time interval, or {@code null} if not set.
* @since 4.10
* @mongodb.server.release 6.3
* @mongodb.driver.manual core/timeseries-collections/ Time-series collections
@@ -179,7 +179,7 @@ public Long getBucketRounding(final TimeUnit timeUnit) {
*
*
* @param bucketRounding time interval. After conversion to seconds using {@link TimeUnit#convert(long, java.util.concurrent.TimeUnit)},
- * the value must be >= 1.
+ * the value must be >= 1. {@code null} can be provided to unset any previously set value.
* @param timeUnit the time unit.
* @return this
* @since 4.10
From d874af08e330bdcb427c96895eb2b28e069f37d9 Mon Sep 17 00:00:00 2001
From: "slav.babanin"
Date: Wed, 14 Jun 2023 11:53:10 -0700
Subject: [PATCH 5/8] - Change javadoc. - Add tests. - Lower server version
because bucket options were backported to 6.3
JAVA-4888
---
.../client/model/TimeSeriesGranularity.java | 2 +-
.../client/model/TimeSeriesOptions.java | 26 +++++---
.../client/model/TimeSeriesOptionsTest.java | 65 +++++++++++++++++++
.../timeseries-collection.json | 2 +-
4 files changed, 83 insertions(+), 12 deletions(-)
create mode 100644 driver-core/src/test/functional/com/mongodb/client/model/TimeSeriesOptionsTest.java
diff --git a/driver-core/src/main/com/mongodb/client/model/TimeSeriesGranularity.java b/driver-core/src/main/com/mongodb/client/model/TimeSeriesGranularity.java
index c15ba59b97a..266ef8489a0 100644
--- a/driver-core/src/main/com/mongodb/client/model/TimeSeriesGranularity.java
+++ b/driver-core/src/main/com/mongodb/client/model/TimeSeriesGranularity.java
@@ -29,7 +29,7 @@ public enum TimeSeriesGranularity {
/**
* Seconds-level granularity.
*
- * If granularity of a time-series collection is unspecified, this is the default value.
+ * This is the default value.
*
*/
SECONDS,
diff --git a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
index 29e68f685b2..43a4ec35040 100644
--- a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
@@ -20,6 +20,7 @@
import java.util.concurrent.TimeUnit;
+import static com.mongodb.assertions.Assertions.isTrue;
import static com.mongodb.assertions.Assertions.isTrueArgument;
import static com.mongodb.assertions.Assertions.notNull;
@@ -97,7 +98,8 @@ public TimeSeriesGranularity getGranularity() {
/**
* Sets the granularity of the time-series data.
*
- * The default value is {@link TimeSeriesGranularity#SECONDS}.
+ * The default value is {@link TimeSeriesGranularity#SECONDS} if neither {@code bucketMaxSpan} nor {@code bucketRounding} is set.
+ * If any of these bucketing options are set, the granularity parameter cannot be set.
*
*
* @param granularity the time-series granularity
@@ -105,6 +107,8 @@ public TimeSeriesGranularity getGranularity() {
* @see #getGranularity()
*/
public TimeSeriesOptions granularity(@Nullable final TimeSeriesGranularity granularity) {
+ isTrue("granularity is not allowed when bucketMaxSpan is set", bucketMaxSpanSeconds == null);
+ isTrue("granularity is not allowed when bucketRounding is set", bucketRoundingSeconds == null);
this.granularity = granularity;
return this;
}
@@ -116,7 +120,6 @@ public TimeSeriesOptions granularity(@Nullable final TimeSeriesGranularity granu
* @return time span between measurements, or {@code null} if not set.
* @since 4.10
* @mongodb.server.release 6.3
- * @mongodb.driver.manual core/timeseries-collections/ Time-series collections
* @see #bucketMaxSpan(Long, TimeUnit)
*/
@Nullable
@@ -130,8 +133,9 @@ public Long getBucketMaxSpan(final TimeUnit timeUnit) {
/**
* Sets the maximum time span between measurements in a bucket.
*
- * The value of {@code bucketMaxSpanSeconds} must be the same as {@code bucketRoundingSeconds}.
- * If you set the {@code bucketMaxSpanSeconds}, parameter, you can't set the granularity parameter.
+ * The value of {@code bucketMaxSpan} must be the same as {@code bucketRounding},
+ * which also means that the options must either be both set or both unset.
+ * If you set the {@code bucketMaxSpan}, parameter, you can't set the granularity parameter.
*
*
* @param bucketMaxSpan time span between measurements. After conversion to seconds using {@link TimeUnit#convert(long, java.util.concurrent.TimeUnit)},
@@ -140,13 +144,14 @@ public Long getBucketMaxSpan(final TimeUnit timeUnit) {
* @return this
* @since 4.10
* @mongodb.server.release 6.3
- * @mongodb.driver.manual core/timeseries-collections/ Time-series collections
* @see #getBucketMaxSpan(TimeUnit)
*/
public TimeSeriesOptions bucketMaxSpan(@Nullable final Long bucketMaxSpan, final TimeUnit timeUnit) {
+ notNull("timeUnit", timeUnit);
if (bucketMaxSpan == null) {
this.bucketMaxSpanSeconds = null;
} else {
+ isTrue("bucketMaxSpan is not allowed when granularity is set", granularity == null);
this.bucketMaxSpanSeconds = TimeUnit.SECONDS.convert(bucketMaxSpan, timeUnit);
isTrueArgument("bucketMaxSpan, after conversion to seconds, must be >= 1", bucketMaxSpanSeconds > 0);
}
@@ -160,7 +165,6 @@ public TimeSeriesOptions bucketMaxSpan(@Nullable final Long bucketMaxSpan, final
* @return the time interval, or {@code null} if not set.
* @since 4.10
* @mongodb.server.release 6.3
- * @mongodb.driver.manual core/timeseries-collections/ Time-series collections
* @see #bucketRounding(Long, TimeUnit)
*/
@Nullable
@@ -174,8 +178,9 @@ public Long getBucketRounding(final TimeUnit timeUnit) {
/**
* Specifies the time interval that determines the starting timestamp for a new bucket.
*
- * The value of {@code bucketRoundingSeconds} must be the same as {@code bucketMaxSpanSeconds}.
- * If you set the {@code bucketRoundingSeconds}, parameter, you can't set the granularity parameter.
+ * The value of {@code bucketRounding} must be the same as {@code bucketMaxSpan},
+ * which also means that the options must either be both set or both unset.
+ * If you set the {@code bucketRounding}, parameter, you can't set the granularity parameter.
*
*
* @param bucketRounding time interval. After conversion to seconds using {@link TimeUnit#convert(long, java.util.concurrent.TimeUnit)},
@@ -184,15 +189,16 @@ public Long getBucketRounding(final TimeUnit timeUnit) {
* @return this
* @since 4.10
* @mongodb.server.release 6.3
- * @mongodb.driver.manual core/timeseries-collections/ Time-series collections
* @see #getBucketRounding(TimeUnit)
*/
public TimeSeriesOptions bucketRounding(@Nullable final Long bucketRounding, final TimeUnit timeUnit) {
+ notNull("timeUnit", timeUnit);
if (bucketRounding == null) {
this.bucketRoundingSeconds = null;
} else {
+ isTrue("bucketRounding is not allowed when granularity is set", granularity == null);
this.bucketRoundingSeconds = TimeUnit.SECONDS.convert(bucketRounding, timeUnit);
- isTrueArgument("bucketRounding, after conversion to seconds, must be >= 1", bucketMaxSpanSeconds > 0);
+ isTrueArgument("bucketRounding, after conversion to seconds, must be >= 1", bucketRoundingSeconds > 0);
}
return this;
}
diff --git a/driver-core/src/test/functional/com/mongodb/client/model/TimeSeriesOptionsTest.java b/driver-core/src/test/functional/com/mongodb/client/model/TimeSeriesOptionsTest.java
new file mode 100644
index 00000000000..a4f4f23a9f1
--- /dev/null
+++ b/driver-core/src/test/functional/com/mongodb/client/model/TimeSeriesOptionsTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.mongodb.client.model;
+
+import com.mongodb.lang.Nullable;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+class TimeSeriesOptionsTest {
+
+ private TimeSeriesOptions timeSeriesOptions;
+
+ @BeforeEach
+ void setUp() {
+ timeSeriesOptions = new TimeSeriesOptions("test");
+ }
+
+ @Test
+ void shouldThrowErrorWhenGranularityIsAlreadySet() {
+ //given
+ timeSeriesOptions.granularity(TimeSeriesGranularity.SECONDS);
+
+ //when & then
+ assertThrows(IllegalStateException.class, () -> timeSeriesOptions.bucketRounding(1L, TimeUnit.SECONDS));
+ assertThrows(IllegalStateException.class, () -> timeSeriesOptions.bucketMaxSpan(1L, TimeUnit.SECONDS));
+ }
+
+ @ParameterizedTest
+ @MethodSource("args")
+ void shouldThrowErrorWhenInvalidArgumentProvided(@Nullable final Long valueToSet, @Nullable final TimeUnit timeUnit) {
+ assertThrows(IllegalArgumentException.class, () -> timeSeriesOptions.bucketRounding(valueToSet, timeUnit));
+ assertThrows(IllegalArgumentException.class, () -> timeSeriesOptions.bucketMaxSpan(valueToSet, timeUnit));
+ }
+
+ private static Stream args() {
+ return Stream.of(
+ arguments(1L, null),
+ arguments(null, null),
+ arguments(1L, TimeUnit.MILLISECONDS)
+ );
+ }
+}
diff --git a/driver-core/src/test/resources/unified-test-format/collection-management/timeseries-collection.json b/driver-core/src/test/resources/unified-test-format/collection-management/timeseries-collection.json
index 8525056fd1a..2ee52eac411 100644
--- a/driver-core/src/test/resources/unified-test-format/collection-management/timeseries-collection.json
+++ b/driver-core/src/test/resources/unified-test-format/collection-management/timeseries-collection.json
@@ -255,7 +255,7 @@
"description": "createCollection with bucketing options",
"runOnRequirements": [
{
- "minServerVersion": "7.0"
+ "minServerVersion": "6.3"
}
],
"operations": [
From 85c85d2e946136cbacdf29f96e2a1ea6cf9e53e1 Mon Sep 17 00:00:00 2001
From: "slav.babanin"
Date: Wed, 14 Jun 2023 12:12:44 -0700
Subject: [PATCH 6/8] Add null checks.
JAVA-4888
---
.../main/com/mongodb/client/model/TimeSeriesOptions.java | 7 +++++--
.../com/mongodb/client/model/TimeSeriesOptionsTest.java | 6 ++++++
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
index 43a4ec35040..6d55789d2ef 100644
--- a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
@@ -32,6 +32,7 @@
* @mongodb.driver.manual core/timeseries-collections/ Time-series collections
*/
public final class TimeSeriesOptions {
+ private static final String PARAMETER_TIME_UNIT = "timeUnit";
private final String timeField;
private String metaField;
private TimeSeriesGranularity granularity;
@@ -124,6 +125,7 @@ public TimeSeriesOptions granularity(@Nullable final TimeSeriesGranularity granu
*/
@Nullable
public Long getBucketMaxSpan(final TimeUnit timeUnit) {
+ notNull(PARAMETER_TIME_UNIT, timeUnit);
if (bucketMaxSpanSeconds == null) {
return null;
}
@@ -147,7 +149,7 @@ public Long getBucketMaxSpan(final TimeUnit timeUnit) {
* @see #getBucketMaxSpan(TimeUnit)
*/
public TimeSeriesOptions bucketMaxSpan(@Nullable final Long bucketMaxSpan, final TimeUnit timeUnit) {
- notNull("timeUnit", timeUnit);
+ notNull(PARAMETER_TIME_UNIT, timeUnit);
if (bucketMaxSpan == null) {
this.bucketMaxSpanSeconds = null;
} else {
@@ -169,6 +171,7 @@ public TimeSeriesOptions bucketMaxSpan(@Nullable final Long bucketMaxSpan, final
*/
@Nullable
public Long getBucketRounding(final TimeUnit timeUnit) {
+ notNull(PARAMETER_TIME_UNIT, timeUnit);
if (bucketRoundingSeconds == null) {
return null;
}
@@ -192,7 +195,7 @@ public Long getBucketRounding(final TimeUnit timeUnit) {
* @see #getBucketRounding(TimeUnit)
*/
public TimeSeriesOptions bucketRounding(@Nullable final Long bucketRounding, final TimeUnit timeUnit) {
- notNull("timeUnit", timeUnit);
+ notNull(PARAMETER_TIME_UNIT, timeUnit);
if (bucketRounding == null) {
this.bucketRoundingSeconds = null;
} else {
diff --git a/driver-core/src/test/functional/com/mongodb/client/model/TimeSeriesOptionsTest.java b/driver-core/src/test/functional/com/mongodb/client/model/TimeSeriesOptionsTest.java
index a4f4f23a9f1..5fed979ae5d 100644
--- a/driver-core/src/test/functional/com/mongodb/client/model/TimeSeriesOptionsTest.java
+++ b/driver-core/src/test/functional/com/mongodb/client/model/TimeSeriesOptionsTest.java
@@ -48,6 +48,12 @@ void shouldThrowErrorWhenGranularityIsAlreadySet() {
assertThrows(IllegalStateException.class, () -> timeSeriesOptions.bucketMaxSpan(1L, TimeUnit.SECONDS));
}
+ @Test
+ void shouldThrowErrorWhenGetWithNullParameter() {
+ assertThrows(IllegalArgumentException.class, () -> timeSeriesOptions.getBucketMaxSpan(null));
+ assertThrows(IllegalArgumentException.class, () -> timeSeriesOptions.getBucketRounding(null));
+ }
+
@ParameterizedTest
@MethodSource("args")
void shouldThrowErrorWhenInvalidArgumentProvided(@Nullable final Long valueToSet, @Nullable final TimeUnit timeUnit) {
From 48f2189fe6af9ee53dfcd7aad3e7e6a58af00075 Mon Sep 17 00:00:00 2001
From: "slav.babanin"
Date: Wed, 14 Jun 2023 14:51:02 -0700
Subject: [PATCH 7/8] Fix unnecessary assignment to variable.
JAVA-4888
---
.../com/mongodb/client/model/TimeSeriesOptions.java | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
index 6d55789d2ef..93ecf895b10 100644
--- a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
@@ -154,8 +154,9 @@ public TimeSeriesOptions bucketMaxSpan(@Nullable final Long bucketMaxSpan, final
this.bucketMaxSpanSeconds = null;
} else {
isTrue("bucketMaxSpan is not allowed when granularity is set", granularity == null);
- this.bucketMaxSpanSeconds = TimeUnit.SECONDS.convert(bucketMaxSpan, timeUnit);
- isTrueArgument("bucketMaxSpan, after conversion to seconds, must be >= 1", bucketMaxSpanSeconds > 0);
+ long seconds = TimeUnit.SECONDS.convert(bucketMaxSpan, timeUnit);
+ isTrueArgument("bucketMaxSpan, after conversion to seconds, must be >= 1", seconds > 0);
+ this.bucketMaxSpanSeconds = seconds;
}
return this;
}
@@ -200,8 +201,9 @@ public TimeSeriesOptions bucketRounding(@Nullable final Long bucketRounding, fin
this.bucketRoundingSeconds = null;
} else {
isTrue("bucketRounding is not allowed when granularity is set", granularity == null);
- this.bucketRoundingSeconds = TimeUnit.SECONDS.convert(bucketRounding, timeUnit);
- isTrueArgument("bucketRounding, after conversion to seconds, must be >= 1", bucketRoundingSeconds > 0);
+ long seconds = TimeUnit.SECONDS.convert(bucketRounding, timeUnit);
+ isTrueArgument("bucketRounding, after conversion to seconds, must be >= 1", seconds > 0);
+ this.bucketRoundingSeconds = seconds;
}
return this;
}
From 698f599fc28e64a226afe38a60b1a1eecac62286 Mon Sep 17 00:00:00 2001
From: "slav.babanin"
Date: Wed, 14 Jun 2023 18:13:21 -0700
Subject: [PATCH 8/8] Correct javadoc.
JAVA-4888
---
.../client/model/TimeSeriesOptions.java | 23 ++++++++-----------
.../client/model/TimeSeriesOptionsTest.java | 19 ++++++++++-----
2 files changed, 23 insertions(+), 19 deletions(-)
diff --git a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
index 93ecf895b10..6844e13848a 100644
--- a/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/TimeSeriesOptions.java
@@ -32,7 +32,6 @@
* @mongodb.driver.manual core/timeseries-collections/ Time-series collections
*/
public final class TimeSeriesOptions {
- private static final String PARAMETER_TIME_UNIT = "timeUnit";
private final String timeField;
private String metaField;
private TimeSeriesGranularity granularity;
@@ -99,8 +98,8 @@ public TimeSeriesGranularity getGranularity() {
/**
* Sets the granularity of the time-series data.
*
- * The default value is {@link TimeSeriesGranularity#SECONDS} if neither {@code bucketMaxSpan} nor {@code bucketRounding} is set.
- * If any of these bucketing options are set, the granularity parameter cannot be set.
+ * The default value is {@link TimeSeriesGranularity#SECONDS} if neither {@link #bucketMaxSpan(Long, TimeUnit)} nor
+ * {@link #bucketRounding(Long, TimeUnit)} is set. If any of these bucketing options are set, the granularity parameter cannot be set.
*
*
* @param granularity the time-series granularity
@@ -125,7 +124,7 @@ public TimeSeriesOptions granularity(@Nullable final TimeSeriesGranularity granu
*/
@Nullable
public Long getBucketMaxSpan(final TimeUnit timeUnit) {
- notNull(PARAMETER_TIME_UNIT, timeUnit);
+ notNull("timeUnit", timeUnit);
if (bucketMaxSpanSeconds == null) {
return null;
}
@@ -135,9 +134,8 @@ public Long getBucketMaxSpan(final TimeUnit timeUnit) {
/**
* Sets the maximum time span between measurements in a bucket.
*
- * The value of {@code bucketMaxSpan} must be the same as {@code bucketRounding},
- * which also means that the options must either be both set or both unset.
- * If you set the {@code bucketMaxSpan}, parameter, you can't set the granularity parameter.
+ * The value of {@code bucketMaxSpan} must be the same as {@link #bucketRounding(Long, TimeUnit)}, which also means that the options
+ * must either be both set or both unset. If you set the {@code bucketMaxSpan} parameter, you can't set the granularity parameter.
*
*
* @param bucketMaxSpan time span between measurements. After conversion to seconds using {@link TimeUnit#convert(long, java.util.concurrent.TimeUnit)},
@@ -149,7 +147,7 @@ public Long getBucketMaxSpan(final TimeUnit timeUnit) {
* @see #getBucketMaxSpan(TimeUnit)
*/
public TimeSeriesOptions bucketMaxSpan(@Nullable final Long bucketMaxSpan, final TimeUnit timeUnit) {
- notNull(PARAMETER_TIME_UNIT, timeUnit);
+ notNull("timeUnit", timeUnit);
if (bucketMaxSpan == null) {
this.bucketMaxSpanSeconds = null;
} else {
@@ -172,7 +170,7 @@ public TimeSeriesOptions bucketMaxSpan(@Nullable final Long bucketMaxSpan, final
*/
@Nullable
public Long getBucketRounding(final TimeUnit timeUnit) {
- notNull(PARAMETER_TIME_UNIT, timeUnit);
+ notNull("timeUnit", timeUnit);
if (bucketRoundingSeconds == null) {
return null;
}
@@ -182,9 +180,8 @@ public Long getBucketRounding(final TimeUnit timeUnit) {
/**
* Specifies the time interval that determines the starting timestamp for a new bucket.
*
- * The value of {@code bucketRounding} must be the same as {@code bucketMaxSpan},
- * which also means that the options must either be both set or both unset.
- * If you set the {@code bucketRounding}, parameter, you can't set the granularity parameter.
+ * The value of {@code bucketRounding} must be the same as {@link #bucketMaxSpan(Long, TimeUnit)}, which also means that the options
+ * must either be both set or both unset. If you set the {@code bucketRounding} parameter, you can't set the granularity parameter.
*
*
* @param bucketRounding time interval. After conversion to seconds using {@link TimeUnit#convert(long, java.util.concurrent.TimeUnit)},
@@ -196,7 +193,7 @@ public Long getBucketRounding(final TimeUnit timeUnit) {
* @see #getBucketRounding(TimeUnit)
*/
public TimeSeriesOptions bucketRounding(@Nullable final Long bucketRounding, final TimeUnit timeUnit) {
- notNull(PARAMETER_TIME_UNIT, timeUnit);
+ notNull("timeUnit", timeUnit);
if (bucketRounding == null) {
this.bucketRoundingSeconds = null;
} else {
diff --git a/driver-core/src/test/functional/com/mongodb/client/model/TimeSeriesOptionsTest.java b/driver-core/src/test/functional/com/mongodb/client/model/TimeSeriesOptionsTest.java
index 5fed979ae5d..4f38da79a3d 100644
--- a/driver-core/src/test/functional/com/mongodb/client/model/TimeSeriesOptionsTest.java
+++ b/driver-core/src/test/functional/com/mongodb/client/model/TimeSeriesOptionsTest.java
@@ -26,6 +26,7 @@
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
+import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.params.provider.Arguments.arguments;
@@ -44,21 +45,27 @@ void shouldThrowErrorWhenGranularityIsAlreadySet() {
timeSeriesOptions.granularity(TimeSeriesGranularity.SECONDS);
//when & then
- assertThrows(IllegalStateException.class, () -> timeSeriesOptions.bucketRounding(1L, TimeUnit.SECONDS));
- assertThrows(IllegalStateException.class, () -> timeSeriesOptions.bucketMaxSpan(1L, TimeUnit.SECONDS));
+ assertAll(
+ () -> assertThrows(IllegalStateException.class, () -> timeSeriesOptions.bucketRounding(1L, TimeUnit.SECONDS)),
+ () -> assertThrows(IllegalStateException.class, () -> timeSeriesOptions.bucketMaxSpan(1L, TimeUnit.SECONDS))
+ );
}
@Test
void shouldThrowErrorWhenGetWithNullParameter() {
- assertThrows(IllegalArgumentException.class, () -> timeSeriesOptions.getBucketMaxSpan(null));
- assertThrows(IllegalArgumentException.class, () -> timeSeriesOptions.getBucketRounding(null));
+ assertAll(
+ () -> assertThrows(IllegalArgumentException.class, () -> timeSeriesOptions.getBucketMaxSpan(null)),
+ () -> assertThrows(IllegalArgumentException.class, () -> timeSeriesOptions.getBucketRounding(null))
+ );
}
@ParameterizedTest
@MethodSource("args")
void shouldThrowErrorWhenInvalidArgumentProvided(@Nullable final Long valueToSet, @Nullable final TimeUnit timeUnit) {
- assertThrows(IllegalArgumentException.class, () -> timeSeriesOptions.bucketRounding(valueToSet, timeUnit));
- assertThrows(IllegalArgumentException.class, () -> timeSeriesOptions.bucketMaxSpan(valueToSet, timeUnit));
+ assertAll(
+ () -> assertThrows(IllegalArgumentException.class, () -> timeSeriesOptions.bucketRounding(valueToSet, timeUnit)),
+ () -> assertThrows(IllegalArgumentException.class, () -> timeSeriesOptions.bucketMaxSpan(valueToSet, timeUnit))
+ );
}
private static Stream args() {