Skip to content

Commit 77f318b

Browse files
christophstroblmp911de
authored andcommitted
Add support to create views via reactive/template API.
This commit introduces support to create MongoDB Views directly via the Reactive-/MongoOperations API. Closes: #2594 Original pull request: #4142.
1 parent 7c7e704 commit 77f318b

File tree

10 files changed

+677
-12
lines changed

10 files changed

+677
-12
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java

+52
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
import org.springframework.data.geo.GeoResults;
2727
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
2828
import org.springframework.data.mongodb.core.aggregation.Aggregation;
29+
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
2930
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
31+
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
3032
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
3133
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
3234
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
@@ -280,6 +282,56 @@ public <T> T execute(SessionCallback<T> action, Consumer<ClientSession> onComple
280282
*/
281283
MongoCollection<Document> createCollection(String collectionName, @Nullable CollectionOptions collectionOptions);
282284

285+
/**
286+
* Create a view with the the provided name whose contents are defined by the {@link AggregationOperation pipeline
287+
* stages} on another collection or view identified by the given {@link #getCollectionName(Class) source type}.
288+
*
289+
* @param name the name of the view to create.
290+
* @param source the type defining the views source collection.
291+
* @param stages the {@link AggregationOperation aggregation pipeline stages} defining the view content.
292+
* @since 4.0
293+
*/
294+
default MongoCollection<Document> createView(String name, Class<?> source, AggregationOperation... stages) {
295+
return createView(name, source, AggregationPipeline.of(stages));
296+
}
297+
298+
/**
299+
* Create a view with the the provided name whose contents are defined by the {@link AggregationPipeline pipeline} on
300+
* another collection or view identified by the given {@link #getCollectionName(Class) source type}.
301+
*
302+
* @param name the name of the view to create.
303+
* @param source the type defining the views source collection.
304+
* @param pipeline the {@link AggregationPipeline} defining the view content.
305+
* @since 4.0
306+
*/
307+
default MongoCollection<Document> createView(String name, Class<?> source, AggregationPipeline pipeline) {
308+
return createView(name, source, pipeline, null);
309+
}
310+
311+
/**
312+
* Create a view with the the provided name whose contents are defined by the {@link AggregationPipeline pipeline} on
313+
* another collection or view identified by the given {@link #getCollectionName(Class) source type}.
314+
*
315+
* @param name the name of the view to create.
316+
* @param source the type defining the views source collection.
317+
* @param pipeline the {@link AggregationPipeline} defining the view content.
318+
* @param options additional settings to apply when creating the view. Can be {@literal null}.
319+
* @since 4.0
320+
*/
321+
MongoCollection<Document> createView(String name, Class<?> source, AggregationPipeline pipeline, @Nullable ViewOptions options);
322+
323+
/**
324+
* Create a view with the the provided name whose contents are defined by the {@link AggregationPipeline pipeline} on
325+
* another collection or view identified by the given source.
326+
*
327+
* @param name the name of the view to create.
328+
* @param source the name of the collection or view defining the to be created views source.
329+
* @param pipeline the {@link AggregationPipeline} defining the view content.
330+
* @param options additional settings to apply when creating the view. Can be {@literal null}.
331+
* @since 4.0
332+
*/
333+
MongoCollection<Document> createView(String name, String source, AggregationPipeline pipeline, @Nullable ViewOptions options);
334+
283335
/**
284336
* A set of collection names.
285337
*

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

+35
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import org.springframework.data.mongodb.core.aggregation.Aggregation;
6868
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
6969
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
70+
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
7071
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
7172
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
7273
import org.springframework.data.mongodb.core.convert.DbRefResolver;
@@ -632,6 +633,40 @@ public MongoCollection<Document> createCollection(String collectionName,
632633
operations.convertToCreateCollectionOptions(collectionOptions, Object.class));
633634
}
634635

636+
@Override
637+
public MongoCollection<Document> createView(String name, Class<?> source, AggregationPipeline pipeline, @Nullable ViewOptions options) {
638+
639+
return createView(name, getCollectionName(source),
640+
queryOperations.createAggregation(Aggregation.newAggregation(source, pipeline.getOperations()), source),
641+
options);
642+
}
643+
644+
@Override
645+
public MongoCollection<Document> createView(String name, String source, AggregationPipeline pipeline, @Nullable ViewOptions options) {
646+
647+
return createView(name, source,
648+
queryOperations.createAggregation(Aggregation.newAggregation(pipeline.getOperations()), (Class<?>) null),
649+
options);
650+
}
651+
652+
private MongoCollection<Document> createView(String name, String source, AggregationDefinition aggregation,
653+
@Nullable ViewOptions options) {
654+
return doCreateView(name, source, aggregation.getAggregationPipeline(), options);
655+
}
656+
657+
protected MongoCollection<Document> doCreateView(String name, String source, List<Document> pipeline, @Nullable ViewOptions options) {
658+
659+
CreateViewOptions viewOptions = new CreateViewOptions();
660+
if (options != null) {
661+
options.getCollation().map(Collation::toMongoCollation).ifPresent(viewOptions::collation);
662+
}
663+
664+
return execute(db -> {
665+
db.createView(name, source, pipeline, viewOptions);
666+
return db.getCollection(name);
667+
});
668+
}
669+
635670
@Override
636671
@SuppressWarnings("ConstantConditions")
637672
public MongoCollection<Document> getCollection(String collectionName) {

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java

+52
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.mongodb.core;
1717

18+
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
19+
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
1820
import reactor.core.publisher.Flux;
1921
import reactor.core.publisher.Mono;
2022

@@ -240,6 +242,56 @@ <T> Mono<MongoCollection<Document>> createCollection(Class<T> entityClass,
240242
*/
241243
Mono<MongoCollection<Document>> createCollection(String collectionName, CollectionOptions collectionOptions);
242244

245+
/**
246+
* Create a view with the the provided name whose contents are defined by the {@link AggregationOperation pipeline
247+
* stages} on another collection or view identified by the given {@link #getCollectionName(Class) source type}.
248+
*
249+
* @param name the name of the view to create.
250+
* @param source the type defining the views source collection.
251+
* @param stages the {@link AggregationOperation aggregation pipeline stages} defining the view content.
252+
* @since 4.0
253+
*/
254+
default Mono<MongoCollection<Document>> createView(String name, Class<?> source, AggregationOperation... stages) {
255+
return createView(name, source, AggregationPipeline.of(stages));
256+
}
257+
258+
/**
259+
* Create a view with the the provided name whose contents are defined by the {@link AggregationPipeline pipeline} on
260+
* another collection or view identified by the given {@link #getCollectionName(Class) source type}.
261+
*
262+
* @param name the name of the view to create.
263+
* @param source the type defining the views source collection.
264+
* @param pipeline the {@link AggregationPipeline} defining the view content.
265+
* @since 4.0
266+
*/
267+
default Mono<MongoCollection<Document>> createView(String name, Class<?> source, AggregationPipeline pipeline) {
268+
return createView(name, source, pipeline, null);
269+
}
270+
271+
/**
272+
* Create a view with the the provided name whose contents are defined by the {@link AggregationPipeline pipeline} on
273+
* another collection or view identified by the given {@link #getCollectionName(Class) source type}.
274+
*
275+
* @param name the name of the view to create.
276+
* @param source the type defining the views source collection.
277+
* @param pipeline the {@link AggregationPipeline} defining the view content.
278+
* @param options additional settings to apply when creating the view. Can be {@literal null}.
279+
* @since 4.0
280+
*/
281+
Mono<MongoCollection<Document>> createView(String name, Class<?> source, AggregationPipeline pipeline, @Nullable ViewOptions options);
282+
283+
/**
284+
* Create a view with the the provided name whose contents are defined by the {@link AggregationPipeline pipeline} on
285+
* another collection or view identified by the given source.
286+
*
287+
* @param name the name of the view to create.
288+
* @param source the name of the collection or view defining the to be created views source.
289+
* @param pipeline the {@link AggregationPipeline} defining the view content.
290+
* @param options additional settings to apply when creating the view. Can be {@literal null}.
291+
* @since 4.0
292+
*/
293+
Mono<MongoCollection<Document>> createView(String name, String source, AggregationPipeline pipeline, @Nullable ViewOptions options);
294+
243295
/**
244296
* A set of collection names.
245297
*

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java

+38-11
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
import org.bson.types.ObjectId;
4747
import org.reactivestreams.Publisher;
4848
import org.reactivestreams.Subscriber;
49-
5049
import org.springframework.beans.BeansException;
5150
import org.springframework.context.ApplicationContext;
5251
import org.springframework.context.ApplicationContextAware;
@@ -81,6 +80,7 @@
8180
import org.springframework.data.mongodb.core.aggregation.Aggregation;
8281
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
8382
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
83+
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
8484
import org.springframework.data.mongodb.core.aggregation.PrefixingDelegatingAggregationOperationContext;
8585
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
8686
import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext;
@@ -127,16 +127,7 @@
127127
import com.mongodb.MongoException;
128128
import com.mongodb.ReadPreference;
129129
import com.mongodb.WriteConcern;
130-
import com.mongodb.client.model.CountOptions;
131-
import com.mongodb.client.model.CreateCollectionOptions;
132-
import com.mongodb.client.model.DeleteOptions;
133-
import com.mongodb.client.model.EstimatedDocumentCountOptions;
134-
import com.mongodb.client.model.FindOneAndDeleteOptions;
135-
import com.mongodb.client.model.FindOneAndReplaceOptions;
136-
import com.mongodb.client.model.FindOneAndUpdateOptions;
137-
import com.mongodb.client.model.ReplaceOptions;
138-
import com.mongodb.client.model.ReturnDocument;
139-
import com.mongodb.client.model.UpdateOptions;
130+
import com.mongodb.client.model.*;
140131
import com.mongodb.client.model.changestream.FullDocument;
141132
import com.mongodb.client.result.DeleteResult;
142133
import com.mongodb.client.result.InsertOneResult;
@@ -671,6 +662,42 @@ public Mono<MongoCollection<Document>> createCollection(String collectionName,
671662
return doCreateCollection(collectionName, convertToCreateCollectionOptions(collectionOptions));
672663
}
673664

665+
@Override
666+
public Mono<MongoCollection<Document>> createView(String name, Class<?> source, AggregationPipeline pipeline, @Nullable ViewOptions options) {
667+
668+
return createView(name, getCollectionName(source),
669+
queryOperations.createAggregation(Aggregation.newAggregation(source, pipeline.getOperations()), source),
670+
options);
671+
}
672+
673+
@Override
674+
public Mono<MongoCollection<Document>> createView(String name, String source, AggregationPipeline pipeline,
675+
@Nullable ViewOptions options) {
676+
677+
return createView(name, source,
678+
queryOperations.createAggregation(Aggregation.newAggregation(pipeline.getOperations()), (Class<?>) null),
679+
options);
680+
}
681+
682+
private Mono<MongoCollection<Document>> createView(String name, String source, AggregationDefinition aggregation,
683+
@Nullable ViewOptions options) {
684+
return doCreateView(name, source, aggregation.getAggregationPipeline(), options);
685+
}
686+
687+
protected Mono<MongoCollection<Document>> doCreateView(String name, String source, List<Document> pipeline,
688+
@Nullable ViewOptions options) {
689+
690+
CreateViewOptions viewOptions = new CreateViewOptions();
691+
if (options != null) {
692+
options.getCollation().map(Collation::toMongoCollation).ifPresent(viewOptions::collation);
693+
}
694+
695+
return execute(db -> {
696+
return Flux.from(db.createView(name, source, pipeline, viewOptions))
697+
.then(Mono.fromSupplier(() -> db.getCollection(name)));
698+
}).next();
699+
}
700+
674701
@Override
675702
public Mono<MongoCollection<Document>> getCollection(String collectionName) {
676703

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core;
17+
18+
import java.util.Optional;
19+
20+
import org.springframework.data.mongodb.core.query.Collation;
21+
import org.springframework.lang.Nullable;
22+
23+
/**
24+
* Immutable object holding additional options to be applied when creating a MongoDB
25+
* <a href="https://www.mongodb.com/docs/manual/core/views/">views</a>.
26+
*
27+
* @author Christoph Strobl
28+
* @since 4.0
29+
*/
30+
public class ViewOptions {
31+
32+
@Nullable private Collation collation;
33+
34+
static ViewOptions none() {
35+
return new ViewOptions();
36+
}
37+
38+
/**
39+
* Creates new instance of {@link ViewOptions}.
40+
*/
41+
public ViewOptions() {
42+
this(null);
43+
}
44+
45+
private ViewOptions(@Nullable Collation collation) {
46+
this.collation = collation;
47+
}
48+
49+
/**
50+
* Get the {@link Collation} to be set.
51+
*
52+
* @return {@link Optional#empty()} if not set.
53+
*/
54+
public Optional<Collation> getCollation() {
55+
return Optional.ofNullable(collation);
56+
}
57+
58+
/**
59+
* @param collation the {@link Collation} to use for language-specific string comparison.
60+
* @return new instance of {@link ViewOptions}.
61+
*/
62+
public ViewOptions collation(Collation collation) {
63+
return new ViewOptions(collation);
64+
}
65+
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationPipeline.java

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.mongodb.core.aggregation;
1717

1818
import java.util.ArrayList;
19+
import java.util.Arrays;
1920
import java.util.Collections;
2021
import java.util.List;
2122
import java.util.function.Predicate;
@@ -34,6 +35,10 @@ public class AggregationPipeline {
3435

3536
private final List<AggregationOperation> pipeline;
3637

38+
public static AggregationPipeline of(AggregationOperation... stages) {
39+
return new AggregationPipeline(Arrays.asList(stages));
40+
}
41+
3742
/**
3843
* Create an empty pipeline
3944
*/

0 commit comments

Comments
 (0)