Polishing.

Rename Granularities/Granularity to Granularity and GranularityDefinition to proivide a more natural wording towards using predefined granularities.

Validate presence of referenced properties through the TimeSeries annotation.

Tweak Javadoc, reformat code, add unit tests.

See #3731
Original pull request: #3732.
This commit is contained in:
Mark Paluch
2021-07-16 09:41:16 +02:00
parent bacbd7133e
commit f00991dc29
11 changed files with 198 additions and 112 deletions

View File

@@ -20,8 +20,8 @@ import java.util.Optional;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
import org.springframework.data.mongodb.core.timeseries.Granularities;
import org.springframework.data.mongodb.core.timeseries.Granularity;
import org.springframework.data.mongodb.core.timeseries.GranularityDefinition;
import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.data.util.Optionals;
import org.springframework.lang.Nullable;
@@ -100,7 +100,7 @@ public class CollectionOptions {
/**
* Quick way to set up {@link CollectionOptions} for a Time Series collection. For more advanced settings use
* {@link #timeSeries(TimeSeriesOptions)}.
*
*
* @param timeField The name of the property which contains the date in each time series document. Must not be
* {@literal null}.
* @return new instance of {@link CollectionOptions}.
@@ -454,12 +454,13 @@ public class CollectionOptions {
private final String timeField;
@Nullable //
private String metaField;
private @Nullable final String metaField;
private Granularity granularity;
private final GranularityDefinition granularity;
private TimeSeriesOptions(String timeField, @Nullable String metaField, Granularity granularity) {
private TimeSeriesOptions(String timeField, @Nullable String metaField, GranularityDefinition granularity) {
Assert.hasText(timeField, "Time field must not be empty or null!");
this.timeField = timeField;
this.metaField = metaField;
@@ -475,7 +476,7 @@ public class CollectionOptions {
* @return new instance of {@link TimeSeriesOptions}.
*/
public static TimeSeriesOptions timeSeries(String timeField) {
return new TimeSeriesOptions(timeField, null, Granularities.DEFAULT);
return new TimeSeriesOptions(timeField, null, Granularity.DEFAULT);
}
/**
@@ -492,12 +493,13 @@ public class CollectionOptions {
}
/**
* Select the {@link Granularity} parameter to define how data in the time series collection is organized. Select
* one that is closest to the time span between incoming measurements.
* Select the {@link GranularityDefinition} parameter to define how data in the time series collection is organized.
* Select one that is closest to the time span between incoming measurements.
*
* @return new instance of {@link TimeSeriesOptions}.
* @see Granularity
*/
public TimeSeriesOptions granularity(Granularity granularity) {
public TimeSeriesOptions granularity(GranularityDefinition granularity) {
return new TimeSeriesOptions(timeField, metaField, granularity);
}
@@ -520,7 +522,7 @@ public class CollectionOptions {
/**
* @return never {@literal null}.
*/
public Granularity getGranularity() {
public GranularityDefinition getGranularity() {
return granularity;
}
}

View File

@@ -38,7 +38,7 @@ import org.springframework.data.mongodb.core.mapping.TimeSeries;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.timeseries.Granularities;
import org.springframework.data.mongodb.core.timeseries.Granularity;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -898,11 +898,23 @@ class EntityOperations {
if (entity.isAnnotationPresent(TimeSeries.class)) {
TimeSeries timeSeries = entity.getRequiredAnnotation(TimeSeries.class);
if (entity.getPersistentProperty(timeSeries.timeField()) == null) {
throw new MappingException(String.format("Time series field '%s' does not exist in type %s",
timeSeries.timeField(), entity.getName()));
}
TimeSeriesOptions options = TimeSeriesOptions.timeSeries(timeSeries.timeField());
if (StringUtils.hasText(timeSeries.metaField())) {
if (entity.getPersistentProperty(timeSeries.metaField()) == null) {
throw new MappingException(
String.format("Meta field '%s' does not exist in type %s", timeSeries.metaField(), entity.getName()));
}
options = options.metaField(timeSeries.metaField());
}
if (!Granularities.DEFAULT.equals(timeSeries.granularity())) {
if (!Granularity.DEFAULT.equals(timeSeries.granularity())) {
options = options.granularity(timeSeries.granularity());
}
collectionOptions = collectionOptions.timeSeries(options);

View File

@@ -99,7 +99,7 @@ import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
import org.springframework.data.mongodb.core.timeseries.Granularities;
import org.springframework.data.mongodb.core.timeseries.Granularity;
import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
@@ -2436,14 +2436,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
co.validationOptions(options);
}
if(collectionOptions.containsKey("timeseries")) {
if (collectionOptions.containsKey("timeseries")) {
Document timeSeries = collectionOptions.get("timeseries", Document.class);
com.mongodb.client.model.TimeSeriesOptions options = new com.mongodb.client.model.TimeSeriesOptions(timeSeries.getString("timeField"));
if(timeSeries.containsKey("metaField")) {
com.mongodb.client.model.TimeSeriesOptions options = new com.mongodb.client.model.TimeSeriesOptions(
timeSeries.getString("timeField"));
if (timeSeries.containsKey("metaField")) {
options.metaField(timeSeries.getString("metaField"));
}
if(timeSeries.containsKey("granularity")) {
if (timeSeries.containsKey("granularity")) {
options.granularity(TimeSeriesGranularity.valueOf(timeSeries.getString("granularity").toUpperCase()));
}
co.timeSeriesOptions(options);
@@ -2604,17 +2605,18 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
collectionOptions.getValidationOptions().ifPresent(it -> it.getValidator() //
.ifPresent(val -> doc.put("validator", getMappedValidator(val, targetType))));
collectionOptions.getTimeSeriesOptions().map(operations.forType(targetType)::mapTimeSeriesOptions).ifPresent(it -> {
collectionOptions.getTimeSeriesOptions().map(operations.forType(targetType)::mapTimeSeriesOptions)
.ifPresent(it -> {
Document timeseries = new Document("timeField", it.getTimeField());
if(StringUtils.hasText(it.getMetaField())) {
timeseries.append("metaField", it.getMetaField());
}
if(!Granularities.DEFAULT.equals(it.getGranularity())) {
timeseries.append("granularity", it.getGranularity().name().toLowerCase());
}
doc.put("timeseries", timeseries);
});
Document timeseries = new Document("timeField", it.getTimeField());
if (StringUtils.hasText(it.getMetaField())) {
timeseries.append("metaField", it.getMetaField());
}
if (!Granularity.DEFAULT.equals(it.getGranularity())) {
timeseries.append("granularity", it.getGranularity().name().toLowerCase());
}
doc.put("timeseries", timeseries);
});
}
return doc;
@@ -2849,9 +2851,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
.initiateFind(getAndPrepareCollection(doGetDatabase(), collectionName), collectionCallback::doInCollection)
.iterator()) {
while (cursor.hasNext()) {
callbackHandler.processDocument(cursor.next());
}
while (cursor.hasNext()) {
callbackHandler.processDocument(cursor.next());
}
} catch (RuntimeException e) {
throw potentiallyConvertRuntimeException(e, exceptionTranslator);
}
@@ -3175,17 +3177,17 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
public T doWith(Document document) {
maybeEmitEvent(new AfterLoadEvent<>(document, type, collectionName));
T entity = reader.read(type, document);
maybeEmitEvent(new AfterLoadEvent<>(document, type, collectionName));
T entity = reader.read(type, document);
if (entity == null) {
throw new MappingException(String.format("EntityReader %s returned null", reader));
}
if (entity == null) {
throw new MappingException(String.format("EntityReader %s returned null", reader));
}
maybeEmitEvent(new AfterConvertEvent<>(document, entity, collectionName));
entity = maybeCallAfterConvert(entity, document, collectionName);
maybeEmitEvent(new AfterConvertEvent<>(document, entity, collectionName));
entity = maybeCallAfterConvert(entity, document, collectionName);
return entity;
return entity;
}
}
@@ -3237,8 +3239,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Object result = targetType.isInterface() ? projectionFactory.createProjection(targetType, entity) : entity;
maybeEmitEvent(new AfterConvertEvent<>(document, result, collectionName));
return (T) maybeCallAfterConvert(result, document, collectionName);
maybeEmitEvent(new AfterConvertEvent<>(document, result, collectionName));
return (T) maybeCallAfterConvert(result, document, collectionName);
}
}

View File

@@ -17,7 +17,6 @@ package org.springframework.data.mongodb.core;
import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
import org.springframework.data.mongodb.core.timeseries.Granularities;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
@@ -111,6 +110,7 @@ import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
import org.springframework.data.mongodb.core.timeseries.Granularity;
import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
@@ -975,7 +975,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
return doAggregate(aggregation, collectionName, null, outputType);
}
protected <O> Flux<O> doAggregate(Aggregation aggregation, String collectionName, @Nullable Class<?> inputType, Class<O> outputType) {
protected <O> Flux<O> doAggregate(Aggregation aggregation, String collectionName, @Nullable Class<?> inputType,
Class<O> outputType) {
Assert.notNull(aggregation, "Aggregation pipeline must not be null!");
Assert.hasText(collectionName, "Collection name must not be null or empty!");
@@ -987,19 +988,18 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
AggregationDefinition ctx = queryOperations.createAggregation(aggregation, inputType);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Streaming aggregation: {} in collection {}", serializeToJsonSafely(ctx.getAggregationPipeline()), collectionName);
LOGGER.debug("Streaming aggregation: {} in collection {}", serializeToJsonSafely(ctx.getAggregationPipeline()),
collectionName);
}
ReadDocumentCallback<O> readCallback = new ReadDocumentCallback<>(mongoConverter, outputType, collectionName);
return execute(collectionName,
collection -> aggregateAndMap(collection, ctx.getAggregationPipeline(), ctx.isOutOrMerge(), options,
readCallback,
ctx.getInputType()));
return execute(collectionName, collection -> aggregateAndMap(collection, ctx.getAggregationPipeline(),
ctx.isOutOrMerge(), options, readCallback, ctx.getInputType()));
}
private <O> Flux<O> aggregateAndMap(MongoCollection<Document> collection, List<Document> pipeline,
boolean isOutOrMerge,
AggregationOptions options, ReadDocumentCallback<O> readCallback, @Nullable Class<?> inputType) {
boolean isOutOrMerge, AggregationOptions options, ReadDocumentCallback<O> readCallback,
@Nullable Class<?> inputType) {
AggregatePublisher<Document> cursor = collection.aggregate(pipeline, Document.class)
.allowDiskUse(options.isAllowDiskUse());
@@ -2510,10 +2510,10 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
TimeSeriesOptions options = new TimeSeriesOptions(it.getTimeField());
if(StringUtils.hasText(it.getMetaField())) {
if (StringUtils.hasText(it.getMetaField())) {
options.metaField(it.getMetaField());
}
if(!Granularities.DEFAULT.equals(it.getGranularity())) {
if (!Granularity.DEFAULT.equals(it.getGranularity())) {
options.granularity(TimeSeriesGranularity.valueOf(it.getGranularity().name().toUpperCase()));
}

View File

@@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.data.mongodb.core.timeseries.Granularities;
import org.springframework.data.mongodb.core.timeseries.Granularity;
/**
* Identifies a domain object to be persisted to a MongoDB Time Series collection.
@@ -50,8 +50,9 @@ public @interface TimeSeries {
String collection() default "";
/**
* The name of the property which contains the date in each time series document. <br />
* {@link Field#name() Annotated fieldnames} will be considered during the mapping process.
* Name of the property which contains the date in each time series document. <br />
* Translation of property names to {@link Field#name() annotated fieldnames} will be considered during the mapping
* process.
*
* @return never {@literal null}.
*/
@@ -60,19 +61,19 @@ public @interface TimeSeries {
/**
* The name of the field which contains metadata in each time series document. Should not be the {@literal id} nor
* {@link #timeField()} nor point to an {@literal array} or {@link java.util.Collection}. <br />
* {@link Field#name() Annotated fieldnames} will be considered during the mapping process.
* Translation of property names to {@link Field#name() annotated fieldnames} will be considered during the mapping
* process.
*
* @return empty {@link String} by default.
*/
String metaField() default "";
/**
* Select the {@link Granularities granularity} parameter to define how data in the time series collection is
* organized.
* Select the {@link Granularity granularity} parameter to define how data in the time series collection is organized.
*
* @return {@link Granularities#DEFAULT server default} by default.
* @return {@link Granularity#DEFAULT server default} by default.
*/
Granularities granularity() default Granularities.DEFAULT;
Granularity granularity() default Granularity.DEFAULT;
/**
* Defines the collation to apply when executing a query or creating indexes.

View File

@@ -16,12 +16,30 @@
package org.springframework.data.mongodb.core.timeseries;
/**
* The Granularity of time series data that is closest to the time span between incoming measurements.
* {@link GranularityDefinition Granularities} available for Time Series data.
*
* @author Christoph Strobl
* @since 3.3
*/
public interface Granularity {
public enum Granularity implements GranularityDefinition {
String name();
/**
* Server default value to indicate no explicit value should be sent.
*/
DEFAULT,
/**
* High frequency ingestion.
*/
SECONDS,
/**
* Medium frequency ingestion.
*/
MINUTES,
/**
* Low frequency ingestion.
*/
HOURS
}

View File

@@ -16,30 +16,12 @@
package org.springframework.data.mongodb.core.timeseries;
/**
* {@link Granularity Granularities} available for Time Series data.
* The Granularity of time series data that is closest to the time span between incoming measurements.
*
* @author Christoph Strobl
* @since 3.3
*/
public enum Granularities implements Granularity {
public interface GranularityDefinition {
/**
* Server default value to indicate no explicit value should be sent.
*/
DEFAULT,
/**
* High frequency ingestion.
*/
SECONDS,
/**
* Medium frequency ingestion.
*/
MINUTES,
/**
* Low frequency ingestion.
*/
HOURS
String name();
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2021 the original author or authors.
*
* 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
*
* https://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 org.springframework.data.mongodb.core;
import static org.assertj.core.api.Assertions.*;
import java.time.Instant;
import org.junit.jupiter.api.Test;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.TimeSeries;
/**
* Unit tests for {@link EntityOperations}.
*
* @author Mark Paluch
*/
class EntityOperationsUnitTests {
EntityOperations operations = new EntityOperations(new MongoMappingContext());
@Test // GH-3731
void shouldReportInvalidTimeField() {
assertThatExceptionOfType(MappingException.class)
.isThrownBy(() -> operations.forType(InvalidTimeField.class).getCollectionOptions())
.withMessageContaining("Time series field 'foo' does not exist");
}
@Test // GH-3731
void shouldReportInvalidMetaField() {
assertThatExceptionOfType(MappingException.class)
.isThrownBy(() -> operations.forType(InvalidMetaField.class).getCollectionOptions())
.withMessageContaining("Meta field 'foo' does not exist");
}
@TimeSeries(timeField = "foo")
static class InvalidTimeField {
}
@TimeSeries(timeField = "time", metaField = "foo")
static class InvalidMetaField {
Instant time;
}
}

View File

@@ -19,7 +19,6 @@ import static org.mockito.Mockito.*;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.test.util.Assertions.*;
import com.mongodb.client.model.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -101,7 +100,7 @@ import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.timeseries.Granularities;
import org.springframework.data.mongodb.core.timeseries.Granularity;
import org.springframework.lang.Nullable;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.CollectionUtils;
@@ -121,6 +120,16 @@ import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.CountOptions;
import com.mongodb.client.model.CreateCollectionOptions;
import com.mongodb.client.model.DeleteOptions;
import com.mongodb.client.model.FindOneAndDeleteOptions;
import com.mongodb.client.model.FindOneAndReplaceOptions;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.MapReduceAction;
import com.mongodb.client.model.ReplaceOptions;
import com.mongodb.client.model.TimeSeriesGranularity;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
@@ -1982,7 +1991,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
ArgumentCaptor<Bson> filter = ArgumentCaptor.forClass(Bson.class);
verify(collection).replaceOne(filter.capture(), any(), any());
assertThat(filter.getValue()).isEqualTo(new Document("_id", "id-1").append("value", "v1").append("nested.custom-named-field", "cname"));
assertThat(filter.getValue())
.isEqualTo(new Document("_id", "id-1").append("value", "v1").append("nested.custom-named-field", "cname"));
}
@Test // DATAMONGO-2341
@@ -2272,7 +2282,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
verify(db).createCollection(any(), options.capture());
assertThat(options.getValue().getTimeSeriesOptions().toString())
.isEqualTo(new com.mongodb.client.model.TimeSeriesOptions("time_stamp").metaField("meta").granularity(TimeSeriesGranularity.HOURS).toString());
.isEqualTo(new com.mongodb.client.model.TimeSeriesOptions("time_stamp").metaField("meta")
.granularity(TimeSeriesGranularity.HOURS).toString());
}
class AutogenerateableId {
@@ -2370,7 +2381,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
@Field("firstname") String name;
}
@Sharded(shardKey = {"value", "nested.customName"})
@Sharded(shardKey = { "value", "nested.customName" })
static class WithShardKeyPointingToNested {
String id;
String value;
@@ -2384,13 +2395,12 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
Instant timestamp;
}
@TimeSeries(timeField = "timestamp", metaField = "meta", granularity = Granularities.HOURS)
@TimeSeries(timeField = "timestamp", metaField = "meta", granularity = Granularity.HOURS)
static class TimeSeriesType {
String id;
@Field("time_stamp")
Instant timestamp;
@Field("time_stamp") Instant timestamp;
Object meta;
}

View File

@@ -20,15 +20,9 @@ import static org.mockito.Mockito.*;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.test.util.Assertions.assertThat;
import com.mongodb.client.model.TimeSeriesGranularity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.mongodb.core.MongoTemplateUnitTests.TimeSeriesType;
import org.springframework.data.mongodb.core.MongoTemplateUnitTests.TimeSeriesTypeWithDefaults;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.mapping.TimeSeries;
import org.springframework.data.mongodb.core.timeseries.Granularities;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
@@ -77,9 +71,11 @@ import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Sw
import org.springframework.data.mongodb.core.aggregation.Fields;
import org.springframework.data.mongodb.core.aggregation.SetOperation;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.TimeSeries;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent;
import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterConvertCallback;
@@ -93,6 +89,7 @@ import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.timeseries.Granularity;
import org.springframework.lang.Nullable;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.CollectionUtils;
@@ -106,6 +103,7 @@ import com.mongodb.client.model.FindOneAndDeleteOptions;
import com.mongodb.client.model.FindOneAndReplaceOptions;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.ReplaceOptions;
import com.mongodb.client.model.TimeSeriesGranularity;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.InsertManyResult;
@@ -951,7 +949,8 @@ public class ReactiveMongoTemplateUnitTests {
@Test // DATAMONGO-2344, DATAMONGO-2572
void allowSecondaryReadsQueryOptionShouldApplyPrimaryPreferredReadPreferenceForFindDistinct() {
template.findDistinct(new Query().allowSecondaryReads(), "name", AutogenerateableId.class, String.class).subscribe();
template.findDistinct(new Query().allowSecondaryReads(), "name", AutogenerateableId.class, String.class)
.subscribe();
verify(collection).withReadPreference(eq(ReadPreference.primaryPreferred()));
}
@@ -1428,8 +1427,7 @@ public class ReactiveMongoTemplateUnitTests {
Publisher<String> publisher = Mono.just("data");
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> template.insert(publisher));
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> template.insert(publisher));
}
@Test // GH-3731
@@ -1453,7 +1451,8 @@ public class ReactiveMongoTemplateUnitTests {
verify(db).createCollection(any(), options.capture());
assertThat(options.getValue().getTimeSeriesOptions().toString())
.isEqualTo(new com.mongodb.client.model.TimeSeriesOptions("time_stamp").metaField("meta").granularity(TimeSeriesGranularity.HOURS).toString());
.isEqualTo(new com.mongodb.client.model.TimeSeriesOptions("time_stamp").metaField("meta")
.granularity(TimeSeriesGranularity.HOURS).toString());
}
private void stubFindSubscribe(Document document) {
@@ -1520,13 +1519,12 @@ public class ReactiveMongoTemplateUnitTests {
Instant timestamp;
}
@TimeSeries(timeField = "timestamp", metaField = "meta", granularity = Granularities.HOURS)
@TimeSeries(timeField = "timestamp", metaField = "meta", granularity = Granularity.HOURS)
static class TimeSeriesType {
String id;
@Field("time_stamp")
Instant timestamp;
@Field("time_stamp") Instant timestamp;
Object meta;
}

View File

@@ -1,13 +1,14 @@
[[time-series]]
== Time Series
MongoDB 5.0 introduced https://docs.mongodb.com/manual/core/timeseries-collections/[Time Series] collections optimized to efficiently store sequences of measurements.
Those collections need to be actively created before inserting any data. This can be done by manually executing the command, defining time series collection options or extracting options from a `@TimeSeries` annotation as shown in the examples below.
MongoDB 5.0 introduced https://docs.mongodb.com/manual/core/timeseries-collections/[Time Series] collections that are optimized to efficiently store documents over time such as measurements or events.
Those collections need to be created as such before inserting any data.
Collections can be created by either running the `createCollection` command, defining time series collection options or extracting options from a `@TimeSeries` annotation as shown in the examples below.
.Create a Time Series Collection
====
.Create a Time Series via the MongoDB Driver
[code, java]
[code,java]
----
template.execute(db -> {
@@ -19,14 +20,14 @@ template.execute(db -> {
});
----
.Create a Time Series Collection with CollectionOptions
[code, java]
.Create a Time Series Collection with `CollectionOptions`
[code,java]
----
template.createCollection("weather", CollectionOptions.timeSeries("timestamp"));
----
.Create a Time Series Collection derived from an Annotation
[code, java]
[code,java]
----
@TimeSeries(collection="weather", timeField = "timestamp")
public class Measurement {
@@ -41,5 +42,5 @@ template.createCollection(Measurement.class);
====
The snippets above can easily be transferred to the reactive API offering the very same methods.
Just make sure to _subscribe_.
Make sure to properly _subscribe_ to the returned publishers.