Skip to content

Refine createCollection(…) documentation and explore CollectionOptions customization #4979

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>5.0.0-SNAPSHOT</version>
<version>5.0.0-GH-4978-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data MongoDB</name>
2 changes: 1 addition & 1 deletion spring-data-mongodb-distribution/pom.xml
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>5.0.0-SNAPSHOT</version>
<version>5.0.0-GH-4978-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

2 changes: 1 addition & 1 deletion spring-data-mongodb/pom.xml
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>5.0.0-SNAPSHOT</version>
<version>5.0.0-GH-4978-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

@@ -260,12 +261,48 @@ default SessionScoped withSession(Supplier<ClientSession> sessionProvider) {

/**
* Create an uncapped collection with a name based on the provided entity class.
* <p>
* This method derives {@link CollectionOptions} from the given {@code entityClass} using
* {@link org.springframework.data.mongodb.core.mapping.Document} and
* {@link org.springframework.data.mongodb.core.mapping.TimeSeries} annotations to determine:
* <ul>
* <li>Collation</li>
* <li>TimeSeries time and meta fields, granularity and {@code expireAfter}</li>
* </ul>
* Any other options such as change stream options, schema-based details (validation, encryption) are not considered
* and must be provided through {@link #createCollection(Class, Function)} or
* {@link #createCollection(Class, CollectionOptions)}.
*
* @param entityClass class that determines the collection to create.
* @return the created collection.
* @see #createCollection(Class, Function)
* @see #createCollection(Class, CollectionOptions)
*/
<T> MongoCollection<Document> createCollection(Class<T> entityClass);

/**
* Create an uncapped collection with a name based on the provided entity class allowing to customize derived
* {@link CollectionOptions}.
* <p>
* This method derives {@link CollectionOptions} from the given {@code entityClass} using
* {@link org.springframework.data.mongodb.core.mapping.Document} and
* {@link org.springframework.data.mongodb.core.mapping.TimeSeries} annotations to determine:
* <ul>
* <li>Collation</li>
* <li>TimeSeries time and meta fields, granularity and {@code expireAfter}</li>
* </ul>
* Any other options such as change stream options, schema-based details (validation, encryption) are not considered
* and must be provided through {@link CollectionOptions}.
*
* @param entityClass class that determines the collection to create.
* @param collectionOptionsCustomizer customizer function to customize the derived {@link CollectionOptions}.
* @return the created collection.
* @see #createCollection(Class, CollectionOptions)
* @since 5.0
*/
<T> MongoCollection<Document> createCollection(Class<T> entityClass,
Function<? super CollectionOptions, ? extends CollectionOptions> collectionOptionsCustomizer);

/**
* Create a collection with a name based on the provided entity class using the options.
*
@@ -998,7 +1035,8 @@ default <T> List<T> findDistinct(Query query, String field, String collection, C
* @see Update
* @see AggregationUpdate
*/
<T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass);
<T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options,
Class<T> entityClass);

/**
* Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify </a>
@@ -1021,8 +1059,8 @@ default <T> List<T> findDistinct(Query query, String field, String collection, C
* @see Update
* @see AggregationUpdate
*/
<T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass,
String collectionName);
<T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options,
Class<T> entityClass, String collectionName);

