Skip to content

Commit a26e780

Browse files
mp911dechristophstrobl
authored andcommitted
Reduce allocations in query and update mapping.
Introduce EmptyDocument and utility methods in BsonUtils. Avoid entrySet and iterator creation for document iterations/inspections. Relates to: #3760 Original Pull Request: #3809
1 parent 8fb0e13 commit a26e780

File tree

15 files changed

+298
-67
lines changed

15 files changed

+298
-67
lines changed

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

+9
Original file line numberDiff line numberDiff line change
@@ -156,5 +156,14 @@ public Boolean isIsolated() {
156156
public List<ArrayFilter> getArrayFilters() {
157157
return delegate.getArrayFilters();
158158
}
159+
160+
/*
161+
* (non-Javadoc)
162+
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#hasArrayFilters()
163+
*/
164+
@Override
165+
public boolean hasArrayFilters() {
166+
return delegate.hasArrayFilters();
167+
}
159168
}
160169
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ class UpdateContext extends QueryContext {
613613

614614
UpdateContext(MappedDocument update, boolean upsert) {
615615

616-
super(new BasicQuery(new Document(BsonUtils.asMap(update.getIdFilter()))));
616+
super(new BasicQuery(BsonUtils.asDocument(update.getIdFilter())));
617617
this.multi = false;
618618
this.upsert = upsert;
619619
this.mappedDocument = update;

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public Object get(MongoPersistentProperty property) {
135135
*/
136136
@Nullable
137137
public Object getRawId(MongoPersistentEntity<?> entity) {
138-
return entity.hasIdProperty() ? get(entity.getRequiredIdProperty()) : BsonUtils.asMap(document).get("_id");
138+
return entity.hasIdProperty() ? get(entity.getRequiredIdProperty()) : BsonUtils.get(document, "_id");
139139
}
140140

141141
/**

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

+7-7
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import java.util.LinkedHashMap;
2626
import java.util.List;
2727
import java.util.Map;
28-
import java.util.Map.Entry;
2928
import java.util.Optional;
3029
import java.util.Set;
3130
import java.util.stream.Collectors;
@@ -1325,21 +1324,22 @@ protected Map<Object, Object> readMap(ConversionContext context, Bson bson, Type
13251324
return map;
13261325
}
13271326

1328-
for (Entry<String, Object> entry : sourceMap.entrySet()) {
1327+
sourceMap.forEach((k, v) -> {
13291328

1330-
if (typeMapper.isTypeKey(entry.getKey())) {
1331-
continue;
1329+
if (typeMapper.isTypeKey(k)) {
1330+
return;
13321331
}
13331332

1334-
Object key = potentiallyUnescapeMapKey(entry.getKey());
1333+
Object key = potentiallyUnescapeMapKey(k);
13351334

13361335
if (!rawKeyType.isAssignableFrom(key.getClass())) {
13371336
key = doConvert(key, rawKeyType);
13381337
}
13391338

1340-
Object value = entry.getValue();
1339+
Object value = v;
13411340
map.put(key, value == null ? value : context.convert(value, valueType));
1342-
}
1341+
1342+
});
13431343

13441344
return map;
13451345
}

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

+3
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ default Object convertId(@Nullable Object id, Class<?> targetType) {
140140
if (ObjectId.isValid(id.toString())) {
141141
return new ObjectId(id.toString());
142142
}
143+
144+
// avoid ConversionException as convertToMongoType will return String anyways.
145+
return id;
143146
}
144147
}
145148

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

+32-21
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,11 @@ public Document getMappedSort(Document sortObject, @Nullable MongoPersistentEnti
193193
Assert.notNull(sortObject, "SortObject must not be null!");
194194

195195
if (sortObject.isEmpty()) {
196-
return new Document();
196+
return BsonUtils.EMPTY_DOCUMENT;
197197
}
198198

199199
Document mappedSort = mapFieldsToPropertyNames(sortObject, entity);
200-
mapMetaAttributes(mappedSort, entity, MetaMapping.WHEN_PRESENT);
201-
return mappedSort;
200+
return mapMetaAttributes(mappedSort, entity, MetaMapping.WHEN_PRESENT);
202201
}
203202

204203
/**
@@ -215,42 +214,51 @@ public Document getMappedFields(Document fieldsObject, @Nullable MongoPersistent
215214
Assert.notNull(fieldsObject, "FieldsObject must not be null!");
216215

217216
Document mappedFields = mapFieldsToPropertyNames(fieldsObject, entity);
218-
mapMetaAttributes(mappedFields, entity, MetaMapping.FORCE);
219-
return mappedFields;
217+
return mapMetaAttributes(mappedFields, entity, MetaMapping.FORCE);
220218
}
221219

222220
private Document mapFieldsToPropertyNames(Document fields, @Nullable MongoPersistentEntity<?> entity) {
223221

224222
if (fields.isEmpty()) {
225-
return new Document();
223+
return BsonUtils.EMPTY_DOCUMENT;
226224

227225
}
228226
Document target = new Document();
229-
for (Map.Entry<String, Object> entry : BsonUtils.asMap(filterUnwrappedObjects(fields, entity)).entrySet()) {
230227

231-
Field field = createPropertyField(entity, entry.getKey(), mappingContext);
228+
BsonUtils.asMap(filterUnwrappedObjects(fields, entity)).forEach((k, v) -> {
229+
230+
Field field = createPropertyField(entity, k, mappingContext);
232231
if (field.getProperty() != null && field.getProperty().isUnwrapped()) {
233-
continue;
232+
return;
234233
}
235234

236-
target.put(field.getMappedKey(), entry.getValue());
237-
}
235+
target.put(field.getMappedKey(), v);
236+
});
237+
238238
return target;
239239
}
240240

241-
private void mapMetaAttributes(Document source, @Nullable MongoPersistentEntity<?> entity, MetaMapping metaMapping) {
241+
private Document mapMetaAttributes(Document source, @Nullable MongoPersistentEntity<?> entity,
242+
MetaMapping metaMapping) {
242243

243244
if (entity == null) {
244-
return;
245+
return source;
245246
}
246247

247248
if (entity.hasTextScoreProperty() && !MetaMapping.IGNORE.equals(metaMapping)) {
249+
250+
if (source == BsonUtils.EMPTY_DOCUMENT) {
251+
source = new Document();
252+
}
253+
248254
MongoPersistentProperty textScoreProperty = entity.getTextScoreProperty();
249255
if (MetaMapping.FORCE.equals(metaMapping)
250256
|| (MetaMapping.WHEN_PRESENT.equals(metaMapping) && source.containsKey(textScoreProperty.getFieldName()))) {
251257
source.putAll(getMappedTextScoreField(textScoreProperty));
252258
}
253259
}
260+
261+
return source;
254262
}
255263

256264
private Document filterUnwrappedObjects(Document fieldsObject, @Nullable MongoPersistentEntity<?> entity) {
@@ -679,7 +687,7 @@ protected final Entry<String, Object> createMapEntry(Field field, @Nullable Obje
679687
private Entry<String, Object> createMapEntry(String key, @Nullable Object value) {
680688

681689
Assert.hasText(key, "Key must not be null or empty!");
682-
return Collections.singletonMap(key, value).entrySet().iterator().next();
690+
return new AbstractMap.SimpleEntry<>(key, value);
683691
}
684692

685693
private Object createReferenceFor(Object source, MongoPersistentProperty property) {
@@ -733,13 +741,13 @@ protected boolean isNestedKeyword(@Nullable Object candidate) {
733741
return false;
734742
}
735743

736-
Set<String> keys = BsonUtils.asMap((Bson) candidate).keySet();
744+
Map<String, Object> map = BsonUtils.asMap((Bson) candidate);
737745

738-
if (keys.size() != 1) {
746+
if (map.size() != 1) {
739747
return false;
740748
}
741749

742-
return isKeyword(keys.iterator().next());
750+
return isKeyword(map.entrySet().iterator().next().getKey());
743751
}
744752

745753
/**
@@ -823,11 +831,14 @@ public Keyword(Bson source, String key) {
823831

824832
public Keyword(Bson bson) {
825833

826-
Set<String> keys = BsonUtils.asMap(bson).keySet();
827-
Assert.isTrue(keys.size() == 1, "Can only use a single value Document!");
834+
Map<String, Object> map = BsonUtils.asMap(bson);
835+
Assert.isTrue(map.size() == 1, "Can only use a single value Document!");
836+
837+
Set<Entry<String, Object>> entries = map.entrySet();
838+
Entry<String, Object> entry = entries.iterator().next();
828839

829-
this.key = keys.iterator().next();
830-
this.value = BsonUtils.get(bson, key);
840+
this.key = entry.getKey();
841+
this.value = entry.getValue();
831842
}
832843

833844
/**

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

+14-4
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ private enum MetaKey {
4949
}
5050
}
5151

52-
private final Map<String, Object> values = new LinkedHashMap<>(2);
53-
private final Set<CursorOption> flags = new LinkedHashSet<>();
52+
private Map<String, Object> values = Collections.emptyMap();
53+
private Set<CursorOption> flags = Collections.emptySet();
5454
private Integer cursorBatchSize;
5555
private Boolean allowDiskUse;
5656

@@ -63,8 +63,9 @@ public Meta() {}
6363
* @param source
6464
*/
6565
Meta(Meta source) {
66-
this.values.putAll(source.values);
67-
this.flags.addAll(source.flags);
66+
67+
this.values = new LinkedHashMap<>(source.values);
68+
this.flags = new LinkedHashSet<>(source.flags);
6869
this.cursorBatchSize = source.cursorBatchSize;
6970
this.allowDiskUse = source.allowDiskUse;
7071
}
@@ -158,6 +159,11 @@ public void setCursorBatchSize(int cursorBatchSize) {
158159
public boolean addFlag(CursorOption option) {
159160

160161
Assert.notNull(option, "CursorOption must not be null!");
162+
163+
if (this.flags == Collections.EMPTY_SET) {
164+
this.flags = new LinkedHashSet<>(2);
165+
}
166+
161167
return this.flags.add(option);
162168
}
163169

@@ -220,6 +226,10 @@ void setValue(String key, @Nullable Object value) {
220226

221227
Assert.hasText(key, "Meta key must not be 'null' or blank.");
222228

229+
if (values == Collections.EMPTY_MAP) {
230+
values = new LinkedHashMap<>(2);
231+
}
232+
223233
if (value == null || (value instanceof String && !StringUtils.hasText((String) value))) {
224234
this.values.remove(key);
225235
}

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

+26-7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.time.Duration;
2222
import java.util.ArrayList;
2323
import java.util.Arrays;
24+
import java.util.Collections;
2425
import java.util.HashSet;
2526
import java.util.LinkedHashMap;
2627
import java.util.List;
@@ -30,6 +31,7 @@
3031
import java.util.concurrent.TimeUnit;
3132

3233
import org.bson.Document;
34+
3335
import org.springframework.data.domain.Pageable;
3436
import org.springframework.data.domain.Sort;
3537
import org.springframework.data.domain.Sort.Order;
@@ -52,7 +54,7 @@ public class Query {
5254

5355
private static final String RESTRICTED_TYPES_KEY = "_$RESTRICTED_TYPES";
5456

55-
private final Set<Class<?>> restrictedTypes = new HashSet<>();
57+
private Set<Class<?>> restrictedTypes = Collections.emptySet();
5658
private final Map<String, CriteriaDefinition> criteria = new LinkedHashMap<>();
5759
private @Nullable Field fieldSpec = null;
5860
private Sort sort = Sort.unsorted();
@@ -235,8 +237,15 @@ public Query restrict(Class<?> type, Class<?>... additionalTypes) {
235237
Assert.notNull(type, "Type must not be null!");
236238
Assert.notNull(additionalTypes, "AdditionalTypes must not be null");
237239

240+
if (restrictedTypes == Collections.EMPTY_SET) {
241+
restrictedTypes = new HashSet<>(1 + additionalTypes.length);
242+
}
243+
238244
restrictedTypes.add(type);
239-
restrictedTypes.addAll(Arrays.asList(additionalTypes));
245+
246+
if (additionalTypes.length > 0) {
247+
restrictedTypes.addAll(Arrays.asList(additionalTypes));
248+
}
240249

241250
return this;
242251
}
@@ -246,6 +255,17 @@ public Query restrict(Class<?> type, Class<?>... additionalTypes) {
246255
*/
247256
public Document getQueryObject() {
248257

258+
if (criteria.isEmpty() && restrictedTypes.isEmpty()) {
259+
return BsonUtils.EMPTY_DOCUMENT;
260+
}
261+
262+
if (criteria.size() == 1 && restrictedTypes.isEmpty()) {
263+
264+
for (CriteriaDefinition definition : criteria.values()) {
265+
return definition.getCriteriaObject();
266+
}
267+
}
268+
249269
Document document = new Document();
250270

251271
for (CriteriaDefinition definition : criteria.values()) {
@@ -263,7 +283,7 @@ public Document getQueryObject() {
263283
* @return the field {@link Document}.
264284
*/
265285
public Document getFieldsObject() {
266-
return this.fieldSpec == null ? new Document() : fieldSpec.getFieldsObject();
286+
return this.fieldSpec == null ? BsonUtils.EMPTY_DOCUMENT : fieldSpec.getFieldsObject();
267287
}
268288

269289
/**
@@ -272,13 +292,12 @@ public Document getFieldsObject() {
272292
public Document getSortObject() {
273293

274294
if (this.sort.isUnsorted()) {
275-
return new Document();
295+
return BsonUtils.EMPTY_DOCUMENT;
276296
}
277297

278298
Document document = new Document();
279299

280-
this.sort.stream()//
281-
.forEach(order -> document.put(order.getProperty(), order.isAscending() ? 1 : -1));
300+
this.sort.forEach(order -> document.put(order.getProperty(), order.isAscending() ? 1 : -1));
282301

283302
return document;
284303
}
@@ -557,7 +576,7 @@ public boolean isSorted() {
557576
target.limit = source.getLimit();
558577
target.hint = source.getHint();
559578
target.collation = source.getCollation();
560-
target.restrictedTypes.addAll(source.getRestrictedTypes());
579+
target.restrictedTypes = new HashSet<>(source.getRestrictedTypes());
561580

562581
if (source.getMeta().hasValues()) {
563582
target.setMeta(new Meta(source.getMeta()));

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

+7-6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import java.util.Locale;
1919

2020
import org.bson.Document;
21+
22+
import org.springframework.data.mongodb.util.BsonUtils;
2123
import org.springframework.lang.Nullable;
2224

2325
/**
@@ -157,7 +159,7 @@ public Document getFieldsObject() {
157159
return super.getFieldsObject();
158160
}
159161

160-
Document fields = super.getFieldsObject();
162+
Document fields = BsonUtils.asMutableDocument(super.getFieldsObject());
161163

162164
fields.put(getScoreFieldName(), META_TEXT_SCORE);
163165
return fields;
@@ -170,15 +172,14 @@ public Document getFieldsObject() {
170172
@Override
171173
public Document getSortObject() {
172174

173-
Document sort = new Document();
174-
175175
if (this.sortByScore) {
176+
Document sort = new Document();
176177
sort.put(getScoreFieldName(), META_TEXT_SCORE);
178+
sort.putAll(super.getSortObject());
179+
return sort;
177180
}
178181

179-
sort.putAll(super.getSortObject());
180-
181-
return sort;
182+
return super.getSortObject();
182183
}
183184

184185
/*

0 commit comments

Comments
 (0)