Skip to content

Commit c5c6fc1

Browse files
mp911dechristophstrobl
authored andcommitted
Support ReadConcern & ReadPreference via the Query and Aggregation API.
Add support for setting the ReadConcern and ReadPreference via the Query and Aggregation API. Closes: #4277, #4286 Original Pull Request: #4288
1 parent 368c644 commit c5c6fc1

14 files changed

+810
-219
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2023 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 org.springframework.util.Assert;
19+
20+
import com.mongodb.client.MongoCollection;
21+
22+
/**
23+
* Interface for functional preparation of a {@link MongoCollection}.
24+
*
25+
* @author Mark Paluch
26+
* @since 4.1
27+
*/
28+
public interface CollectionPreparer<T> {
29+
30+
/**
31+
* Returns a preparer that always returns its input collection.
32+
*
33+
* @return a preparer that always returns its input collection.
34+
*/
35+
static <T> CollectionPreparer<T> identity() {
36+
return it -> it;
37+
}
38+
39+
/**
40+
* Prepare the {@code collection}.
41+
*
42+
* @param collection the collection to prepare.
43+
* @return the prepared collection.
44+
*/
45+
T prepare(T collection);
46+
47+
/**
48+
* Returns a composed {@code CollectionPreparer} that first applies this preparer to the collection, and then applies
49+
* the {@code after} preparer to the result. If evaluation of either function throws an exception, it is relayed to
50+
* the caller of the composed function.
51+
*
52+
* @param after the collection preparer to apply after this function is applied.
53+
* @return a composed {@code CollectionPreparer} that first applies this preparer and then applies the {@code after}
54+
* preparer.
55+
*/
56+
default CollectionPreparer<T> andThen(CollectionPreparer<T> after) {
57+
Assert.notNull(after, "After CollectionPreparer must not be null");
58+
return c -> after.prepare(prepare(c));
59+
}
60+
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/*
2+
* Copyright 2023 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.Arrays;
19+
import java.util.List;
20+
import java.util.function.BiFunction;
21+
import java.util.function.Function;
22+
23+
import org.bson.Document;
24+
25+
import com.mongodb.ReadConcern;
26+
import com.mongodb.ReadPreference;
27+
import com.mongodb.client.MongoCollection;
28+
29+
/**
30+
* Support class for delegate implementations to apply {@link ReadConcern} and {@link ReadPreference} settings upon
31+
* {@link CollectionPreparer preparing a collection}.
32+
*
33+
* @author Mark Paluch
34+
* @since 4.1
35+
*/
36+
class CollectionPreparerSupport implements ReadConcernAware, ReadPreferenceAware {
37+
38+
private final List<Object> sources;
39+
40+
private CollectionPreparerSupport(List<Object> sources) {
41+
this.sources = sources;
42+
}
43+
44+
<T> T doPrepare(T collection, Function<T, ReadConcern> concernAccessor, BiFunction<T, ReadConcern, T> concernFunction,
45+
Function<T, ReadPreference> preferenceAccessor, BiFunction<T, ReadPreference, T> preferenceFunction) {
46+
47+
T collectionToUse = collection;
48+
49+
for (Object source : sources) {
50+
if (source instanceof ReadConcernAware rca && rca.hasReadConcern()) {
51+
52+
ReadConcern concern = rca.getReadConcern();
53+
if (concernAccessor.apply(collectionToUse) != concern) {
54+
collectionToUse = concernFunction.apply(collectionToUse, concern);
55+
}
56+
break;
57+
}
58+
}
59+
60+
for (Object source : sources) {
61+
if (source instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) {
62+
63+
ReadPreference preference = rpa.getReadPreference();
64+
if (preferenceAccessor.apply(collectionToUse) != preference) {
65+
collectionToUse = preferenceFunction.apply(collectionToUse, preference);
66+
}
67+
break;
68+
}
69+
}
70+
71+
return collectionToUse;
72+
}
73+
74+
@Override
75+
public boolean hasReadConcern() {
76+
77+
for (Object aware : sources) {
78+
if (aware instanceof ReadConcernAware rca && rca.hasReadConcern()) {
79+
return true;
80+
}
81+
}
82+
83+
return false;
84+
}
85+
86+
@Override
87+
public ReadConcern getReadConcern() {
88+
89+
for (Object aware : sources) {
90+
if (aware instanceof ReadConcernAware rca && rca.hasReadConcern()) {
91+
return rca.getReadConcern();
92+
}
93+
}
94+
95+
return null;
96+
}
97+
98+
@Override
99+
public boolean hasReadPreference() {
100+
101+
for (Object aware : sources) {
102+
if (aware instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) {
103+
return true;
104+
}
105+
}
106+
107+
return false;
108+
}
109+
110+
@Override
111+
public ReadPreference getReadPreference() {
112+
113+
for (Object aware : sources) {
114+
if (aware instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) {
115+
return rpa.getReadPreference();
116+
}
117+
}
118+
119+
return null;
120+
}
121+
122+
static class CollectionPreparerDelegate extends CollectionPreparerSupport
123+
implements CollectionPreparer<MongoCollection<Document>> {
124+
125+
private CollectionPreparerDelegate(List<Object> sources) {
126+
super(sources);
127+
}
128+
129+
public static CollectionPreparerDelegate of(ReadPreferenceAware... awares) {
130+
return of((Object[]) awares);
131+
}
132+
133+
public static CollectionPreparerDelegate of(Object... mixedAwares) {
134+
135+
if (mixedAwares.length == 1 && mixedAwares[0] instanceof CollectionPreparerDelegate) {
136+
return (CollectionPreparerDelegate) mixedAwares[0];
137+
}
138+
139+
return new CollectionPreparerDelegate(Arrays.asList(mixedAwares));
140+
}
141+
142+
@Override
143+
public MongoCollection<Document> prepare(MongoCollection<Document> collection) {
144+
return doPrepare(collection, MongoCollection::getReadConcern, MongoCollection::withReadConcern,
145+
MongoCollection::getReadPreference, MongoCollection::withReadPreference);
146+
}
147+
148+
}
149+
150+
static class ReactiveCollectionPreparerDelegate extends CollectionPreparerSupport
151+
implements CollectionPreparer<com.mongodb.reactivestreams.client.MongoCollection<Document>> {
152+
153+
private ReactiveCollectionPreparerDelegate(List<Object> sources) {
154+
super(sources);
155+
}
156+
157+
public static ReactiveCollectionPreparerDelegate of(ReadPreferenceAware... awares) {
158+
return of((Object[]) awares);
159+
}
160+
161+
public static ReactiveCollectionPreparerDelegate of(Object... mixedAwares) {
162+
163+
if (mixedAwares.length == 1 && mixedAwares[0] instanceof CollectionPreparerDelegate) {
164+
return (ReactiveCollectionPreparerDelegate) mixedAwares[0];
165+
}
166+
167+
return new ReactiveCollectionPreparerDelegate(Arrays.asList(mixedAwares));
168+
}
169+
170+
@Override
171+
public com.mongodb.reactivestreams.client.MongoCollection<Document> prepare(
172+
com.mongodb.reactivestreams.client.MongoCollection<Document> collection) {
173+
return doPrepare(collection, //
174+
com.mongodb.reactivestreams.client.MongoCollection::getReadConcern,
175+
com.mongodb.reactivestreams.client.MongoCollection::withReadConcern,
176+
com.mongodb.reactivestreams.client.MongoCollection::getReadPreference,
177+
com.mongodb.reactivestreams.client.MongoCollection::withReadPreference);
178+
}
179+
180+
}
181+
182+
}

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.stream.Stream;
2121

2222
import org.bson.Document;
23-
2423
import org.springframework.dao.IncorrectResultSizeDataAccessException;
2524
import org.springframework.data.mongodb.core.query.NearQuery;
2625
import org.springframework.data.mongodb.core.query.Query;
@@ -168,7 +167,8 @@ private List<T> doFind(@Nullable CursorPreparer preparer) {
168167
Document queryObject = query.getQueryObject();
169168
Document fieldsObject = query.getFieldsObject();
170169

171-
return template.doFind(getCollectionName(), queryObject, fieldsObject, domainType, returnType,
170+
return template.doFind(template.createDelegate(query), getCollectionName(), queryObject, fieldsObject, domainType,
171+
returnType,
172172
getCursorPreparer(query, preparer));
173173
}
174174

0 commit comments

Comments
 (0)