Skip to content

Commit 95667ed

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. Original Pull Request: #3809
1 parent c1a52de commit 95667ed

File tree

15 files changed

+299
-65
lines changed

15 files changed

+299
-65
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

@@ -1192,21 +1191,22 @@ protected Map<Object, Object> readMap(ConversionContext context, Bson bson, Type
11921191
return map;
11931192
}
11941193

1195-
for (Entry<String, Object> entry : sourceMap.entrySet()) {
1194+
sourceMap.forEach((k, v) -> {
11961195

1197-
if (typeMapper.isTypeKey(entry.getKey())) {
1198-
continue;
1196+
if (typeMapper.isTypeKey(k)) {
1197+
return;
11991198
}
12001199

1201-
Object key = potentiallyUnescapeMapKey(entry.getKey());
1200+
Object key = potentiallyUnescapeMapKey(k);
12021201

12031202
if (!rawKeyType.isAssignableFrom(key.getClass())) {
12041203
key = doConvert(key, rawKeyType);
12051204
}
12061205

1207-
Object value = entry.getValue();
1206+
Object value = v;
12081207
map.put(key, value == null ? value : context.convert(value, valueType));
1209-
}
1208+
1209+
});
12101210

12111211
return map;
12121212
}

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
@@ -192,12 +192,11 @@ public Document getMappedSort(Document sortObject, @Nullable MongoPersistentEnti
192192
Assert.notNull(sortObject, "SortObject must not be null!");
193193

194194
if (sortObject.isEmpty()) {
195-
return new Document();
195+
return BsonUtils.EMPTY_DOCUMENT;
196196
}
197197

198198
Document mappedSort = mapFieldsToPropertyNames(sortObject, entity);
199-
mapMetaAttributes(mappedSort, entity, MetaMapping.WHEN_PRESENT);
200-
return mappedSort;
199+
return mapMetaAttributes(mappedSort, entity, MetaMapping.WHEN_PRESENT);
201200
}
202201

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

216215
Document mappedFields = mapFieldsToPropertyNames(fieldsObject, entity);
217-
mapMetaAttributes(mappedFields, entity, MetaMapping.FORCE);
218-
return mappedFields;
216+
return mapMetaAttributes(mappedFields, entity, MetaMapping.FORCE);
219217
}
220218

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

223221
if (fields.isEmpty()) {
224-
return new Document();
222+
return BsonUtils.EMPTY_DOCUMENT;
225223

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

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

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

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

242243
if (entity == null) {
243-
return;
244+
return source;
244245
}
245246

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

255263
private Document filterUnwrappedObjects(Document fieldsObject, @Nullable MongoPersistentEntity<?> entity) {
@@ -678,7 +686,7 @@ protected final Entry<String, Object> createMapEntry(Field field, @Nullable Obje
678686
private Entry<String, Object> createMapEntry(String key, @Nullable Object value) {
679687

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

684692
private DBRef createDbRefFor(Object source, MongoPersistentProperty property) {
@@ -727,13 +735,13 @@ protected boolean isNestedKeyword(@Nullable Object candidate) {
727735
return false;
728736
}
729737

730-
Set<String> keys = BsonUtils.asMap((Bson) candidate).keySet();
738+
Map<String, Object> map = BsonUtils.asMap((Bson) candidate);
731739

732-
if (keys.size() != 1) {
740+
if (map.size() != 1) {
733741
return false;
734742
}
735743

736-
return isKeyword(keys.iterator().next());
744+
return isKeyword(map.entrySet().iterator().next().getKey());
737745
}
738746

739747
/**
@@ -817,11 +825,14 @@ public Keyword(Bson source, String key) {
817825

818826
public Keyword(Bson bson) {
819827

820-
Set<String> keys = BsonUtils.asMap(bson).keySet();
821-
Assert.isTrue(keys.size() == 1, "Can only use a single value Document!");
828+
Map<String, Object> map = BsonUtils.asMap(bson);
829+
Assert.isTrue(map.size() == 1, "Can only use a single value Document!");
830+
831+
Set<Entry<String, Object>> entries = map.entrySet();
832+
Entry<String, Object> entry = entries.iterator().next();
822833

823-
this.key = keys.iterator().next();
824-
this.value = BsonUtils.get(bson, key);
834+
this.key = entry.getKey();
835+
this.value = entry.getValue();
825836
}
826837

827838
/**

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)