/**
* Triggers
@@ -1102,7 +1140,8 @@ default <T> List<T> findDistinct(Query query, String field, String collection, C
* as it is after the update.
* @since 2.1
*/
default <T> @Nullable T findAndReplace(Query query, T replacement, FindAndReplaceOptions options, String collectionName) {
default <T> @Nullable T findAndReplace(Query query, T replacement, FindAndReplaceOptions options,
String collectionName) {

Assert.notNull(replacement, "Replacement must not be null");
return findAndReplace(query, replacement, options, (Class<T>) ClassUtils.getUserClass(replacement), collectionName);
@@ -1154,8 +1193,8 @@ default <T> List<T> findDistinct(Query query, String field, String collection, C
* {@link #getCollectionName(Class) derived} from the given replacement value.
* @since 2.1
*/
default <S, T> @Nullable T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType,
Class<T> resultType) {
default <S, T> @Nullable T findAndReplace(Query query, S replacement, FindAndReplaceOptions options,
Class<S> entityType, Class<T> resultType) {

return findAndReplace(query, replacement, options, entityType,
getCollectionName(ClassUtils.getUserClass(entityType)), resultType);
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@@ -650,7 +651,17 @@ public void setSessionSynchronization(SessionSynchronization sessionSynchronizat

@Override
public <T> MongoCollection<Document> createCollection(Class<T> entityClass) {
return createCollection(entityClass, operations.forType(entityClass).getCollectionOptions());
return createCollection(entityClass, Function.identity());
}

@Override
public <T> MongoCollection<Document> createCollection(Class<T> entityClass,
Function<? super CollectionOptions, ? extends CollectionOptions> collectionOptionsCustomizer) {

Assert.notNull(collectionOptionsCustomizer, "CollectionOptions customizer function must not be null");

return createCollection(entityClass,
collectionOptionsCustomizer.apply(operations.forType(entityClass).getCollectionOptions()));
}

@Override
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@

import java.util.Collection;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

import org.bson.Document;
@@ -214,12 +215,48 @@ default ReactiveSessionScoped withSession(Supplier<ClientSession> sessionProvide

/**
* Create an uncapped collection with a name based on the provided entity class.
* <p>
* This method derives {@link CollectionOptions} from the given {@code entityClass} using
* {@link org.springframework.data.mongodb.core.mapping.Document} and
* {@link org.springframework.data.mongodb.core.mapping.TimeSeries} annotations to determine:
* <ul>
* <li>Collation</li>
* <li>TimeSeries time and meta fields, granularity and {@code expireAfter}</li>
* </ul>
* Any other options such as change stream options, schema-based details (validation, encryption) are not considered
* and must be provided through {@link #createCollection(Class, Function)} or
* {@link #createCollection(Class, CollectionOptions)}.
*
* @param entityClass class that determines the collection to create.
* @return the created collection.
* @see #createCollection(Class, Function)
* @see #createCollection(Class, CollectionOptions)
*/
<T> Mono<MongoCollection<Document>> createCollection(Class<T> entityClass);

/**
* Create an uncapped collection with a name based on the provided entity class allowing to customize derived
* {@link CollectionOptions}.
* <p>
* This method derives {@link CollectionOptions} from the given {@code entityClass} using
* {@link org.springframework.data.mongodb.core.mapping.Document} and
* {@link org.springframework.data.mongodb.core.mapping.TimeSeries} annotations to determine:
* <ul>
* <li>Collation</li>
* <li>TimeSeries time and meta fields, granularity and {@code expireAfter}</li>
* </ul>
* Any other options such as change stream options, schema-based details (validation, encryption) are not considered
* and must be provided through {@link CollectionOptions}.
*
* @param entityClass class that determines the collection to create.
* @param collectionOptionsCustomizer customizer function to customize the derived {@link CollectionOptions}.
* @return the created collection.
* @see #createCollection(Class, CollectionOptions)
* @since 5.0
*/
<T> Mono<MongoCollection<Document>> createCollection(Class<T> entityClass,
Function<? super CollectionOptions, ? extends CollectionOptions> collectionOptionsCustomizer);

/**
* Create a collection with a name based on the provided entity class using the options.
*
Original file line number Diff line number Diff line change
@@ -659,7 +659,17 @@ public <T> Mono<T> createMono(String collectionName, ReactiveCollectionCallback<

@Override
public <T> Mono<MongoCollection<Document>> createCollection(Class<T> entityClass) {
return createCollection(entityClass, operations.forType(entityClass).getCollectionOptions());
return createCollection(entityClass, Function.identity());
}

@Override
public <T> Mono<MongoCollection<Document>> createCollection(Class<T> entityClass,
Function<? super CollectionOptions, ? extends CollectionOptions> collectionOptionsCustomizer) {

Assert.notNull(collectionOptionsCustomizer, "CollectionOptions customizer function must not be null");

return createCollection(entityClass,
collectionOptionsCustomizer.apply(operations.forType(entityClass).getCollectionOptions()));
}

@Override
@@ -740,11 +750,10 @@ public <T> Mono<Boolean> collectionExists(Class<T> entityClass) {

@Override
public Mono<Boolean> collectionExists(String collectionName) {
return createMono(
db -> Flux.from(db.listCollectionNames()) //
.filter(s -> s.equals(collectionName)) //
.map(s -> true) //
.single(false));
return createMono(db -> Flux.from(db.listCollectionNames()) //
.filter(s -> s.equals(collectionName)) //
.map(s -> true) //
.single(false));
}

@Override
@@ -2293,7 +2302,7 @@ protected <T> Flux<T> doFindAndDelete(String collectionName, Query query, Class<
.flatMapSequential(deleteResult -> Flux.fromIterable(list)));
}

@SuppressWarnings({"rawtypes", "unchecked", "NullAway"})
@SuppressWarnings({ "rawtypes", "unchecked", "NullAway" })
<S, T> Flux<T> doFindAndDelete(String collectionName, Query query, Class<S> entityClass,
QueryResultConverter<? super S, ? extends T> resultConverter) {

Original file line number Diff line number Diff line change
@@ -42,8 +42,8 @@
/**
* The collection the document representing the entity is supposed to be stored in. If not configured, a default
* collection name will be derived from the type's name. The attribute supports SpEL expressions to dynamically
* calculate the collection to based on a per operation basis.
*
* calculate the collection to based on a per-operation basis.
*
* @return the name of the collection to be used.
*/
@AliasFor("collection")
@@ -53,7 +53,7 @@
* The collection the document representing the entity is supposed to be stored in. If not configured, a default
* collection name will be derived from the type's name. The attribute supports SpEL expressions to dynamically
* calculate the collection to based on a per operation basis.
*
*
* @return the name of the collection to be used.
*/
@AliasFor("value")
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@
/**
* The collection the document representing the entity is supposed to be stored in. If not configured, a default
* collection name will be derived from the type's name. The attribute supports SpEL expressions to dynamically
* calculate the collection based on a per operation basis.
* calculate the collection based on a per-operation basis.
*
* @return the name of the collection to be used.
* @see Document#collection()
Original file line number Diff line number Diff line change
@@ -186,11 +186,13 @@ void beforeEach() {
when(collection.aggregate(any(List.class), any())).thenReturn(aggregateIterable);
when(collection.withReadConcern(any())).thenReturn(collection);
when(collection.withReadPreference(any())).thenReturn(collection);
when(collection.replaceOne(any(), any(), any(com.mongodb.client.model.ReplaceOptions.class))).thenReturn(updateResult);
when(collection.replaceOne(any(), any(), any(com.mongodb.client.model.ReplaceOptions.class)))
.thenReturn(updateResult);
when(collection.withWriteConcern(any())).thenReturn(collectionWithWriteConcern);
when(collection.distinct(anyString(), any(Document.class), any())).thenReturn(distinctIterable);
when(collectionWithWriteConcern.deleteOne(any(Bson.class), any())).thenReturn(deleteResult);
when(collectionWithWriteConcern.replaceOne(any(), any(), any(com.mongodb.client.model.ReplaceOptions.class))).thenReturn(updateResult);
when(collectionWithWriteConcern.replaceOne(any(), any(), any(com.mongodb.client.model.ReplaceOptions.class)))
.thenReturn(updateResult);
when(findIterable.projection(any())).thenReturn(findIterable);
when(findIterable.sort(any(org.bson.Document.class))).thenReturn(findIterable);
when(findIterable.collation(any())).thenReturn(findIterable);
@@ -1263,7 +1265,8 @@ void saveVersionedEntityShouldCallUpdateCorrectly() {

template.save(entity);

verify(collection, times(1)).replaceOne(queryCaptor.capture(), updateCaptor.capture(), any(com.mongodb.client.model.ReplaceOptions.class));
verify(collection, times(1)).replaceOne(queryCaptor.capture(), updateCaptor.capture(),
any(com.mongodb.client.model.ReplaceOptions.class));

assertThat(queryCaptor.getValue()).isEqualTo(new Document("_id", 1).append("version", 10));
assertThat(updateCaptor.getValue())
@@ -1399,10 +1402,14 @@ void createCollectionShouldNotCollationIfNotPresent() {
Assertions.assertThat(options.getValue().getCollation()).isNull();
}

@Test // DATAMONGO-1854
@Test // DATAMONGO-1854, GH-4978
void createCollectionShouldApplyDefaultCollation() {

template.createCollection(Sith.class);
template.createCollection(Sith.class, options -> {

assertThat(options.getCollation()).contains(Collation.of("de_AT"));
return options;
});

ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());
@@ -1426,7 +1433,7 @@ void createCollectionShouldFavorExplicitOptionsOverDefaultCollation() {
@Test // DATAMONGO-1854
void createCollectionShouldUseDefaultCollationIfCollectionOptionsAreNull() {

template.createCollection(Sith.class, null);
template.createCollection(Sith.class, (CollectionOptions) null);

ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());
@@ -2399,8 +2406,7 @@ void createCollectionShouldSetUpTimeSeriesWithExpirationFromString() {
ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());

assertThat(options.getValue().getExpireAfter(TimeUnit.MINUTES))
.isEqualTo(10);
assertThat(options.getValue().getExpireAfter(TimeUnit.MINUTES)).isEqualTo(10);
}

@Test // GH-4099
@@ -2413,8 +2419,7 @@ void createCollectionShouldSetUpTimeSeriesWithExpirationFromProperty() {
ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());

assertThat(options.getValue().getExpireAfter(TimeUnit.MINUTES))
.isEqualTo(12);
assertThat(options.getValue().getExpireAfter(TimeUnit.MINUTES)).isEqualTo(12);
}

@Test // GH-4099
@@ -2425,8 +2430,7 @@ void createCollectionShouldSetUpTimeSeriesWithExpirationFromIso8601String() {
ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());

assertThat(options.getValue().getExpireAfter(TimeUnit.DAYS))
.isEqualTo(1);
assertThat(options.getValue().getExpireAfter(TimeUnit.DAYS)).isEqualTo(1);
}

@Test // GH-4099
@@ -2437,8 +2441,7 @@ void createCollectionShouldSetUpTimeSeriesWithExpirationFromExpression() {
ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());

assertThat(options.getValue().getExpireAfter(TimeUnit.SECONDS))
.isEqualTo(11);
assertThat(options.getValue().getExpireAfter(TimeUnit.SECONDS)).isEqualTo(11);
}

@Test // GH-4099
@@ -2449,16 +2452,14 @@ void createCollectionShouldSetUpTimeSeriesWithExpirationFromExpressionReturningD
ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());

assertThat(options.getValue().getExpireAfter(TimeUnit.SECONDS))
.isEqualTo(100);
assertThat(options.getValue().getExpireAfter(TimeUnit.SECONDS)).isEqualTo(100);
}

@Test // GH-4099
void createCollectionShouldSetUpTimeSeriesWithInvalidTimeoutExpiration() {

assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() ->
template.createCollection(TimeSeriesTypeWithInvalidExpireAfter.class)
);
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> template.createCollection(TimeSeriesTypeWithInvalidExpireAfter.class));
}

@Test // GH-3522
@@ -2611,32 +2612,31 @@ public WriteConcern resolve(MongoAction action) {
verify(collection).withWriteConcern(eq(WriteConcern.UNACKNOWLEDGED));
}

@Test // GH-4099
void passOnTimeSeriesExpireOption() {

template.createCollection("time-series-collection",
CollectionOptions.timeSeries("time_stamp", options -> options.expireAfter(Duration.ofSeconds(10))));
@Test // GH-4099
void passOnTimeSeriesExpireOption() {

ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());
template.createCollection("time-series-collection",
CollectionOptions.timeSeries("time_stamp", options -> options.expireAfter(Duration.ofSeconds(10))));

assertThat(options.getValue().getExpireAfter(TimeUnit.SECONDS)).isEqualTo(10);
}
ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());

@Test // GH-4099
void doNotSetTimeSeriesExpireOptionForNegativeValue() {
assertThat(options.getValue().getExpireAfter(TimeUnit.SECONDS)).isEqualTo(10);
}

template.createCollection("time-series-collection",
CollectionOptions.timeSeries("time_stamp", options -> options.expireAfter(Duration.ofSeconds(-10))));
@Test // GH-4099
void doNotSetTimeSeriesExpireOptionForNegativeValue() {

ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());
template.createCollection("time-series-collection",
CollectionOptions.timeSeries("time_stamp", options -> options.expireAfter(Duration.ofSeconds(-10))));

assertThat(options.getValue().getExpireAfter(TimeUnit.SECONDS)).isEqualTo(0L);
}
ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());

assertThat(options.getValue().getExpireAfter(TimeUnit.SECONDS)).isEqualTo(0L);
}

class AutogenerateableId {
class AutogenerateableId {

@Id BigInteger id;
}
Original file line number Diff line number Diff line change
@@ -456,8 +456,10 @@ void doesNotApplyFieldsWhenInterfaceProjectionIsClosedAndQueryDefinesFields() {
@Test // DATAMONGO-1719
void doesNotApplyFieldsWhenInterfaceProjectionIsOpen() {

template.doFind("star-wars", CollectionPreparer.identity(), new Document(), new Document(), Person.class,
PersonSpELProjection.class, QueryResultConverter.entity(), FindPublisherPreparer.NO_OP_PREPARER).subscribe();
template
.doFind("star-wars", CollectionPreparer.identity(), new Document(), new Document(), Person.class,
PersonSpELProjection.class, QueryResultConverter.entity(), FindPublisherPreparer.NO_OP_PREPARER)
.subscribe();

verify(findPublisher, never()).projection(any());
}
@@ -638,10 +640,14 @@ void createCollectionShouldNotCollationIfNotPresent() {
Assertions.assertThat(options.getValue().getCollation()).isNull();
}

@Test // DATAMONGO-1854
@Test // DATAMONGO-1854, GH-4978
void createCollectionShouldApplyDefaultCollation() {

template.createCollection(Sith.class).subscribe();
template.createCollection(Sith.class, options -> {

assertThat(options.getCollation()).contains(Collation.of("de_AT"));
return options;
}).subscribe();

ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());
@@ -665,7 +671,7 @@ void createCollectionShouldFavorExplicitOptionsOverDefaultCollation() {
@Test // DATAMONGO-1854
void createCollectionShouldUseDefaultCollationIfCollectionOptionsAreNull() {

template.createCollection(Sith.class, null).subscribe();
template.createCollection(Sith.class, (CollectionOptions) null).subscribe();

ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());
@@ -1751,8 +1757,7 @@ void createCollectionShouldSetUpTimeSeriesWithExpirationFromString() {
ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());

assertThat(options.getValue().getExpireAfter(TimeUnit.MINUTES))
.isEqualTo(10);
assertThat(options.getValue().getExpireAfter(TimeUnit.MINUTES)).isEqualTo(10);
}

@Test // GH-4099
@@ -1763,8 +1768,7 @@ void createCollectionShouldSetUpTimeSeriesWithExpirationFromIso8601String() {
ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());

assertThat(options.getValue().getExpireAfter(TimeUnit.DAYS))
.isEqualTo(1);
assertThat(options.getValue().getExpireAfter(TimeUnit.DAYS)).isEqualTo(1);
}

@Test // GH-4099
@@ -1775,8 +1779,7 @@ void createCollectionShouldSetUpTimeSeriesWithExpirationFromExpression() {
ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());

assertThat(options.getValue().getExpireAfter(TimeUnit.SECONDS))
.isEqualTo(11);
assertThat(options.getValue().getExpireAfter(TimeUnit.SECONDS)).isEqualTo(11);
}

@Test // GH-4099
@@ -1787,16 +1790,14 @@ void createCollectionShouldSetUpTimeSeriesWithExpirationFromExpressionReturningD
ArgumentCaptor<CreateCollectionOptions> options = ArgumentCaptor.forClass(CreateCollectionOptions.class);
verify(db).createCollection(any(), options.capture());

assertThat(options.getValue().getExpireAfter(TimeUnit.SECONDS))
.isEqualTo(100);
assertThat(options.getValue().getExpireAfter(TimeUnit.SECONDS)).isEqualTo(100);
}

@Test // GH-4099
void createCollectionShouldSetUpTimeSeriesWithInvalidTimeoutExpiration() {

assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() ->
template.createCollection(TimeSeriesTypeWithInvalidExpireAfter.class).subscribe()
);
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> template.createCollection(TimeSeriesTypeWithInvalidExpireAfter.class).subscribe());
}

private void stubFindSubscribe(Document document) {
Original file line number Diff line number Diff line change
@@ -188,7 +188,7 @@ It is possible to create custom annotations out of the provided ones.
MongoDB Collection Info::
+
====
[source,java,indent=0,subs="verbatim,quotes",role="thrid"]
[source,jsonc,indent=0,subs="verbatim,quotes",role="thrid"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should have been json, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

highlightjs/highlight.js#2155

Alright, let's stick with json

----
{
name: 'patient',