From 5543866d135e2350ddaef0daa8f88746b47e7c81 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 23 Jun 2017 09:21:03 +0200 Subject: [PATCH 1/3] DATAMONGO-1682 - Prepare issue branch. --- pom.xml | 2 +- spring-data-mongodb-benchmarks/pom.xml | 2 +- spring-data-mongodb-cross-store/pom.xml | 4 ++-- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 61eb8ba165..36229264c4 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1682-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml index 494925fb63..748c301901 100644 --- a/spring-data-mongodb-benchmarks/pom.xml +++ b/spring-data-mongodb-benchmarks/pom.xml @@ -7,7 +7,7 @@ org.springframework.data spring-data-mongodb-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1682-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml index 4a49168713..963601a3b7 100644 --- a/spring-data-mongodb-cross-store/pom.xml +++ b/spring-data-mongodb-cross-store/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-mongodb-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1682-SNAPSHOT ../pom.xml @@ -48,7 +48,7 @@ org.springframework.data spring-data-mongodb - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1682-SNAPSHOT diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index 750ed23aa8..4c1fc7ad39 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1682-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 5e935eb8d8..d4def9ebae 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1682-SNAPSHOT ../pom.xml From a5780e4c387d8b973733b0c4b506344c73ca16d1 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 2 May 2017 08:20:51 +0200 Subject: [PATCH 2/3] DATAMONGO-1682 - Add support partialFilterExpression for reactive index creation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now support partial filter expression on indexes via Index.partial(…) on the reactive API. This allows to create partial indexes that only index the documents in a collection that meet a specified filter expression. --- .../core/DefaultReactiveIndexOperations.java | 72 ++++++++++-- .../mongodb/core/ReactiveIndexOperations.java | 4 +- .../mongodb/core/ReactiveMongoTemplate.java | 4 +- .../DefaultReactiveIndexOperationsTests.java | 107 +++++++++++++++++- 4 files changed, 169 insertions(+), 18 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java index 0ba81a33f7..f19905b66c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java @@ -15,27 +15,50 @@ */ package org.springframework.data.mongodb.core; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Collection; +import java.util.Optional; + import org.bson.Document; +import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.index.IndexDefinition; import org.springframework.data.mongodb.core.index.IndexInfo; import org.springframework.data.mongodb.core.index.IndexOperations; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.util.Assert; +import com.mongodb.client.model.IndexOptions; import com.mongodb.reactivestreams.client.ListIndexesPublisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - /** * Default implementation of {@link IndexOperations}. * * @author Mark Paluch - * @since 1.11 + * @author Christoph Strobl + * @since 2.0 */ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations { + private static final String PARTIAL_FILTER_EXPRESSION_KEY = "partialFilterExpression"; + private final ReactiveMongoOperations mongoOperations; private final String collectionName; + private final QueryMapper queryMapper; + private final Optional> type; + + /** + * Creates a new {@link DefaultReactiveIndexOperations}. + * + * @param mongoOperations must not be {@literal null}. + * @param collectionName must not be {@literal null}. + */ + public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName, + QueryMapper queryMapper) { + + this(mongoOperations, collectionName, queryMapper, null); + } /** * Creates a new {@link DefaultReactiveIndexOperations}. @@ -43,13 +66,17 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations { * @param mongoOperations must not be {@literal null}. * @param collectionName must not be {@literal null}. */ - public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName) { + public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName, + QueryMapper queryMapper, Class type) { Assert.notNull(mongoOperations, "ReactiveMongoOperations must not be null!"); Assert.notNull(collectionName, "Collection must not be null!"); + Assert.notNull(queryMapper, "QueryMapper must not be null!"); this.mongoOperations = mongoOperations; this.collectionName = collectionName; + this.queryMapper = queryMapper; + this.type = Optional.ofNullable(type); } /* (non-Javadoc) @@ -57,19 +84,46 @@ public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, S */ public Mono ensureIndex(final IndexDefinition indexDefinition) { - return mongoOperations.execute(collectionName, (ReactiveCollectionCallback) collection -> { + return mongoOperations.execute(collectionName, collection -> { Document indexOptions = indexDefinition.getIndexOptions(); if (indexOptions != null) { - return collection.createIndex(indexDefinition.getIndexKeys(), - IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition)); + + IndexOptions ops = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition); + + if (indexOptions.containsKey(PARTIAL_FILTER_EXPRESSION_KEY)) { + + Assert.isInstanceOf(Document.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY)); + + MongoPersistentEntity entity = type + .map(val -> (MongoPersistentEntity) queryMapper.getMappingContext().getRequiredPersistentEntity(val)) + .orElseGet(() -> lookupPersistentEntity(collectionName)); + + ops = ops.partialFilterExpression( + queryMapper.getMappedObject((Document) indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY), entity)); + } + + return collection.createIndex(indexDefinition.getIndexKeys(), ops); } return collection.createIndex(indexDefinition.getIndexKeys()); }).next(); } + private MongoPersistentEntity lookupPersistentEntity(String collection) { + + Collection> entities = queryMapper.getMappingContext().getPersistentEntities(); + + for (MongoPersistentEntity entity : entities) { + if (entity.getCollection().equals(collection)) { + return entity; + } + } + + return null; + } + /* (non-Javadoc) * @see org.springframework.data.mongodb.core.ReactiveIndexOperations#dropIndex(java.lang.String) */ @@ -78,7 +132,7 @@ public Mono dropIndex(final String name) { return mongoOperations.execute(collectionName, collection -> { return Mono.from(collection.dropIndex(name)); - }).flatMap(success -> Mono.empty()).next(); + }).flatMap(success -> Mono. empty()).next(); } /* (non-Javadoc) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveIndexOperations.java index 88c89d2c3c..259ad13d16 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveIndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveIndexOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2017 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. @@ -15,8 +15,6 @@ */ package org.springframework.data.mongodb.core; -import java.util.List; - import org.springframework.data.mongodb.core.index.IndexDefinition; import org.springframework.data.mongodb.core.index.IndexInfo; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java index 9ed9ae3f01..39862d413a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java @@ -302,14 +302,14 @@ public MongoConverter getConverter() { * @see org.springframework.data.mongodb.core.ReactiveMongoOperations#reactiveIndexOps(java.lang.String) */ public ReactiveIndexOperations indexOps(String collectionName) { - return new DefaultReactiveIndexOperations(this, collectionName); + return new DefaultReactiveIndexOperations(this, collectionName, this.queryMapper); } /* (non-Javadoc) * @see org.springframework.data.mongodb.core.ReactiveMongoOperations#reactiveIndexOps(java.lang.Class) */ public ReactiveIndexOperations indexOps(Class entityClass) { - return new DefaultReactiveIndexOperations(this, determineCollectionName(entityClass)); + return new DefaultReactiveIndexOperations(this, determineCollectionName(entityClass), this.queryMapper); } public String getCollectionName(Class entityClass) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsTests.java index 1dbff6cf40..2494fbfc6e 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsTests.java @@ -18,9 +18,14 @@ import static org.assertj.core.api.Assertions.*; import static org.hamcrest.core.Is.*; import static org.junit.Assume.*; +import static org.springframework.data.mongodb.core.index.PartialIndexFilter.*; +import static org.springframework.data.mongodb.core.query.Criteria.*; +import reactor.core.publisher.Mono; import reactor.test.StepVerifier; +import java.util.function.Predicate; + import org.bson.Document; import org.junit.Before; import org.junit.Test; @@ -31,9 +36,11 @@ import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.core.query.Collation.CaseFirst; -import org.springframework.data.mongodb.core.DefaultIndexOperationsIntegrationTests.DefaultIndexOperationsIntegrationTestsSample; +import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.index.Index; import org.springframework.data.mongodb.core.index.IndexDefinition; +import org.springframework.data.mongodb.core.index.IndexInfo; +import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.util.Version; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -64,6 +71,7 @@ protected String getDatabaseName() { } } + private static final Version THREE_DOT_TWO = new Version(3, 2); private static final Version THREE_DOT_FOUR = new Version(3, 4); private static Version mongoVersion; @@ -79,8 +87,10 @@ public void setUp() { String collectionName = this.template.getCollectionName(DefaultIndexOperationsIntegrationTestsSample.class); this.collection = this.template.getMongoDatabase().getCollection(collectionName, Document.class); - this.collection.dropIndexes(); - this.indexOps = new DefaultReactiveIndexOperations(template, collectionName); + Mono.from(this.collection.dropIndexes()).subscribe(); + + this.indexOps = new DefaultReactiveIndexOperations(template, collectionName, + new QueryMapper(template.getConverter())); } private void queryMongoVersionIfNecessary() { @@ -111,7 +121,7 @@ public void shouldCreateIndexWithCollationCorrectly() { .append("normalization", false) // .append("backwards", false); - StepVerifier.create(indexOps.getIndexInfo().filter(val -> val.getName().equals("with-collation"))) + StepVerifier.create(indexOps.getIndexInfo().filter(this.indexByName("with-collation"))) // .consumeNextWith(indexInfo -> { assertThat(indexInfo.getCollation()).isPresent(); @@ -122,7 +132,96 @@ public void shouldCreateIndexWithCollationCorrectly() { assertThat(result).isEqualTo(expected); }) // + .thenAwait(); + } + + @Test // DATAMONGO-1682 + public void shouldApplyPartialFilterCorrectly() { + + assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true)); + + IndexDefinition id = new Index().named("partial-with-criteria").on("k3y", Direction.ASC) + .partial(of(where("q-t-y").gte(10))); + + indexOps.ensureIndex(id).subscribe(); + + StepVerifier.create(indexOps.getIndexInfo().filter(this.indexByName("partial-with-criteria"))) // + .consumeNextWith(indexInfo -> { + assertThat(indexInfo.getPartialFilterExpression()).isEqualTo("{ \"q-t-y\" : { \"$gte\" : 10 } }"); + }) // + .thenAwait(); + } + + @Test // DATAMONGO-1682 + public void shouldApplyPartialFilterWithMappedPropertyCorrectly() { + + assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true)); + + IndexDefinition id = new Index().named("partial-with-mapped-criteria").on("k3y", Direction.ASC) + .partial(of(where("quantity").gte(10))); + + indexOps.ensureIndex(id).subscribe(); + + StepVerifier.create(indexOps.getIndexInfo().filter(this.indexByName("partial-with-mapped-criteria"))) // + .consumeNextWith(indexInfo -> { + assertThat(indexInfo.getPartialFilterExpression()).isEqualTo("{ \"qty\" : { \"$gte\" : 10 } }"); + }).thenAwait(); + } + + @Test // DATAMONGO-1682 + public void shouldApplyPartialDBOFilterCorrectly() { + + assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true)); + + IndexDefinition id = new Index().named("partial-with-dbo").on("k3y", Direction.ASC) + .partial(of(new org.bson.Document("qty", new org.bson.Document("$gte", 10)))); + + indexOps.ensureIndex(id).subscribe(); + + StepVerifier.create(indexOps.getIndexInfo().filter(this.indexByName("partial-with-dbo"))) // + .consumeNextWith(indexInfo -> { + assertThat(indexInfo.getPartialFilterExpression()).isEqualTo("{ \"qty\" : { \"$gte\" : 10 } }"); + }) // + .thenAwait(); + + } + + @Test // DATAMONGO-1682 + public void shouldFavorExplicitMappingHintViaClass() { + + assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true)); + + IndexDefinition id = new Index().named("partial-with-inheritance").on("k3y", Direction.ASC) + .partial(of(where("age").gte(10))); + + indexOps = new DefaultReactiveIndexOperations(template, + this.template.getCollectionName(DefaultIndexOperationsIntegrationTestsSample.class), + new QueryMapper(template.getConverter()), MappingToSameCollection.class); + + indexOps.ensureIndex(id).subscribe(); + + StepVerifier.create(indexOps.getIndexInfo().filter(this.indexByName("partial-with-inheritance"))) // + .consumeNextWith(indexInfo -> { + assertThat(indexInfo.getPartialFilterExpression()).isEqualTo("{ \"a_g_e\" : { \"$gte\" : 10 } }"); + }) // .verifyComplete(); } + Predicate indexByName(String name) { + return indexInfo -> indexInfo.getName().equals(name); + } + + @org.springframework.data.mongodb.core.mapping.Document(collection = "default-index-operations-tests") + static class DefaultIndexOperationsIntegrationTestsSample { + + @Field("qty") Integer quantity; + } + + @org.springframework.data.mongodb.core.mapping.Document(collection = "default-index-operations-tests") + static class MappingToSameCollection + extends DefaultIndexOperationsIntegrationTests.DefaultIndexOperationsIntegrationTestsSample { + + @Field("a_g_e") Integer age; + } + } From 477eac07e1d14510c901911ae31589d2a1e49b8f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 5 Jul 2017 10:36:46 +0200 Subject: [PATCH 3/3] DATAMONGO-1682 - Polishing. Require non-null arguments in DefaultReactiveIndexOperations constructor. Remove superfluous publisher creation indirections. Use StepVerifier.verifyComplete() to verify the step sequence. Use provided entity type in template API to construct index operations. --- .../mongodb/core/DefaultIndexOperations.java | 28 ++++---- .../core/DefaultReactiveIndexOperations.java | 70 +++++++++---------- .../data/mongodb/core/MongoTemplate.java | 3 +- .../mongodb/core/ReactiveIndexOperations.java | 1 - .../mongodb/core/ReactiveMongoTemplate.java | 6 +- .../DefaultReactiveIndexOperationsTests.java | 24 +++---- 6 files changed, 61 insertions(+), 71 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java index 7b46efd80c..51c0a31d05 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java @@ -23,8 +23,8 @@ import org.bson.Document; import org.springframework.dao.DataAccessException; -import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.index.IndexDefinition; import org.springframework.data.mongodb.core.index.IndexInfo; import org.springframework.data.mongodb.core.index.IndexOperations; @@ -38,7 +38,7 @@ /** * Default implementation of {@link IndexOperations}. - * + * * @author Mark Pollack * @author Oliver Gierke * @author Komi Innocent @@ -56,7 +56,7 @@ public class DefaultIndexOperations implements IndexOperations { /** * Creates a new {@link DefaultIndexOperations}. - * + * * @param mongoDbFactory must not be {@literal null}. * @param collectionName must not be {@literal null}. * @param queryMapper must not be {@literal null}. @@ -98,24 +98,22 @@ public String ensureIndex(final IndexDefinition indexDefinition) { Document indexOptions = indexDefinition.getIndexOptions(); - if (indexOptions != null) { - - IndexOptions ops = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition); + if (indexOptions == null) { + return collection.createIndex(indexDefinition.getIndexKeys()); + } - if (indexOptions.containsKey(PARTIAL_FILTER_EXPRESSION_KEY)) { + IndexOptions ops = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition); - Assert.isInstanceOf(Document.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY)); + if (indexOptions.containsKey(PARTIAL_FILTER_EXPRESSION_KEY)) { - ops.partialFilterExpression( mapper.getMappedObject( - (Document) indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY), lookupPersistentEntity(type, collectionName))); - } + Assert.isInstanceOf(Document.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY)); - return collection.createIndex(indexDefinition.getIndexKeys(), ops); + ops.partialFilterExpression(mapper.getMappedObject((Document) indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY), + lookupPersistentEntity(type, collectionName))); } - return collection.createIndex(indexDefinition.getIndexKeys()); - } - ); + return collection.createIndex(indexDefinition.getIndexKeys(), ops); + }); } private MongoPersistentEntity lookupPersistentEntity(Class entityType, String collection) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java index f19905b66c..431dd73c5e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2017 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. @@ -25,15 +25,13 @@ import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.index.IndexDefinition; import org.springframework.data.mongodb.core.index.IndexInfo; -import org.springframework.data.mongodb.core.index.IndexOperations; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.util.Assert; import com.mongodb.client.model.IndexOptions; -import com.mongodb.reactivestreams.client.ListIndexesPublisher; /** - * Default implementation of {@link IndexOperations}. + * Default implementation of {@link ReactiveIndexOperations}. * * @author Mark Paluch * @author Christoph Strobl @@ -53,11 +51,11 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations { * * @param mongoOperations must not be {@literal null}. * @param collectionName must not be {@literal null}. + * @param queryMapper must not be {@literal null}. */ public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName, QueryMapper queryMapper) { - - this(mongoOperations, collectionName, queryMapper, null); + this(mongoOperations, collectionName, queryMapper, Optional.empty()); } /** @@ -65,9 +63,16 @@ public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, S * * @param mongoOperations must not be {@literal null}. * @param collectionName must not be {@literal null}. + * @param queryMapper must not be {@literal null}. + * @param type used for mapping potential partial index filter expression, must not be {@literal null}. */ public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName, QueryMapper queryMapper, Class type) { + this(mongoOperations, collectionName, queryMapper, Optional.of(type)); + } + + private DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName, + QueryMapper queryMapper, Optional> type) { Assert.notNull(mongoOperations, "ReactiveMongoOperations must not be null!"); Assert.notNull(collectionName, "Collection must not be null!"); @@ -76,7 +81,7 @@ public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, S this.mongoOperations = mongoOperations; this.collectionName = collectionName; this.queryMapper = queryMapper; - this.type = Optional.ofNullable(type); + this.type = type; } /* (non-Javadoc) @@ -88,26 +93,26 @@ public Mono ensureIndex(final IndexDefinition indexDefinition) { Document indexOptions = indexDefinition.getIndexOptions(); - if (indexOptions != null) { - - IndexOptions ops = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition); + if (indexOptions == null) { + return collection.createIndex(indexDefinition.getIndexKeys()); + } - if (indexOptions.containsKey(PARTIAL_FILTER_EXPRESSION_KEY)) { + IndexOptions ops = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition); - Assert.isInstanceOf(Document.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY)); + if (indexOptions.containsKey(PARTIAL_FILTER_EXPRESSION_KEY)) { - MongoPersistentEntity entity = type - .map(val -> (MongoPersistentEntity) queryMapper.getMappingContext().getRequiredPersistentEntity(val)) - .orElseGet(() -> lookupPersistentEntity(collectionName)); + Assert.isInstanceOf(Document.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY)); - ops = ops.partialFilterExpression( - queryMapper.getMappedObject((Document) indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY), entity)); - } + MongoPersistentEntity entity = type + .map(val -> (MongoPersistentEntity) queryMapper.getMappingContext().getRequiredPersistentEntity(val)) + .orElseGet(() -> lookupPersistentEntity(collectionName)); - return collection.createIndex(indexDefinition.getIndexKeys(), ops); + ops = ops.partialFilterExpression( + queryMapper.getMappedObject(indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY, Document.class), entity)); } - return collection.createIndex(indexDefinition.getIndexKeys()); + return collection.createIndex(indexDefinition.getIndexKeys(), ops); + }).next(); } @@ -115,24 +120,17 @@ private MongoPersistentEntity lookupPersistentEntity(String collection) { Collection> entities = queryMapper.getMappingContext().getPersistentEntities(); - for (MongoPersistentEntity entity : entities) { - if (entity.getCollection().equals(collection)) { - return entity; - } - } - - return null; + return entities.stream() // + .filter(entity -> entity.getCollection().equals(collection)) // + .findFirst() // + .orElse(null); } /* (non-Javadoc) * @see org.springframework.data.mongodb.core.ReactiveIndexOperations#dropIndex(java.lang.String) */ public Mono dropIndex(final String name) { - - return mongoOperations.execute(collectionName, collection -> { - - return Mono.from(collection.dropIndex(name)); - }).flatMap(success -> Mono. empty()).next(); + return mongoOperations.execute(collectionName, collection -> collection.dropIndex(name)).then(); } /* (non-Javadoc) @@ -147,11 +145,7 @@ public Mono dropAllIndexes() { */ public Flux getIndexInfo() { - return mongoOperations.execute(collectionName, collection -> { - - ListIndexesPublisher indexesPublisher = collection.listIndexes(Document.class); - - return Flux.from(indexesPublisher).map(IndexConverters.documentToIndexInfoConverter()::convert); - }); + return mongoOperations.execute(collectionName, collection -> collection.listIndexes(Document.class)) // + .map(IndexConverters.documentToIndexInfoConverter()::convert); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index d1e34e095b..8c27f268a9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -548,7 +548,8 @@ public IndexOperations indexOps(String collectionName) { } public IndexOperations indexOps(Class entityClass) { - return new DefaultIndexOperations(getMongoDbFactory(), determineCollectionName(entityClass), queryMapper); + return new DefaultIndexOperations(getMongoDbFactory(), determineCollectionName(entityClass), queryMapper, + entityClass); } public BulkOperations bulkOps(BulkMode bulkMode, String collectionName) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveIndexOperations.java index 259ad13d16..c375c07971 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveIndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveIndexOperations.java @@ -56,5 +56,4 @@ public interface ReactiveIndexOperations { * @return index information on the collection */ Flux getIndexInfo(); - } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java index 39862d413a..8cdcb90b28 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java @@ -309,7 +309,8 @@ public ReactiveIndexOperations indexOps(String collectionName) { * @see org.springframework.data.mongodb.core.ReactiveMongoOperations#reactiveIndexOps(java.lang.Class) */ public ReactiveIndexOperations indexOps(Class entityClass) { - return new DefaultReactiveIndexOperations(this, determineCollectionName(entityClass), this.queryMapper); + return new DefaultReactiveIndexOperations(this, determineCollectionName(entityClass), this.queryMapper, + entityClass); } public String getCollectionName(Class entityClass) { @@ -1915,8 +1916,7 @@ String determineCollectionName(Class entityClass) { "No class parameter provided, entity collection can't be determined!"); } - MongoPersistentEntity entity = mappingContext.getRequiredPersistentEntity(entityClass); - return entity.getCollection(); + return mappingContext.getRequiredPersistentEntity(entityClass).getCollection(); } private static MappingMongoConverter getDefaultMongoConverter() { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsTests.java index 2494fbfc6e..effbf4ef27 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsTests.java @@ -16,12 +16,10 @@ package org.springframework.data.mongodb.core; import static org.assertj.core.api.Assertions.*; -import static org.hamcrest.core.Is.*; import static org.junit.Assume.*; import static org.springframework.data.mongodb.core.index.PartialIndexFilter.*; import static org.springframework.data.mongodb.core.query.Criteria.*; -import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import java.util.function.Predicate; @@ -87,10 +85,10 @@ public void setUp() { String collectionName = this.template.getCollectionName(DefaultIndexOperationsIntegrationTestsSample.class); this.collection = this.template.getMongoDatabase().getCollection(collectionName, Document.class); - Mono.from(this.collection.dropIndexes()).subscribe(); - this.indexOps = new DefaultReactiveIndexOperations(template, collectionName, new QueryMapper(template.getConverter())); + + StepVerifier.create(this.collection.dropIndexes()).expectNextCount(1).verifyComplete(); } private void queryMongoVersionIfNecessary() { @@ -104,7 +102,7 @@ private void queryMongoVersionIfNecessary() { @Test // DATAMONGO-1518 public void shouldCreateIndexWithCollationCorrectly() { - assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_FOUR), is(true)); + assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_FOUR)); IndexDefinition id = new Index().named("with-collation").on("xyz", Direction.ASC) .collation(Collation.of("de_AT").caseFirst(CaseFirst.off())); @@ -132,13 +130,13 @@ public void shouldCreateIndexWithCollationCorrectly() { assertThat(result).isEqualTo(expected); }) // - .thenAwait(); + .verifyComplete(); } @Test // DATAMONGO-1682 public void shouldApplyPartialFilterCorrectly() { - assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true)); + assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO)); IndexDefinition id = new Index().named("partial-with-criteria").on("k3y", Direction.ASC) .partial(of(where("q-t-y").gte(10))); @@ -149,13 +147,13 @@ public void shouldApplyPartialFilterCorrectly() { .consumeNextWith(indexInfo -> { assertThat(indexInfo.getPartialFilterExpression()).isEqualTo("{ \"q-t-y\" : { \"$gte\" : 10 } }"); }) // - .thenAwait(); + .verifyComplete(); } @Test // DATAMONGO-1682 public void shouldApplyPartialFilterWithMappedPropertyCorrectly() { - assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true)); + assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO)); IndexDefinition id = new Index().named("partial-with-mapped-criteria").on("k3y", Direction.ASC) .partial(of(where("quantity").gte(10))); @@ -165,13 +163,13 @@ public void shouldApplyPartialFilterWithMappedPropertyCorrectly() { StepVerifier.create(indexOps.getIndexInfo().filter(this.indexByName("partial-with-mapped-criteria"))) // .consumeNextWith(indexInfo -> { assertThat(indexInfo.getPartialFilterExpression()).isEqualTo("{ \"qty\" : { \"$gte\" : 10 } }"); - }).thenAwait(); + }).verifyComplete(); } @Test // DATAMONGO-1682 public void shouldApplyPartialDBOFilterCorrectly() { - assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true)); + assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO)); IndexDefinition id = new Index().named("partial-with-dbo").on("k3y", Direction.ASC) .partial(of(new org.bson.Document("qty", new org.bson.Document("$gte", 10)))); @@ -182,14 +180,14 @@ public void shouldApplyPartialDBOFilterCorrectly() { .consumeNextWith(indexInfo -> { assertThat(indexInfo.getPartialFilterExpression()).isEqualTo("{ \"qty\" : { \"$gte\" : 10 } }"); }) // - .thenAwait(); + .verifyComplete(); } @Test // DATAMONGO-1682 public void shouldFavorExplicitMappingHintViaClass() { - assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true)); + assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO)); IndexDefinition id = new Index().named("partial-with-inheritance").on("k3y", Direction.ASC) .partial(of(where("age").gte(10)));