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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user