fix(mongodb): support shell delete commands

This commit is contained in:
t8y2 2026-07-01 20:23:51 +08:00
parent ff1a0f5eda
commit 0c4ae7caf6
7 changed files with 68 additions and 5 deletions

View File

@ -48,6 +48,7 @@ public final class AgentProtocol {
public static final String MONGO_METHOD_UPDATE_DOCUMENT = "update_document";
public static final String MONGO_METHOD_UPDATE_DOCUMENTS = "update_documents";
public static final String MONGO_METHOD_DELETE_DOCUMENT = "delete_document";
public static final String MONGO_METHOD_DELETE_DOCUMENTS = "delete_documents";
public static final String KV_METHOD_LIST_PREFIX = "kv_list_prefix";
public static final String KV_METHOD_GET = "kv_get";
@ -123,7 +124,9 @@ public final class AgentProtocol {
MONGO_METHOD_SERVER_VERSION,
MONGO_METHOD_INSERT_DOCUMENT,
MONGO_METHOD_UPDATE_DOCUMENT,
MONGO_METHOD_DELETE_DOCUMENT
MONGO_METHOD_UPDATE_DOCUMENTS,
MONGO_METHOD_DELETE_DOCUMENT,
MONGO_METHOD_DELETE_DOCUMENTS
));
public static final List<String> KV_METHODS = Collections.unmodifiableList(Arrays.asList(

View File

@ -72,7 +72,9 @@
"server_version",
"insert_document",
"update_document",
"delete_document"
"update_documents",
"delete_document",
"delete_documents"
],
"kvMethods": [
"kv_list_prefix",

View File

@ -536,6 +536,21 @@ public final class MongoAgent {
return Collections.singletonMap("deleted_count", result.getDeletedCount());
}
private static Object deleteDocuments(JsonObject params) {
MongoClient c = requireClient();
String database = params.get("database").getAsString();
String collection = params.get("collection").getAsString();
String filterJson = params.get("filter_json").getAsString();
boolean many = params.get("many").getAsBoolean();
var col = c.getDatabase(database).getCollection(collection);
Document filter = documentForWrite(filterJson);
// Shell deleteOne/deleteMany use a filter document, unlike the row-view
// delete path which always targets a single _id.
var result = many ? col.deleteMany(filter) : col.deleteOne(filter);
return Collections.singletonMap("deleted_count", result.getDeletedCount());
}
private static Map<String, Object> bsonToJson(Document doc) {
Map<String, Object> result = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : doc.entrySet()) {
@ -662,6 +677,7 @@ public final class MongoAgent {
case AgentProtocol.MONGO_METHOD_UPDATE_DOCUMENT -> updateDocument(params);
case AgentProtocol.MONGO_METHOD_UPDATE_DOCUMENTS -> updateDocuments(params);
case AgentProtocol.MONGO_METHOD_DELETE_DOCUMENT -> deleteDocument(params);
case AgentProtocol.MONGO_METHOD_DELETE_DOCUMENTS -> deleteDocuments(params);
case AgentProtocol.METHOD_DISCONNECT, AgentProtocol.METHOD_SHUTDOWN -> {
if (client != null) {
client.close();

View File

@ -149,6 +149,20 @@ class MongoAgentTest {
assertFalse(json.getAsJsonObject("error").get("message").getAsString().contains("Unknown method"));
}
@Test
void deleteDocumentsMethodIsRecognizedOverJsonRpc() {
String response = MongoAgent.handleRequest(
"{\"jsonrpc\":\"2.0\",\"id\":11,\"method\":\"delete_documents\","
+ "\"params\":{\"database\":\"app\",\"collection\":\"orders\","
+ "\"filter_json\":\"{\\\"status\\\":\\\"draft\\\"}\",\"many\":true}}");
JsonObject json = JsonParser.parseString(response).getAsJsonObject();
assertEquals(11, json.get("id").getAsInt());
assertEquals("Not connected", json.getAsJsonObject("error").get("message").getAsString());
assertFalse(json.getAsJsonObject("error").get("message").getAsString().contains("Unknown method"));
assertTrue(AgentProtocol.MONGO_LEGACY_METHODS.contains(AgentProtocol.MONGO_METHOD_DELETE_DOCUMENTS));
}
@Test
void extractsServerVersionFromBuildInfo() {
assertEquals("4.4.29", MongoAgent.serverVersionFromBuildInfo(new Document("version", "4.4.29")));

View File

@ -72,7 +72,9 @@
"server_version",
"insert_document",
"update_document",
"delete_document"
"update_documents",
"delete_document",
"delete_documents"
],
"kvMethods": [
"kv_list_prefix",

View File

@ -249,10 +249,11 @@ pub enum MongoAgentMethod {
UpdateDocument,
UpdateDocuments,
DeleteDocument,
DeleteDocuments,
}
impl MongoAgentMethod {
pub const ALL: [Self; 8] = [
pub const ALL: [Self; 10] = [
Self::ListDatabases,
Self::ListCollections,
Self::FindDocuments,
@ -260,7 +261,9 @@ impl MongoAgentMethod {
Self::ServerVersion,
Self::InsertDocument,
Self::UpdateDocument,
Self::UpdateDocuments,
Self::DeleteDocument,
Self::DeleteDocuments,
];
pub fn as_str(self) -> &'static str {
@ -274,6 +277,7 @@ impl MongoAgentMethod {
Self::UpdateDocument => "update_document",
Self::UpdateDocuments => "update_documents",
Self::DeleteDocument => "delete_document",
Self::DeleteDocuments => "delete_documents",
}
}
}
@ -972,6 +976,13 @@ impl AgentDriverClient {
self.call_mongo_method(MongoAgentMethod::DeleteDocument, params).await
}
pub async fn mongo_delete_documents<T: DeserializeOwned + Send + 'static>(
&mut self,
params: Value,
) -> Result<T, String> {
self.call_mongo_method(MongoAgentMethod::DeleteDocuments, params).await
}
pub async fn try_optional_handshake(&mut self, app_version: &str) -> Option<AgentHandshake> {
match self.call_method::<AgentHandshake>(AgentMethod::Handshake, agent_handshake_params(app_version)).await {
Ok(handshake) => {
@ -1495,7 +1506,9 @@ mod tests {
assert_eq!(MongoAgentMethod::ServerVersion.as_str(), "server_version");
assert_eq!(MongoAgentMethod::InsertDocument.as_str(), "insert_document");
assert_eq!(MongoAgentMethod::UpdateDocument.as_str(), "update_document");
assert_eq!(MongoAgentMethod::UpdateDocuments.as_str(), "update_documents");
assert_eq!(MongoAgentMethod::DeleteDocument.as_str(), "delete_document");
assert_eq!(MongoAgentMethod::DeleteDocuments.as_str(), "delete_documents");
}
#[test]
@ -1537,7 +1550,9 @@ mod tests {
let _mongo_server_version = AgentDriverClient::mongo_server_version::<serde_json::Value>;
let _mongo_insert_document = AgentDriverClient::mongo_insert_document::<serde_json::Value>;
let _mongo_update_document = AgentDriverClient::mongo_update_document::<serde_json::Value>;
let _mongo_update_documents = AgentDriverClient::mongo_update_documents::<serde_json::Value>;
let _mongo_delete_document = AgentDriverClient::mongo_delete_document::<serde_json::Value>;
let _mongo_delete_documents = AgentDriverClient::mongo_delete_documents::<serde_json::Value>;
}
#[test]

View File

@ -318,7 +318,18 @@ pub async fn mongo_delete_documents_core(
PoolKind::MongoDb(client) => {
mongo_driver::delete_documents(client, database, collection, filter_json, many).await
}
PoolKind::Agent(_) => Err("MongoDB legacy agent does not support bulk deleteOne/deleteMany writes".to_string()),
PoolKind::Agent(client) => {
let mut client = client.lock().await;
let result: serde_json::Value = client
.mongo_delete_documents(serde_json::json!({
"database": database,
"collection": collection,
"filter_json": filter_json,
"many": many,
}))
.await?;
Ok(result.get("deleted_count").and_then(|v| v.as_u64()).unwrap_or(0))
}
_ => Err("Not a MongoDB connection".to_string()),
}
}