Skip to content

Commit c11e0e6

Browse files
committed
improved deserialize of responses of CRUD operations on multiple documents
1 parent 0cd35d9 commit c11e0e6

File tree

11 files changed

+141
-153
lines changed

11 files changed

+141
-153
lines changed

core/src/main/java/com/arangodb/entity/MultiDocumentEntity.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,17 @@
2020

2121
package com.arangodb.entity;
2222

23+
import java.util.ArrayList;
2324
import java.util.List;
2425

2526
/**
2627
* @author Mark Vollmary
2728
*/
2829
public final class MultiDocumentEntity<E> {
2930

30-
private List<E> documents;
31-
private List<ErrorEntity> errors;
32-
private List<Object> documentsAndErrors;
31+
private List<E> documents = new ArrayList<>();
32+
private List<ErrorEntity> errors = new ArrayList<>();
33+
private List<Object> documentsAndErrors = new ArrayList<>();
3334
private boolean isPotentialDirtyRead = false;
3435

3536
public MultiDocumentEntity() {

core/src/main/java/com/arangodb/internal/InternalArangoCollection.java

Lines changed: 15 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import java.lang.reflect.Type;
3333
import java.util.ArrayList;
3434
import java.util.Collection;
35-
import java.util.List;
3635

3736
import static com.arangodb.internal.serde.SerdeUtils.constructParametricType;
3837

@@ -109,31 +108,11 @@ private InternalRequest createInsertDocumentRequest(final DocumentCreateOptions
109108
return request;
110109
}
111110

112-
// TODO: avoid serialization-deserialization round-trip
113111
protected <T> ResponseDeserializer<MultiDocumentEntity<DocumentCreateEntity<T>>> insertDocumentsResponseDeserializer(Class<T> userDataClass) {
114112
return (response) -> {
115-
final MultiDocumentEntity<DocumentCreateEntity<T>> multiDocument = new MultiDocumentEntity<>();
116-
final List<DocumentCreateEntity<T>> docs = new ArrayList<>();
117-
final List<ErrorEntity> errors = new ArrayList<>();
118-
final List<Object> documentsAndErrors = new ArrayList<>();
119-
final JsonNode body = getSerde().parse(response.getBody());
120-
for (final JsonNode next : body) {
121-
JsonNode isError = next.get(ArangoResponseField.ERROR_FIELD_NAME);
122-
if (isError != null && isError.booleanValue()) {
123-
final ErrorEntity error = getSerde().deserialize(next, ErrorEntity.class);
124-
errors.add(error);
125-
documentsAndErrors.add(error);
126-
} else {
127-
Type type = constructParametricType(DocumentCreateEntity.class, userDataClass);
128-
final DocumentCreateEntity<T> doc = getSerde().deserialize(next, type);
129-
docs.add(doc);
130-
documentsAndErrors.add(doc);
131-
}
132-
}
133-
multiDocument.setDocuments(docs);
134-
multiDocument.setErrors(errors);
135-
multiDocument.setDocumentsAndErrors(documentsAndErrors);
136-
return multiDocument;
113+
Type type = constructParametricType(MultiDocumentEntity.class,
114+
constructParametricType(DocumentCreateEntity.class, userDataClass));
115+
return getSerde().deserialize(response.getBody(), type);
137116
};
138117
}
139118

@@ -185,32 +164,12 @@ protected InternalRequest getDocumentsRequest(final Iterable<String> keys, final
185164
return request;
186165
}
187166

188-
// TODO: avoid serialization-deserialization round-trip
189-
protected <T> ResponseDeserializer<MultiDocumentEntity<T>> getDocumentsResponseDeserializer(
190-
final Class<T> type) {
167+
protected <T> ResponseDeserializer<MultiDocumentEntity<T>> getDocumentsResponseDeserializer(final Class<T> type) {
191168
return (response) -> {
192-
final MultiDocumentEntity<T> multiDocument = new MultiDocumentEntity<>();
169+
MultiDocumentEntity<T> multiDocument = getSerde().deserialize(response.getBody(),
170+
constructParametricType(MultiDocumentEntity.class, type));
193171
boolean potentialDirtyRead = Boolean.parseBoolean(response.getMeta("X-Arango-Potential-Dirty-Read"));
194172
multiDocument.setPotentialDirtyRead(potentialDirtyRead);
195-
final List<T> docs = new ArrayList<>();
196-
final List<ErrorEntity> errors = new ArrayList<>();
197-
final List<Object> documentsAndErrors = new ArrayList<>();
198-
final JsonNode body = getSerde().parse(response.getBody());
199-
for (final JsonNode next : body) {
200-
JsonNode isError = next.get(ArangoResponseField.ERROR_FIELD_NAME);
201-
if (isError != null && isError.booleanValue()) {
202-
final ErrorEntity error = getSerde().deserialize(next, ErrorEntity.class);
203-
errors.add(error);
204-
documentsAndErrors.add(error);
205-
} else {
206-
final T doc = getSerde().deserializeUserData(getSerde().serialize(next), type);
207-
docs.add(doc);
208-
documentsAndErrors.add(doc);
209-
}
210-
}
211-
multiDocument.setDocuments(docs);
212-
multiDocument.setErrors(errors);
213-
multiDocument.setDocumentsAndErrors(documentsAndErrors);
214173
return multiDocument;
215174
};
216175
}
@@ -249,32 +208,12 @@ private InternalRequest createReplaceDocumentRequest(final DocumentReplaceOption
249208
return request;
250209
}
251210

252-
// TODO: avoid serialization-deserialization round-trip
253211
protected <T> ResponseDeserializer<MultiDocumentEntity<DocumentUpdateEntity<T>>> replaceDocumentsResponseDeserializer(
254212
final Class<T> returnType) {
255213
return (response) -> {
256-
final MultiDocumentEntity<DocumentUpdateEntity<T>> multiDocument = new MultiDocumentEntity<>();
257-
final List<DocumentUpdateEntity<T>> docs = new ArrayList<>();
258-
final List<ErrorEntity> errors = new ArrayList<>();
259-
final List<Object> documentsAndErrors = new ArrayList<>();
260-
final JsonNode body = getSerde().parse(response.getBody());
261-
for (final JsonNode next : body) {
262-
JsonNode isError = next.get(ArangoResponseField.ERROR_FIELD_NAME);
263-
if (isError != null && isError.booleanValue()) {
264-
final ErrorEntity error = getSerde().deserialize(next, ErrorEntity.class);
265-
errors.add(error);
266-
documentsAndErrors.add(error);
267-
} else {
268-
Type type = constructParametricType(DocumentUpdateEntity.class, returnType);
269-
final DocumentUpdateEntity<T> doc = getSerde().deserialize(next, type);
270-
docs.add(doc);
271-
documentsAndErrors.add(doc);
272-
}
273-
}
274-
multiDocument.setDocuments(docs);
275-
multiDocument.setErrors(errors);
276-
multiDocument.setDocumentsAndErrors(documentsAndErrors);
277-
return multiDocument;
214+
Type type = constructParametricType(MultiDocumentEntity.class,
215+
constructParametricType(DocumentUpdateEntity.class, returnType));
216+
return getSerde().deserialize(response.getBody(), type);
278217
};
279218
}
280219

@@ -313,32 +252,12 @@ private InternalRequest createUpdateDocumentRequest(final DocumentUpdateOptions
313252
return request;
314253
}
315254

316-
// TODO: avoid serialization-deserialization round-trip
317255
protected <T> ResponseDeserializer<MultiDocumentEntity<DocumentUpdateEntity<T>>> updateDocumentsResponseDeserializer(
318256
final Class<T> returnType) {
319257
return (response) -> {
320-
final MultiDocumentEntity<DocumentUpdateEntity<T>> multiDocument = new MultiDocumentEntity<>();
321-
final List<DocumentUpdateEntity<T>> docs = new ArrayList<>();
322-
final List<ErrorEntity> errors = new ArrayList<>();
323-
final List<Object> documentsAndErrors = new ArrayList<>();
324-
final JsonNode body = getSerde().parse(response.getBody());
325-
for (final JsonNode next : body) {
326-
JsonNode isError = next.get(ArangoResponseField.ERROR_FIELD_NAME);
327-
if (isError != null && isError.booleanValue()) {
328-
final ErrorEntity error = getSerde().deserialize(next, ErrorEntity.class);
329-
errors.add(error);
330-
documentsAndErrors.add(error);
331-
} else {
332-
Type type = constructParametricType(DocumentUpdateEntity.class, returnType);
333-
final DocumentUpdateEntity<T> doc = getSerde().deserialize(next, type);
334-
docs.add(doc);
335-
documentsAndErrors.add(doc);
336-
}
337-
}
338-
multiDocument.setDocuments(docs);
339-
multiDocument.setErrors(errors);
340-
multiDocument.setDocumentsAndErrors(documentsAndErrors);
341-
return multiDocument;
258+
Type type = constructParametricType(MultiDocumentEntity.class,
259+
constructParametricType(DocumentUpdateEntity.class, returnType));
260+
return getSerde().deserialize(response.getBody(), type);
342261
};
343262
}
344263

@@ -371,32 +290,12 @@ private InternalRequest createDeleteDocumentRequest(final DocumentDeleteOptions
371290
return request;
372291
}
373292

374-
// TODO: avoid serialization-deserialization round-trip
375293
protected <T> ResponseDeserializer<MultiDocumentEntity<DocumentDeleteEntity<T>>> deleteDocumentsResponseDeserializer(
376294
final Class<T> userDataClass) {
377295
return (response) -> {
378-
final MultiDocumentEntity<DocumentDeleteEntity<T>> multiDocument = new MultiDocumentEntity<>();
379-
final List<DocumentDeleteEntity<T>> docs = new ArrayList<>();
380-
final List<ErrorEntity> errors = new ArrayList<>();
381-
final List<Object> documentsAndErrors = new ArrayList<>();
382-
final JsonNode body = getSerde().parse(response.getBody());
383-
for (final JsonNode next : body) {
384-
JsonNode isError = next.get(ArangoResponseField.ERROR_FIELD_NAME);
385-
if (isError != null && isError.booleanValue()) {
386-
final ErrorEntity error = getSerde().deserialize(next, ErrorEntity.class);
387-
errors.add(error);
388-
documentsAndErrors.add(error);
389-
} else {
390-
Type type = constructParametricType(DocumentDeleteEntity.class, userDataClass);
391-
final DocumentDeleteEntity<T> doc = getSerde().deserialize(next, type);
392-
docs.add(doc);
393-
documentsAndErrors.add(doc);
394-
}
395-
}
396-
multiDocument.setDocuments(docs);
397-
multiDocument.setErrors(errors);
398-
multiDocument.setDocumentsAndErrors(documentsAndErrors);
399-
return multiDocument;
296+
Type type = constructParametricType(MultiDocumentEntity.class,
297+
constructParametricType(DocumentDeleteEntity.class, userDataClass));
298+
return getSerde().deserialize(response.getBody(), type);
400299
};
401300
}
402301

core/src/main/java/com/arangodb/internal/serde/InternalDeserializers.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ public RawJson deserialize(JsonParser p, DeserializationContext ctxt) throws IOE
3434
if (JsonFactory.FORMAT_NAME_JSON.equals(p.getCodec().getFactory().getFormatName())) {
3535
return RawJson.of(new String(SerdeUtils.extractBytes(p), StandardCharsets.UTF_8));
3636
} else {
37-
// TODO: compare perfs using ByteArrayOutputStream and StringWriter
3837
StringWriter w = new StringWriter();
3938
try (JsonGenerator gen = SerdeUtils.INSTANCE.getJsonMapper().createGenerator(w)) {
4039
gen.copyCurrentStructure(p);

core/src/main/java/com/arangodb/internal/serde/InternalModule.java

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.arangodb.entity.CollectionStatus;
44
import com.arangodb.entity.CollectionType;
55
import com.arangodb.entity.InvertedIndexPrimarySort;
6+
import com.arangodb.entity.MultiDocumentEntity;
67
import com.arangodb.entity.ReplicationFactor;
78
import com.arangodb.util.RawBytes;
89
import com.arangodb.util.RawJson;
@@ -11,15 +12,12 @@
1112
import com.fasterxml.jackson.databind.Module;
1213
import com.fasterxml.jackson.databind.module.SimpleModule;
1314

14-
import java.util.function.Supplier;
15+
class InternalModule {
1516

16-
enum InternalModule implements Supplier<Module> {
17-
INSTANCE;
17+
static Module get(InternalSerde serde) {
18+
SimpleModule module = new SimpleModule();
1819

19-
private final SimpleModule module;
20-
21-
InternalModule() {
22-
module = new SimpleModule();
20+
module.addDeserializer(MultiDocumentEntity.class, new MultiDocumentEntityDeserializer(serde));
2321

2422
module.addSerializer(RawJson.class, InternalSerializers.RAW_JSON_SERIALIZER);
2523
module.addSerializer(InternalRequest.class, InternalSerializers.REQUEST);
@@ -32,11 +30,7 @@ enum InternalModule implements Supplier<Module> {
3230
module.addDeserializer(ReplicationFactor.class, InternalDeserializers.REPLICATION_FACTOR);
3331
module.addDeserializer(InternalResponse.class, InternalDeserializers.RESPONSE);
3432
module.addDeserializer(InvertedIndexPrimarySort.Field.class, InternalDeserializers.INVERTED_INDEX_PRIMARY_SORT_FIELD);
35-
}
3633

37-
@Override
38-
public Module get() {
3934
return module;
4035
}
41-
4236
}

core/src/main/java/com/arangodb/internal/serde/InternalSerde.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.arangodb.arch.UsedInApi;
44
import com.arangodb.serde.ArangoSerde;
55
import com.arangodb.ContentType;
6+
import com.fasterxml.jackson.core.JsonParser;
7+
import com.fasterxml.jackson.databind.JavaType;
68
import com.fasterxml.jackson.databind.JsonNode;
79

810
import java.lang.reflect.Type;
@@ -59,14 +61,6 @@ default <T> T deserialize(JsonNode node, Class<T> clazz) {
5961
*/
6062
<T> T deserialize(JsonNode node, Type type);
6163

62-
/**
63-
* Parses the content.
64-
*
65-
* @param content VPack or byte encoded JSON string
66-
* @return root of the parsed tree
67-
*/
68-
JsonNode parse(byte[] content);
69-
7064
/**
7165
* Parses the content at json pointer.
7266
*
@@ -136,6 +130,16 @@ default <T> T deserialize(byte[] content, String jsonPointer, Type type) {
136130
*/
137131
<T> T deserializeUserData(byte[] content, Type type);
138132

133+
/**
134+
* Deserializes the parsed json node and binds it to the target data type.
135+
* The parser is not closed.
136+
*
137+
* @param parser json parser
138+
* @param clazz class of target data type
139+
* @return deserialized object
140+
*/
141+
<T> T deserializeUserData(JsonParser parser, JavaType clazz);
142+
139143
/**
140144
* @return the user serde
141145
*/

core/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.fasterxml.jackson.core.JsonProcessingException;
1313
import com.fasterxml.jackson.core.JsonToken;
1414
import com.fasterxml.jackson.databind.DeserializationFeature;
15+
import com.fasterxml.jackson.databind.JavaType;
1516
import com.fasterxml.jackson.databind.JsonNode;
1617
import com.fasterxml.jackson.databind.Module;
1718
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -39,7 +40,7 @@ final class InternalSerdeImpl implements InternalSerde {
3940
mapper.deactivateDefaultTyping();
4041
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
4142
mapper.enable(JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION);
42-
mapper.registerModule(InternalModule.INSTANCE.get());
43+
mapper.registerModule(InternalModule.get(this));
4344
if (protocolModule != null) {
4445
mapper.registerModule(protocolModule);
4546
}
@@ -113,16 +114,6 @@ public byte[] extract(final byte[] content, final String jsonPointer) {
113114
}
114115
}
115116

116-
// TODO: remove
117-
@Override
118-
public JsonNode parse(byte[] content) {
119-
try {
120-
return mapper.readTree(content);
121-
} catch (IOException e) {
122-
throw ArangoDBException.of(e);
123-
}
124-
}
125-
126117
@Override
127118
public JsonNode parse(byte[] content, String jsonPointer) {
128119
try {
@@ -184,6 +175,19 @@ public <T> T deserializeUserData(byte[] content, Type type) {
184175
}
185176
}
186177

178+
@Override
179+
public <T> T deserializeUserData(JsonParser parser, JavaType clazz) {
180+
try {
181+
if (SerdeUtils.isManagedClass(clazz.getRawClass())) {
182+
return mapper.readerFor(clazz).readValue(parser);
183+
} else {
184+
return deserializeUserData(extractBytes(parser), clazz);
185+
}
186+
} catch (IOException e) {
187+
throw ArangoDBException.of(e);
188+
}
189+
}
190+
187191
@Override
188192
public ArangoSerde getUserSerde() {
189193
return userSerde;

0 commit comments

Comments
 (0)