From 8e429b0ba461a67fdd4eb381c206cb8b60792213 Mon Sep 17 00:00:00 2001 From: Jason King <837490+jsonking@users.noreply.github.com> Date: Sat, 6 Feb 2021 13:36:55 +0000 Subject: [PATCH] Issue #90 Convert InsertMany to execute as a runCommand --- .../statement/InsertManyStatement.java | 45 +++++++---------- .../mongodb/statement/InsertOneStatement.java | 26 +--------- .../statement/InsertManyStatementIT.java | 50 ++++++++++++++++--- 3 files changed, 63 insertions(+), 58 deletions(-) diff --git a/src/main/java/liquibase/ext/mongodb/statement/InsertManyStatement.java b/src/main/java/liquibase/ext/mongodb/statement/InsertManyStatement.java index 9ef18ab6..5acd7518 100644 --- a/src/main/java/liquibase/ext/mongodb/statement/InsertManyStatement.java +++ b/src/main/java/liquibase/ext/mongodb/statement/InsertManyStatement.java @@ -20,9 +20,6 @@ * #L% */ -import com.mongodb.client.MongoCollection; -import liquibase.ext.mongodb.database.MongoConnection; -import liquibase.nosql.statement.NoSqlExecuteStatement; import lombok.EqualsAndHashCode; import lombok.Getter; import org.bson.Document; @@ -30,16 +27,20 @@ import java.util.ArrayList; import java.util.List; -import static java.util.Optional.ofNullable; +import static java.util.Objects.nonNull; import static liquibase.ext.mongodb.statement.BsonUtils.orEmptyDocument; import static liquibase.ext.mongodb.statement.BsonUtils.orEmptyList; +/** + * Inserts many documents via the database runCommand method + * For a list of supported options see the reference page: + * https://docs.mongodb.com/manual/reference/command/insert/ + */ @Getter @EqualsAndHashCode(callSuper = true) -public class InsertManyStatement extends AbstractCollectionStatement - implements NoSqlExecuteStatement { +public class InsertManyStatement extends AbstractRunCommandStatement { - public static final String COMMAND_NAME = "insertMany"; + public static final String RUN_COMMAND_NAME = "insert"; private final List documents; private final Document options; @@ -49,34 +50,22 @@ public InsertManyStatement(final String collectionName, final String documents, } public InsertManyStatement(final String collectionName, final List documents, final Document options) { - super(collectionName); + super(BsonUtils.toCommand(RUN_COMMAND_NAME, collectionName, combine(documents, options))); this.documents = documents; this.options = options; } - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public String toJs() { - return - "db." + - getCollectionName() + - "." + - getCommandName() + - "(" + - ofNullable(documents).map(List::toString).orElse(null) + - ", " + - ofNullable(options).map(Document::toJson).orElse(null) + - ");"; + private static Document combine(final List documents, final Document options) { + final Document combined = new Document(BsonUtils.DOCUMENTS, documents); + if (nonNull(options)) { + combined.putAll(options); + } + return combined; } @Override - public void execute(final MongoConnection connection) { - final MongoCollection collection = connection.getDatabase().getCollection(collectionName); - collection.insertMany(documents); + public String getRunCommandName() { + return RUN_COMMAND_NAME; } } diff --git a/src/main/java/liquibase/ext/mongodb/statement/InsertOneStatement.java b/src/main/java/liquibase/ext/mongodb/statement/InsertOneStatement.java index 8da7e61f..5b3bd383 100644 --- a/src/main/java/liquibase/ext/mongodb/statement/InsertOneStatement.java +++ b/src/main/java/liquibase/ext/mongodb/statement/InsertOneStatement.java @@ -24,7 +24,6 @@ import lombok.Getter; import org.bson.Document; -import static java.util.Objects.nonNull; import static java.util.Collections.singletonList; import static liquibase.ext.mongodb.statement.BsonUtils.orEmptyDocument; @@ -35,34 +34,13 @@ */ @Getter @EqualsAndHashCode(callSuper = true) -public class InsertOneStatement extends AbstractRunCommandStatement { - - public static final String RUN_COMMAND_NAME = "insert"; +public class InsertOneStatement extends InsertManyStatement { public InsertOneStatement(final String collectionName, final String document, final String options) { this(collectionName, orEmptyDocument(document), orEmptyDocument(options)); } public InsertOneStatement(final String collectionName, final Document document, final Document options) { - super(BsonUtils.toCommand(RUN_COMMAND_NAME, collectionName, combine(document, options))); - } - - private static Document combine(final Document document, final Document options) { - final Document combined = new Document(BsonUtils.DOCUMENTS, singletonList(document)); - if (nonNull(options)) { - combined.putAll(options); - } - return combined; - } - - /** - * Returns the RunCommand command name. - * - * @return the run command as this is not used and not required for a generic RunCommandStatement - * @see Database Commands - */ - @Override - public String getRunCommandName() { - return RUN_COMMAND_NAME; + super(collectionName, singletonList(document), options); } } diff --git a/src/test/java/liquibase/ext/mongodb/statement/InsertManyStatementIT.java b/src/test/java/liquibase/ext/mongodb/statement/InsertManyStatementIT.java index b71040da..1056d0f8 100644 --- a/src/test/java/liquibase/ext/mongodb/statement/InsertManyStatementIT.java +++ b/src/test/java/liquibase/ext/mongodb/statement/InsertManyStatementIT.java @@ -20,27 +20,51 @@ * #L% */ +import com.mongodb.MongoException; import com.mongodb.client.MongoDatabase; import liquibase.ext.AbstractMongoIntegrationTest; +import lombok.SneakyThrows; import org.bson.Document; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; +import static liquibase.ext.mongodb.TestUtils.formatDoubleQuoted; import static liquibase.ext.mongodb.TestUtils.COLLECTION_NAME_1; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; class InsertManyStatementIT extends AbstractMongoIntegrationTest { + private String collectionName; + + @BeforeEach + public void createCollectionName() { + collectionName = COLLECTION_NAME_1 + System.nanoTime(); + } + @Test void toStringTest() { - final InsertManyStatement statement = new InsertManyStatement(COLLECTION_NAME_1, Collections.emptyList(), new Document()); + + String expected = formatDoubleQuoted( + "db.runCommand({'insert': '%s', " + + "'documents': [{'key1': 'value1'}, {'key1': 'value2'}], " + + "'ordered': false});", collectionName); + + final InsertManyStatement statement = new InsertManyStatement( + collectionName, + Arrays.asList( + new Document("key1", "value1"), + new Document("key1", "value2")), + new Document("ordered", false)); assertThat(statement.toJs()) .isEqualTo(statement.toString()) - .isEqualTo("db.collectionName.insertMany([], {});"); + .isEqualTo(expected); } @Test @@ -50,9 +74,9 @@ void executeForList() { .mapToObj(id -> Collections.singletonMap("id", (Object) id)) .map(Document::new) .collect(Collectors.toList()); - new InsertManyStatement(COLLECTION_NAME_1, testObjects, new Document()).execute(connection); + new InsertManyStatement(collectionName, testObjects, new Document()).execute(connection); - assertThat(database.getCollection(COLLECTION_NAME_1).find()) + assertThat(database.getCollection(collectionName).find()) .hasSize(5); } @@ -64,9 +88,23 @@ void executeForString() { .map(Document::new) .map(Document::toJson) .collect(Collectors.joining(",", "[", "]")); - new InsertManyStatement(COLLECTION_NAME_1, testObjects, "").execute(connection); + new InsertManyStatement(collectionName, testObjects, "").execute(connection); - assertThat(database.getCollection(COLLECTION_NAME_1).find()) + assertThat(database.getCollection(collectionName).find()) .hasSize(5); } + + @Test + @SneakyThrows + void cannotInsertSameDocumentsTwice() { + final List documents = Arrays.asList(new Document("_id", "x"),new Document("_id", "y")); + final Document options = new Document("ordered", false); + final InsertManyStatement statement = new InsertManyStatement(collectionName, documents, options); + statement.execute(connection); + + assertThatExceptionOfType(MongoException.class) + .isThrownBy(() -> statement.execute(connection)) + .withMessageStartingWith("Command failed. The full response is") + .withMessageContaining("E11000 duplicate key error collection"); + } }