From aca97d7343558453fe3730b3026d5c4d741bddc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=86=B2?= Date: Thu, 12 Oct 2023 12:09:35 +0800 Subject: [PATCH 1/5] Deep encode key when use object id --- .../vertx/ext/mongo/impl/MongoClientImpl.java | 47 ++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java b/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java index 4e224efa..f335fd4a 100644 --- a/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java +++ b/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java @@ -55,7 +55,9 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -905,15 +907,48 @@ private AggregatePublisher doAggregate(final String collection, fina return aggregate; } - JsonObject encodeKeyWhenUseObjectId(JsonObject json) { - if (!useObjectId) return json; + JsonArray encodeKeyWhenUseObjectId(JsonArray arr) { + if(!useObjectId) return arr; - Object idString = json.getValue(ID_FIELD, null); - if (idString instanceof String && ObjectId.isValid((String) idString)) { - json.put(ID_FIELD, new JsonObject().put(JsonObjectCodec.OID_FIELD, idString)); + JsonArray newArr = new JsonArray(new ArrayList<>(arr.size())); + + for (Object item : arr) { + if (item instanceof JsonArray) { + newArr.add(encodeKeyWhenUseObjectId((JsonArray) item)); + } else if (item instanceof JsonObject) { + newArr.add(encodeKeyWhenUseObjectId((JsonObject) item)); + } else { + newArr.add(item); + } + // we don't handle cases that value instanceof Map or List } - return json; + return newArr; + } + + JsonObject encodeKeyWhenUseObjectId(JsonObject json) { + if(!useObjectId) return json; + + JsonObject newJson = new JsonObject(new LinkedHashMap<>(json.size())); + + for (Map.Entry entry : json) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (key.equals(ID_FIELD) + && value instanceof String + && ObjectId.isValid((String) value)) { + newJson.put(key, new JsonObject().put(JsonObjectCodec.OID_FIELD, value)); + } else if (value instanceof JsonObject) { + newJson.put(key, encodeKeyWhenUseObjectId((JsonObject) value)); + } else if (value instanceof JsonArray) { + newJson.put(key, encodeKeyWhenUseObjectId((JsonArray) value)); + } else { + newJson.put(key, value); + } + // we don't handle cases that value instanceof Map or List + } + + return newJson; } private JsonObject decodeKeyWhenUseObjectId(JsonObject json) { From 931355ccfe43446cbbfbaef33aba5fe273a906ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=86=B2?= Date: Thu, 12 Oct 2023 12:23:40 +0800 Subject: [PATCH 2/5] Use deepEncodeKeyWhenUseObjectId only in query or filter --- .../vertx/ext/mongo/impl/MongoClientImpl.java | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java b/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java index f335fd4a..01182472 100644 --- a/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java +++ b/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java @@ -209,7 +209,7 @@ public Future close() { requireNonNull(options, OPTIONS_CANNOT_BE_NULL); MongoCollection coll = getCollection(collection, options.getWriteOption()); - Bson bquery = wrap(encodeKeyWhenUseObjectId(query)); + Bson bquery = wrap(deepEncodeKeyWhenUseObjectId(query)); Bson bupdate = wrap(encodeKeyWhenUseObjectId(generateIdIfNeeded(query, update, options))); com.mongodb.client.model.UpdateOptions updateOptions = new com.mongodb.client.model.UpdateOptions().upsert(options.isUpsert()); @@ -248,7 +248,7 @@ public Future close() { requireNonNull(options, OPTIONS_CANNOT_BE_NULL); MongoCollection coll = getCollection(collection, options.getWriteOption()); - Bson bquery = wrap(encodeKeyWhenUseObjectId(query)); + Bson bquery = wrap(deepEncodeKeyWhenUseObjectId(query)); List bpipeline = new ArrayList<>(pipeline.size()); for (int i=0 ; i coll = getCollection(collection, options.getWriteOption()); - Bson bquery = wrap(encodeKeyWhenUseObjectId(query)); + Bson bquery = wrap(deepEncodeKeyWhenUseObjectId(query)); com.mongodb.client.model.ReplaceOptions replaceOptions = new com.mongodb.client.model.ReplaceOptions().upsert(options.isUpsert()); if (options.getHint() != null) { replaceOptions.hint(wrap(options.getHint())); @@ -334,7 +334,7 @@ public Future> findWithOptions(String collection, JsonObject qu requireNonNull(query, QUERY_CANNOT_BE_NULL); Promise> promise = vertx.promise(); - doFind(collection, encodeKeyWhenUseObjectId(query), options) + doFind(collection, deepEncodeKeyWhenUseObjectId(query), options) .subscribe(new MappingAndBufferingSubscriber<>(this::decodeKeyWhenUseObjectId, promise)); return promise.future(); } @@ -357,7 +357,7 @@ public ReadStream findBatchWithOptions(String collection, JsonObject requireNonNull(collection, COLLECTION_CANNOT_BE_NULL); requireNonNull(query, QUERY_CANNOT_BE_NULL); - JsonObject encodedQuery = encodeKeyWhenUseObjectId(query); + JsonObject encodedQuery = deepEncodeKeyWhenUseObjectId(query); Bson bquery = wrap(encodedQuery); Bson bfields = wrap(fields); @@ -379,7 +379,7 @@ public ReadStream findBatchWithOptions(String collection, JsonObject requireNonNull(findOptions, FIND_OPTIONS_CANNOT_BE_NULL); requireNonNull(updateOptions, "update options cannot be null"); - JsonObject encodedQuery = encodeKeyWhenUseObjectId(query); + JsonObject encodedQuery = deepEncodeKeyWhenUseObjectId(query); Bson bquery = wrap(encodedQuery); Bson bupdate = wrap(update); @@ -430,7 +430,7 @@ public ReadStream findBatchWithOptions(String collection, JsonObject requireNonNull(findOptions, FIND_OPTIONS_CANNOT_BE_NULL); requireNonNull(updateOptions, "update options cannot be null"); - JsonObject encodedQuery = encodeKeyWhenUseObjectId(query); + JsonObject encodedQuery = deepEncodeKeyWhenUseObjectId(query); Bson bquery = wrap(encodedQuery); FindOneAndReplaceOptions foarOptions = new FindOneAndReplaceOptions(); @@ -472,7 +472,7 @@ public ReadStream findBatchWithOptions(String collection, JsonObject requireNonNull(query, QUERY_CANNOT_BE_NULL); requireNonNull(findOptions, FIND_OPTIONS_CANNOT_BE_NULL); - JsonObject encodedQuery = encodeKeyWhenUseObjectId(query); + JsonObject encodedQuery = deepEncodeKeyWhenUseObjectId(query); Bson bquery = wrap(encodedQuery); FindOneAndDeleteOptions foadOptions = new FindOneAndDeleteOptions(); @@ -506,7 +506,7 @@ public Future countWithOptions(String collection, JsonObject query, CountO requireNonNull(collection, COLLECTION_CANNOT_BE_NULL); requireNonNull(query, QUERY_CANNOT_BE_NULL); - Bson bquery = wrap(encodeKeyWhenUseObjectId(query)); + Bson bquery = wrap(deepEncodeKeyWhenUseObjectId(query)); MongoCollection coll = getCollection(collection); Promise promise = vertx.promise(); Publisher countPublisher = countOptions != null @@ -527,7 +527,7 @@ public Future countWithOptions(String collection, JsonObject query, CountO requireNonNull(query, QUERY_CANNOT_BE_NULL); MongoCollection coll = getCollection(collection, writeOption); - Bson bquery = wrap(encodeKeyWhenUseObjectId(query)); + Bson bquery = wrap(deepEncodeKeyWhenUseObjectId(query)); Promise promise = vertx.promise(); coll.deleteMany(bquery).subscribe(new SingleResultSubscriber<>(promise)); return promise.future().map(Utils::toMongoClientDeleteResult); @@ -544,7 +544,7 @@ public Future countWithOptions(String collection, JsonObject query, CountO requireNonNull(query, QUERY_CANNOT_BE_NULL); MongoCollection coll = getCollection(collection, writeOption); - Bson bquery = wrap(encodeKeyWhenUseObjectId(query)); + Bson bquery = wrap(deepEncodeKeyWhenUseObjectId(query)); Promise promise = vertx.promise(); coll.deleteOne(bquery).subscribe(new SingleResultSubscriber<>(promise)); return promise.future().map(Utils::toMongoClientDeleteResult); @@ -573,7 +573,7 @@ private List> convertBulkOperations(List o for (BulkOperation bulkOperation : operations) { switch (bulkOperation.getType()) { case DELETE: - Bson bsonFilter = toBson(encodeKeyWhenUseObjectId(bulkOperation.getFilter())); + Bson bsonFilter = toBson(deepEncodeKeyWhenUseObjectId(bulkOperation.getFilter())); DeleteOptions deleteOptions = new DeleteOptions(); if (bulkOperation.getHint() != null) { deleteOptions.hint(toBson(bulkOperation.getHint())); @@ -591,7 +591,7 @@ private List> convertBulkOperations(List o } break; case INSERT: - result.add(new InsertOneModel<>(encodeKeyWhenUseObjectId(bulkOperation.getDocument()))); + result.add(new InsertOneModel<>(deepEncodeKeyWhenUseObjectId(bulkOperation.getDocument()))); break; case REPLACE: ReplaceOptions replaceOptions = new ReplaceOptions(); @@ -604,11 +604,11 @@ private List> convertBulkOperations(List o if (bulkOperation.getHintString() != null && !bulkOperation.getHintString().isEmpty()) { replaceOptions.hintString(bulkOperation.getHintString()); } - result.add(new ReplaceOneModel<>(toBson(encodeKeyWhenUseObjectId(bulkOperation.getFilter())), bulkOperation.getDocument(), + result.add(new ReplaceOneModel<>(toBson(deepEncodeKeyWhenUseObjectId(bulkOperation.getFilter())), bulkOperation.getDocument(), replaceOptions.upsert(bulkOperation.isUpsert()))); break; case UPDATE: - Bson filter = toBson(encodeKeyWhenUseObjectId(bulkOperation.getFilter())); + Bson filter = toBson(deepEncodeKeyWhenUseObjectId(bulkOperation.getFilter())); Bson document = toBson(encodeKeyWhenUseObjectId(bulkOperation.getDocument())); com.mongodb.client.model.UpdateOptions updateOptions = new com.mongodb.client.model.UpdateOptions() .upsert(bulkOperation.isUpsert()); @@ -874,7 +874,7 @@ private DistinctPublisher findDistinctValuesWithQuery(String collection, Stri requireNonNull(fieldName, FIELD_NAME_CANNOT_BE_NULL); requireNonNull(query, QUERY_CANNOT_BE_NULL); - JsonObject encodedQuery = encodeKeyWhenUseObjectId(query); + JsonObject encodedQuery = deepEncodeKeyWhenUseObjectId(query); Bson bquery = wrap(encodedQuery); @@ -907,16 +907,18 @@ private AggregatePublisher doAggregate(final String collection, fina return aggregate; } - JsonArray encodeKeyWhenUseObjectId(JsonArray arr) { + + + JsonArray deepEncodeKeyWhenUseObjectId(JsonArray arr) { if(!useObjectId) return arr; JsonArray newArr = new JsonArray(new ArrayList<>(arr.size())); for (Object item : arr) { if (item instanceof JsonArray) { - newArr.add(encodeKeyWhenUseObjectId((JsonArray) item)); + newArr.add(deepEncodeKeyWhenUseObjectId((JsonArray) item)); } else if (item instanceof JsonObject) { - newArr.add(encodeKeyWhenUseObjectId((JsonObject) item)); + newArr.add(deepEncodeKeyWhenUseObjectId((JsonObject) item)); } else { newArr.add(item); } @@ -926,7 +928,7 @@ JsonArray encodeKeyWhenUseObjectId(JsonArray arr) { return newArr; } - JsonObject encodeKeyWhenUseObjectId(JsonObject json) { + JsonObject deepEncodeKeyWhenUseObjectId(JsonObject json) { if(!useObjectId) return json; JsonObject newJson = new JsonObject(new LinkedHashMap<>(json.size())); @@ -939,9 +941,9 @@ JsonObject encodeKeyWhenUseObjectId(JsonObject json) { && ObjectId.isValid((String) value)) { newJson.put(key, new JsonObject().put(JsonObjectCodec.OID_FIELD, value)); } else if (value instanceof JsonObject) { - newJson.put(key, encodeKeyWhenUseObjectId((JsonObject) value)); + newJson.put(key, deepEncodeKeyWhenUseObjectId((JsonObject) value)); } else if (value instanceof JsonArray) { - newJson.put(key, encodeKeyWhenUseObjectId((JsonArray) value)); + newJson.put(key, deepEncodeKeyWhenUseObjectId((JsonArray) value)); } else { newJson.put(key, value); } @@ -951,6 +953,18 @@ JsonObject encodeKeyWhenUseObjectId(JsonObject json) { return newJson; } + JsonObject encodeKeyWhenUseObjectId(JsonObject json) { + if (!useObjectId) + return json; + + Object idString = json.getValue(ID_FIELD, null); + if (idString instanceof String && ObjectId.isValid((String) idString)) { + json.put(ID_FIELD, new JsonObject().put(JsonObjectCodec.OID_FIELD, idString)); + } + + return json; + } + private JsonObject decodeKeyWhenUseObjectId(JsonObject json) { if (!useObjectId) return json; @@ -967,7 +981,7 @@ private JsonObject decodeKeyWhenUseObjectId(JsonObject json) { private FindPublisher doFind(String collection, JsonObject query, FindOptions options) { MongoCollection coll = getCollection(collection); - Bson bquery = wrap(encodeKeyWhenUseObjectId(query)); + Bson bquery = wrap(deepEncodeKeyWhenUseObjectId(query)); FindPublisher find = coll.find(bquery, JsonObject.class); if (options.getLimit() != -1) { find.limit(options.getLimit()); From 49adb45181ac144b9050c5119dd9896f9eaf3af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=86=B2?= Date: Tue, 24 Oct 2023 14:04:24 +0800 Subject: [PATCH 3/5] Fix deepEncodeKeyWhenUseObjectId should not use on bulk INSERT --- src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java b/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java index 01182472..3dfa1334 100644 --- a/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java +++ b/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java @@ -591,7 +591,7 @@ private List> convertBulkOperations(List o } break; case INSERT: - result.add(new InsertOneModel<>(deepEncodeKeyWhenUseObjectId(bulkOperation.getDocument()))); + result.add(new InsertOneModel<>(encodeKeyWhenUseObjectId(bulkOperation.getDocument()))); break; case REPLACE: ReplaceOptions replaceOptions = new ReplaceOptions(); From 90538503336309e53ae0d7d359c6948336b5aba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=86=B2?= Date: Tue, 24 Oct 2023 14:05:09 +0800 Subject: [PATCH 4/5] Add unit tests for deepEncodeKeyWhenUseObjectId for MongoClient --- .../mongo/MongoClientWithObjectIdTest.java | 56 +++++++++++++++++++ .../io/vertx/ext/mongo/MongoTestBase.java | 33 ++++++++++- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/vertx/ext/mongo/MongoClientWithObjectIdTest.java b/src/test/java/io/vertx/ext/mongo/MongoClientWithObjectIdTest.java index b8f031fa..a373d158 100644 --- a/src/test/java/io/vertx/ext/mongo/MongoClientWithObjectIdTest.java +++ b/src/test/java/io/vertx/ext/mongo/MongoClientWithObjectIdTest.java @@ -1,5 +1,6 @@ package io.vertx.ext.mongo; +import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import org.bson.types.ObjectId; import org.junit.Test; @@ -77,6 +78,32 @@ public void testFindOneReturnsStringId() throws Exception { await(); } + @Test + public void testFindOneWithNestedQueryReturnsStringId() throws Exception { + String collection = randomCollection(); + mongoClient.createCollection(collection).onComplete(onSuccess(res -> { + JsonObject orig = createDoc(); + JsonObject doc = orig.copy(); + String objectId = getObjectId(doc); + JsonObject query = JsonObject.of("$and", JsonArray.of( + JsonObject.of("foo", "bar"), + JsonObject.of("_id", objectId))); + mongoClient.insert(collection, doc).onComplete(onSuccess(id -> { + // no auto-generated objectId from mongo + assertNull(id); + mongoClient.findOne(collection, query, null).onComplete(onSuccess(obj -> { + assertTrue(obj.containsKey("_id")); + assertTrue(obj.getValue("_id") instanceof String); + obj.remove("_id"); + // nested "_id" will not be modified when insert + assertEquals(orig, obj); + testComplete(); + })); + })); + })); + await(); + } + @Test public void testFindOneReturnsNothing() throws Exception { String collection = randomCollection(); @@ -116,6 +143,35 @@ public void testFindReturnsStringId() throws Exception { await(); } + + @Test + public void testFindWithNestedQueryReturnsStringId() throws Exception { + String collection = randomCollection(); + mongoClient.createCollection(collection).onComplete(onSuccess(res -> { + JsonObject orig = createDoc(); + JsonObject doc = orig.copy(); + String objectId = getObjectId(doc); + JsonObject query = JsonObject.of("$and", JsonArray.of( + JsonObject.of("foo", "bar"), + JsonObject.of("_id", objectId))); + mongoClient.insert(collection, doc).onComplete(onSuccess(id -> { + // no auto-generated objectId from mongo + assertNull(id); + mongoClient.find(collection, query).onComplete(onSuccess(list -> { + assertTrue(list.size() == 1); + JsonObject obj = list.get(0); + assertTrue(obj.containsKey("_id")); + assertTrue(obj.getValue("_id") instanceof String); + obj.remove("_id"); + // nested "_id" will not be modified when insert + assertEquals(orig, obj); + testComplete(); + })); + })); + })); + await(); + } + @Test @Override public void testInsertPreexistingObjectID() throws Exception { diff --git a/src/test/java/io/vertx/ext/mongo/MongoTestBase.java b/src/test/java/io/vertx/ext/mongo/MongoTestBase.java index 486733ed..b325267d 100644 --- a/src/test/java/io/vertx/ext/mongo/MongoTestBase.java +++ b/src/test/java/io/vertx/ext/mongo/MongoTestBase.java @@ -182,7 +182,14 @@ protected JsonObject createDoc() { .put("myarr", new JsonArray() .add("blah") .add(true) - .add(312))); + .add(312))) + .put("nested_id1", new JsonObject() + .put("_id", new ObjectId().toHexString())) + .put("nested_id2", new JsonArray() + .add(new JsonObject() + .put("_id", new ObjectId().toHexString())) + .add(new JsonObject() + .put("_id", new ObjectId().toHexString()))); } protected JsonObject createDoc(int num) { @@ -210,4 +217,28 @@ protected JsonObject createDocWithAmbiguitiesDependingOnLocale(int num) { .put("longval", 123456789L).put("dblval", 1.23); } + // WARN: try to getObjectId from doc will generate new objectId on doc if not exists + protected String getObjectId(JsonObject doc) { + Object idVal = doc.getValue("_id"); + + // auto generate when not exists + if(idVal == null) { + String _id = new ObjectId().toHexString(); + doc.put("_id", JsonObject.of("$oid", _id)); + return _id; + } + + // return string + if(idVal instanceof String) { + return (String) idVal; + } + + // return $oid from ObjectId object + if(idVal instanceof JsonObject) { + return ((JsonObject) idVal).getString("$oid"); + } + + return null; + } + } From 469603c2eb955106ef874ced5a691772d48a89e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=86=B2?= Date: Wed, 8 Nov 2023 19:06:44 +0800 Subject: [PATCH 5/5] Handle Map and List for deepEncodeKeyWhenUseObjectId --- .../vertx/ext/mongo/impl/MongoClientImpl.java | 11 +++++-- .../mongo/MongoClientWithObjectIdTest.java | 33 +++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java b/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java index 3dfa1334..30e800b6 100644 --- a/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java +++ b/src/main/java/io/vertx/ext/mongo/impl/MongoClientImpl.java @@ -908,7 +908,6 @@ private AggregatePublisher doAggregate(final String collection, fina } - JsonArray deepEncodeKeyWhenUseObjectId(JsonArray arr) { if(!useObjectId) return arr; @@ -917,12 +916,15 @@ JsonArray deepEncodeKeyWhenUseObjectId(JsonArray arr) { for (Object item : arr) { if (item instanceof JsonArray) { newArr.add(deepEncodeKeyWhenUseObjectId((JsonArray) item)); + } else if(item instanceof List) { + newArr.add(deepEncodeKeyWhenUseObjectId(new JsonArray((List) item))); } else if (item instanceof JsonObject) { newArr.add(deepEncodeKeyWhenUseObjectId((JsonObject) item)); + } else if (item instanceof Map) { + newArr.add(deepEncodeKeyWhenUseObjectId(new JsonObject((Map) item))); } else { newArr.add(item); } - // we don't handle cases that value instanceof Map or List } return newArr; @@ -942,12 +944,15 @@ JsonObject deepEncodeKeyWhenUseObjectId(JsonObject json) { newJson.put(key, new JsonObject().put(JsonObjectCodec.OID_FIELD, value)); } else if (value instanceof JsonObject) { newJson.put(key, deepEncodeKeyWhenUseObjectId((JsonObject) value)); + } else if (value instanceof Map) { + newJson.put(key, deepEncodeKeyWhenUseObjectId(new JsonObject((Map) value))); } else if (value instanceof JsonArray) { newJson.put(key, deepEncodeKeyWhenUseObjectId((JsonArray) value)); + } else if (value instanceof List) { + newJson.put(key, deepEncodeKeyWhenUseObjectId(new JsonArray((List) value))); } else { newJson.put(key, value); } - // we don't handle cases that value instanceof Map or List } return newJson; diff --git a/src/test/java/io/vertx/ext/mongo/MongoClientWithObjectIdTest.java b/src/test/java/io/vertx/ext/mongo/MongoClientWithObjectIdTest.java index a373d158..f322a499 100644 --- a/src/test/java/io/vertx/ext/mongo/MongoClientWithObjectIdTest.java +++ b/src/test/java/io/vertx/ext/mongo/MongoClientWithObjectIdTest.java @@ -5,6 +5,9 @@ import org.bson.types.ObjectId; import org.junit.Test; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.CountDownLatch; import static io.vertx.ext.mongo.WriteOption.ACKNOWLEDGED; @@ -172,6 +175,36 @@ public void testFindWithNestedQueryReturnsStringId() throws Exception { await(); } + @Test + public void testFindWithNestedQueryWithListMapReturnsStringId() throws Exception { + String collection = randomCollection(); + mongoClient.createCollection(collection).onComplete(onSuccess(res -> { + JsonObject orig = createDoc(); + JsonObject doc = orig.copy(); + String objectId = getObjectId(doc); + Map m1 = new HashMap<>(); + m1.put("foo", "bar"); + Map m2 = new HashMap<>(); + m2.put("_id", objectId); + JsonObject query = JsonObject.of("$and", Arrays.asList(m1, m2)); + mongoClient.insert(collection, doc).onComplete(onSuccess(id -> { + // no auto-generated objectId from mongo + assertNull(id); + mongoClient.find(collection, query).onComplete(onSuccess(list -> { + assertTrue(list.size() == 1); + JsonObject obj = list.get(0); + assertTrue(obj.containsKey("_id")); + assertTrue(obj.getValue("_id") instanceof String); + obj.remove("_id"); + // nested "_id" will not be modified when insert + assertEquals(orig, obj); + testComplete(); + })); + })); + })); + await(); + } + @Test @Override public void testInsertPreexistingObjectID() throws Exception {