fix(mongodb): decode extended JSON document ids

This commit is contained in:
t8y2 2026-07-24 14:30:34 +08:00
parent 6b98c7ebf1
commit 5a47084c7b
No known key found for this signature in database
2 changed files with 44 additions and 14 deletions

View File

@ -641,12 +641,9 @@ public final class MongoAgent {
return stringId;
}
String trimmed = id.trim();
if (isNumberLongIdWrapper(trimmed)) {
try {
return Document.parse("{\"_id\":" + trimmed + "}").get("_id");
} catch (Exception e) {
// Fall through to the legacy ObjectId/string handling below.
}
Object extendedJsonId = parseExtendedJsonId(trimmed);
if (extendedJsonId != null) {
return extendedJsonId;
}
try {
return new ObjectId(id);
@ -668,20 +665,21 @@ public final class MongoAgent {
}
}
private static boolean isNumberLongIdWrapper(String value) {
private static Object parseExtendedJsonId(String value) {
try {
JsonElement parsed = JsonParser.parseString(value);
if (!parsed.isJsonObject()) {
return false;
return null;
}
JsonObject wrapper = parsed.getAsJsonObject();
JsonElement numberLong = wrapper.get("$numberLong");
return wrapper.size() == 1
&& numberLong != null
&& numberLong.isJsonPrimitive()
&& numberLong.getAsJsonPrimitive().isString();
if (wrapper.size() != 1 || (!wrapper.has("$oid") && !wrapper.has("$numberLong"))) {
return null;
}
// The document browser preserves BSON _id types as Extended JSON;
// decode only known wrappers so JSON-looking string IDs stay strings.
return Document.parse("{\"_id\":" + value + "}").get("_id");
} catch (Exception e) {
return false;
return null;
}
}
@ -698,9 +696,22 @@ public final class MongoAgent {
var result = isUpdateOperatorDocument(newDoc)
? col.updateOne(filter, newDoc)
: col.replaceOne(filter, replacementDocument(newDoc));
requireMatchedDocument(id, result);
return Collections.singletonMap("modified_count", result.getModifiedCount());
}
static void requireMatchedDocument(String id, UpdateResult result) {
if (result.getMatchedCount() == 0) {
throw new IllegalStateException(noMatchingDocumentError(id));
}
}
private static String noMatchingDocumentError(String id) {
String display = decodeStringDocumentId(id);
return "No document matched _id " + (display == null ? id : display)
+ ". It may have been deleted or its _id changed since the query ran.";
}
private static Object updateDocuments(JsonObject params) {
MongoClient c = requireClient();
String database = params.get("database").getAsString();

View File

@ -179,10 +179,12 @@ class MongoAgentTest {
void preservesLongDocumentIdTypeForGridUpdates() {
Object id = MongoAgent.convertDocumentFieldValue("_id", 2_048_938_405_781_032_962L);
Object value = MongoAgent.convertDocumentFieldValue("snowflake", 2_048_938_405_781_032_962L);
ObjectId objectId = new ObjectId("507f1f77bcf86cd799439011");
assertEquals(Collections.singletonMap("$numberLong", "2048938405781032962"), id);
assertEquals("2048938405781032962", value);
assertEquals(2_048_938_405_781_032_962L, MongoAgent.parseId("{\"$numberLong\":\"2048938405781032962\"}"));
assertEquals(objectId, MongoAgent.parseId("{\"$oid\":\"507f1f77bcf86cd799439011\"}"));
}
@Test
@ -196,6 +198,23 @@ class MongoAgentTest {
assertEquals("{\"$numberLong\":\"invalid\"}", MongoAgent.parseId("{\"$numberLong\":\"invalid\"}"));
}
@Test
void documentUpdateDistinguishesNoMatchFromUnchangedValue() {
MongoAgent.requireMatchedDocument(
"{\"$oid\":\"507f1f77bcf86cd799439011\"}",
UpdateResult.acknowledged(1, 0L, null)
);
IllegalStateException error = assertThrows(
IllegalStateException.class,
() -> MongoAgent.requireMatchedDocument(
"{\"$oid\":\"507f1f77bcf86cd799439012\"}",
UpdateResult.acknowledged(0, 0L, null)
)
);
assertTrue(error.getMessage().startsWith("No document matched _id"));
}
@Test
void serverVersionMethodIsRecognizedOverJsonRpc() {
String response = MongoAgent.handleRequest(