Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,83 @@ public static long countDocuments(Object mongoCollection, @ThirdPartyCast(actual
return handleCount("countDocumentsClientSessionBsonCountOptions", mongoCollection, Arrays.asList(clientSession, filter, options), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "deleteOneBson", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.DeleteResult")
public static Object deleteOne(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter) {
return handleDelete("deleteOneBson", mongoCollection, Arrays.asList(filter), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "deleteOneBsonDeleteOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.DeleteResult")
public static Object deleteOne(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, @ThirdPartyCast(actualType = "com.mongodb.client.model.DeleteOptions") Object options) {
return handleDelete("deleteOneBsonDeleteOptions", mongoCollection, Arrays.asList(filter, options), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "deleteOneClientSessionBson", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.DeleteResult")
public static Object deleteOne_EM_0(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter) {
return handleDelete("deleteOneClientSessionBson", mongoCollection, Arrays.asList(clientSession, filter), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "deleteOneClientSessionBsonDeleteOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.DeleteResult")
public static Object deleteOne(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, @ThirdPartyCast(actualType = "com.mongodb.client.model.DeleteOptions") Object options) {
return handleDelete("deleteOneClientSessionBsonDeleteOptions", mongoCollection, Arrays.asList(clientSession, filter, options), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "deleteManyBson", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.DeleteResult")
public static Object deleteMany(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter) {
return handleDelete("deleteManyBson", mongoCollection, Arrays.asList(filter), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "deleteManyBsonDeleteOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.DeleteResult")
public static Object deleteMany(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, @ThirdPartyCast(actualType = "com.mongodb.client.model.DeleteOptions") Object options) {
return handleDelete("deleteManyBsonDeleteOptions", mongoCollection, Arrays.asList(filter, options), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "deleteManyClientSessionBson", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.DeleteResult")
public static Object deleteMany_EM_0(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter) {
return handleDelete("deleteManyClientSessionBson", mongoCollection, Arrays.asList(clientSession, filter), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "deleteManyClientSessionBsonDeleteOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.DeleteResult")
public static Object deleteMany(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, @ThirdPartyCast(actualType = "com.mongodb.client.model.DeleteOptions") Object options) {
return handleDelete("deleteManyClientSessionBsonDeleteOptions", mongoCollection, Arrays.asList(clientSession, filter, options), filter);
}

/**
* Unlike find, count is executed eagerly, so we know right away whether the filter was valid
* Processes the count handling based on the provided parameters.
*
* @param id The identifier used to locate the relevant resource.
* @param mongoCollection The collection object to interact with the database.
* @param args A list of additional arguments required for the operation.
* @param filter The filter criteria applied during the handling process.
* @return The calculated count as a long value.
*/
private static long handleCount(String id, Object mongoCollection, List<Object> args, Object filter) {
return (long) handleEagerQuery(id, mongoCollection, args, filter);
}

/**
* Handles the deletion operation for a specific document in the provided collection.
*
* @param id The identifier of the document to be deleted.
* @param mongoCollection The collection from which the document will be deleted.
* @param args A list of additional arguments required for the delete operation.
* @param filter The filter conditions to locate the document for deletion.
* @return The result of the delete operation, which can vary based on the implementation.
*/
private static Object handleDelete(String id, Object mongoCollection, List<Object> args, Object filter) {
return handleEagerQuery(id, mongoCollection, args, filter);
}

/**
* Unlike find, operations like count and delete are executed eagerly,
* so we know right away whether the filter was valid
*/
private static Object handleEagerQuery(String id, Object mongoCollection, List<Object> args, Object filter) {
long start = System.currentTimeMillis();
Method countMethod = retrieveFindMethod(id, mongoCollection);
Method method = retrieveFindMethod(id, mongoCollection);
try {
long result = (long) countMethod.invoke(mongoCollection, args.toArray());
Object result = method.invoke(mongoCollection, args.toArray());
long end = System.currentTimeMillis();
handleMongo(mongoCollection, filter, true, end - start);
handleMongo(mongoCollection, filter, true, end - start);
return result;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
Expand All @@ -98,7 +165,19 @@ private static long handleCount(String id, Object mongoCollection, List<Object>
}
}

private static Object handleFind(String id, Object mongoCollection, List<Object> args, Object query) {
/**
* Handles a lazy query execution on a MongoDB collection and ensures the query is valid
* by invoking the relevant "find" method and explicitly verifying the results.
*
* @param id The identifier used to retrieve the appropriate find method for the query.
* @param mongoCollection The MongoDB collection object on which the query is executed.
* @param args The list of arguments passed to the find method for the query execution.
* @param query The query object representing the query being executed.
* @return The result of the query execution, typically an iterable containing the query results.
* @throws RuntimeException If an error occurs during method invocation or if an exception is thrown
* by the query's hasNext() method.
*/
private static Object handleLazyQuery(String id, Object mongoCollection, List<Object> args, Object query) {
long start = System.currentTimeMillis();
try {
Method findMethod = retrieveFindMethod(id, mongoCollection);
Expand All @@ -124,6 +203,26 @@ private static Object handleFind(String id, Object mongoCollection, List<Object>
}
}


/**
* Handles the invocation of a "find" operation on the given MongoDB collection.
* This method retrieves the appropriate find method for the specified identifier
* and invokes it with the provided arguments. It also ensures that the query execution
* is valid by verifying the result through its iterator.
*
* @param id The identifier used to retrieve the appropriate "find" method.
* @param mongoCollection The MongoDB collection object on which the "find" operation is to be executed.
* @param args A list of arguments to be passed to the "find" method.
* @param query The query object associated with the "find" operation, used for logging and analysis.
* @return The result of the "find" operation, typically an iterable or cursor representing the query result.
* @throws RuntimeException If an IllegalAccessException or InvocationTargetException occurs,
* or if the root cause of the exception during invocation is a runtime exception.
*/
private static Object handleFind(String id, Object mongoCollection, List<Object> args, Object query) {
return handleLazyQuery(id, mongoCollection, args, query);
}


private static Method retrieveFindMethod(String id, Object mongoCollection) {
return getOriginal(singleton, id, mongoCollection);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoQueryException;
import com.mongodb.client.*;
import com.mongodb.client.model.DeleteOptions;
import com.mongodb.client.result.DeleteResult;
import org.bson.BsonDocument;
import org.bson.BsonDocumentWriter;
import org.bson.BsonWriter;
Expand Down Expand Up @@ -450,4 +452,172 @@ public void testCountDocumentsWithInvalidFilter() {
BsonDocument retrievedQuery = assertSingleRecordedCommand(false);
assertEquals(-1, retrievedQuery.getDocument("tags").getInt32("$size").getValue());
}

@Test
public void testDeleteOneWithFilter() {
final MongoCollection<Document> collection = getMongoCollection();
insertPerson("John Doe", 30);
insertPerson("Jane Doe", 25);

ExecutionTracer.setExecutingInitMongo(false);
DeleteResult result = (DeleteResult) MongoCollectionClassReplacement.deleteOne(collection, new Document("age", 30));

assertEquals(1, result.getDeletedCount());
assertEquals(1, collection.countDocuments());
BsonDocument retrievedQuery = assertSingleRecordedCommand(true);
assertEquals(30, retrievedQuery.getInt32("age").getValue());
}

@Test
public void testDeleteOneWithFilterNoMatch() {
final MongoCollection<Document> collection = getMongoCollection();

ExecutionTracer.setExecutingInitMongo(false);
DeleteResult result = (DeleteResult) MongoCollectionClassReplacement.deleteOne(collection, new Document("age", 99));

assertEquals(0, result.getDeletedCount());
BsonDocument retrievedQuery = assertSingleRecordedCommand(true);
assertEquals(99, retrievedQuery.getInt32("age").getValue());
}

@Test
public void testDeleteOneWithFilterAndOptions() {
final MongoCollection<Document> collection = getMongoCollection();
insertPerson("John Doe", 30);

ExecutionTracer.setExecutingInitMongo(false);
DeleteResult result = (DeleteResult) MongoCollectionClassReplacement.deleteOne(collection, new Document("age", 30), new DeleteOptions());

assertEquals(1, result.getDeletedCount());
BsonDocument retrievedQuery = assertSingleRecordedCommand(true);
assertEquals(30, retrievedQuery.getInt32("age").getValue());
}

@Test
public void testDeleteOneWithClientSessionAndFilter() {
try (ClientSession clientSession = mongoClient.startSession()) {
final MongoCollection<Document> collection = getMongoCollection();
insertPerson("John Doe", 23);

ExecutionTracer.setExecutingInitMongo(false);
DeleteResult result = (DeleteResult) MongoCollectionClassReplacement.deleteOne_EM_0(collection, clientSession, new Document("age", 23));

assertEquals(1, result.getDeletedCount());
BsonDocument retrievedQuery = assertSingleRecordedCommand(true);
assertEquals(23, retrievedQuery.getInt32("age").getValue());
}
}

@Test
public void testDeleteOneWithClientSessionFilterAndOptions() {
try (ClientSession clientSession = mongoClient.startSession()) {
final MongoCollection<Document> collection = getMongoCollection();
insertPerson("John Doe", 23);

ExecutionTracer.setExecutingInitMongo(false);
DeleteResult result = (DeleteResult) MongoCollectionClassReplacement.deleteOne(collection, clientSession, new Document("age", 23), new DeleteOptions());

assertEquals(1, result.getDeletedCount());
BsonDocument retrievedQuery = assertSingleRecordedCommand(true);
assertEquals(23, retrievedQuery.getInt32("age").getValue());
}
}

@Test
public void testDeleteOneWithInvalidFilter() {
final MongoCollection<Document> collection = getMongoCollection();
insertPerson("John Doe", 30);

Document invalidFilter = new Document("tags", new Document("$size", -1));

ExecutionTracer.setExecutingInitMongo(false);
assertThrows(com.mongodb.MongoException.class, () -> MongoCollectionClassReplacement.deleteOne(collection, invalidFilter));

BsonDocument retrievedQuery = assertSingleRecordedCommand(false);
assertEquals(-1, retrievedQuery.getDocument("tags").getInt32("$size").getValue());
}

@Test
public void testDeleteManyWithFilter() {
final MongoCollection<Document> collection = getMongoCollection();
insertPerson("John Doe", 30);
insertPerson("Jane Doe", 25);

ExecutionTracer.setExecutingInitMongo(false);
DeleteResult result = (DeleteResult) MongoCollectionClassReplacement.deleteMany(collection, new Document("age", 30));

assertEquals(1, result.getDeletedCount());
assertEquals(1, collection.countDocuments());
BsonDocument retrievedQuery = assertSingleRecordedCommand(true);
assertEquals(30, retrievedQuery.getInt32("age").getValue());
}

@Test
public void testDeleteManyWithFilterNoMatch() {
final MongoCollection<Document> collection = getMongoCollection();

ExecutionTracer.setExecutingInitMongo(false);
DeleteResult result = (DeleteResult) MongoCollectionClassReplacement.deleteMany(collection, new Document("age", 99));

assertEquals(0, result.getDeletedCount());
BsonDocument retrievedQuery = assertSingleRecordedCommand(true);
assertEquals(99, retrievedQuery.getInt32("age").getValue());
}

@Test
public void testDeleteManyWithFilterAndOptions() {
final MongoCollection<Document> collection = getMongoCollection();
insertPerson("John Doe", 30);

ExecutionTracer.setExecutingInitMongo(false);
DeleteResult result = (DeleteResult) MongoCollectionClassReplacement.deleteMany(collection, new Document("age", 30), new DeleteOptions());

assertEquals(1, result.getDeletedCount());
BsonDocument retrievedQuery = assertSingleRecordedCommand(true);
assertEquals(30, retrievedQuery.getInt32("age").getValue());
}

@Test
public void testDeleteManyWithClientSessionAndFilter() {
try (ClientSession clientSession = mongoClient.startSession()) {
final MongoCollection<Document> collection = getMongoCollection();
insertPerson("John Doe", 23);

ExecutionTracer.setExecutingInitMongo(false);
DeleteResult result = (DeleteResult) MongoCollectionClassReplacement.deleteMany_EM_0(collection, clientSession, new Document("age", 23));

assertEquals(1, result.getDeletedCount());
BsonDocument retrievedQuery = assertSingleRecordedCommand(true);
assertEquals(23, retrievedQuery.getInt32("age").getValue());
}
}

@Test
public void testDeleteManyWithClientSessionFilterAndOptions() {
try (ClientSession clientSession = mongoClient.startSession()) {
final MongoCollection<Document> collection = getMongoCollection();
insertPerson("John Doe", 23);

ExecutionTracer.setExecutingInitMongo(false);
DeleteResult result = (DeleteResult) MongoCollectionClassReplacement.deleteMany(collection, clientSession, new Document("age", 23), new DeleteOptions());

assertEquals(1, result.getDeletedCount());
BsonDocument retrievedQuery = assertSingleRecordedCommand(true);
assertEquals(23, retrievedQuery.getInt32("age").getValue());
}
}

@Test
public void testDeleteManyWithInvalidFilter() {
final MongoCollection<Document> collection = getMongoCollection();
insertPerson("John Doe", 30);

Document invalidFilter = new Document("tags", new Document("$size", -1));

ExecutionTracer.setExecutingInitMongo(false);
assertThrows(com.mongodb.MongoException.class, () -> MongoCollectionClassReplacement.deleteMany(collection, invalidFilter));

BsonDocument retrievedQuery = assertSingleRecordedCommand(false);
assertEquals(-1, retrievedQuery.getDocument("tags").getInt32("$size").getValue());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.mongo.delete;

import com.mongo.SwaggerConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@EnableSwagger2
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class MongoDeleteApp extends SwaggerConfiguration {
public MongoDeleteApp() {
super("mongodelete");
}

public static void main(String[] args) {
SpringApplication.run(MongoDeleteApp.class, args);
}

}
Loading
Loading