Skip to content

Commit a261e1f

Browse files
committed
Issue liquibase#90 Convert FindOneAndUpdateStatement to use runCommand
1 parent cd36861 commit a261e1f

File tree

2 files changed

+152
-37
lines changed

2 files changed

+152
-37
lines changed

src/main/java/liquibase/ext/mongodb/statement/FindOneAndUpdateStatement.java

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,67 +20,65 @@
2020
* #L%
2121
*/
2222

23-
import com.mongodb.client.MongoCollection;
24-
import com.mongodb.client.model.FindOneAndUpdateOptions;
2523
import liquibase.ext.mongodb.database.MongoLiquibaseDatabase;
26-
import liquibase.nosql.statement.NoSqlExecuteStatement;
2724
import liquibase.nosql.statement.NoSqlUpdateStatement;
2825
import lombok.EqualsAndHashCode;
2926
import lombok.Getter;
3027
import org.bson.Document;
3128
import org.bson.conversions.Bson;
3229

3330
import static java.util.Objects.isNull;
34-
import static java.util.Optional.ofNullable;
35-
import static liquibase.ext.mongodb.statement.AbstractRunCommandStatement.SHELL_DB_PREFIX;
31+
import static java.util.Objects.nonNull;
32+
import static liquibase.ext.mongodb.statement.BsonUtils.toCommand;
3633

34+
/**
35+
* Finds and updates a single document via the database runCommand method
36+
* NOTE: This does not return the original document,
37+
* instead returns 1 if a document was updated, else 0
38+
*
39+
* For a list of supported options see the reference page:
40+
* @see <a href="https://docs.mongodb.com/manual/reference/command/findAndModify//">findAndModify</a>
41+
*
42+
*/
3743
@Getter
3844
@EqualsAndHashCode(callSuper = true)
39-
public class FindOneAndUpdateStatement extends AbstractCollectionStatement
40-
implements NoSqlExecuteStatement<MongoLiquibaseDatabase>, NoSqlUpdateStatement<MongoLiquibaseDatabase> {
45+
public class FindOneAndUpdateStatement extends AbstractRunCommandStatement
46+
implements NoSqlUpdateStatement<MongoLiquibaseDatabase> {
4147

42-
public static final String COMMAND_NAME = "updateLastTag";
43-
44-
private final Bson filter;
45-
private final Bson document;
46-
private final Bson sort;
48+
public static final String RUN_COMMAND_NAME = "findAndModify";
49+
public static final String QUERY = "query";
50+
public static final String UPDATE = "update";
51+
public static final String SORT = "sort";
52+
public static final String VALUE = "value";
4753

4854
public FindOneAndUpdateStatement(final String collectionName, final Bson filter, final Bson document, final Bson sort) {
49-
super(collectionName);
50-
this.filter = filter;
51-
this.document = document;
52-
this.sort = sort;
55+
this(collectionName, combine(filter, document, sort));
5356
}
5457

55-
@Override
56-
public String getCommandName() {
57-
return COMMAND_NAME;
58+
public FindOneAndUpdateStatement(final String collectionName, Document options) {
59+
super(toCommand(RUN_COMMAND_NAME, collectionName, options));
5860
}
5961

6062
@Override
61-
public String toJs() {
62-
return
63-
SHELL_DB_PREFIX +
64-
getCollectionName() +
65-
"." +
66-
getCommandName() +
67-
"(" +
68-
ofNullable(filter).map(Bson::toString).orElse(null) +
69-
", " +
70-
ofNullable(document).map(Bson::toString).orElse(null) +
71-
", " +
72-
ofNullable(sort).map(Bson::toString).orElse(null) +
73-
");";
63+
public String getRunCommandName() {
64+
return RUN_COMMAND_NAME;
7465
}
7566

76-
@Override
77-
public void execute(final MongoLiquibaseDatabase database) {
78-
update(database);
67+
private static Document combine(final Bson filter, final Bson document, final Bson sort) {
68+
final Document combined = new Document(QUERY, filter);
69+
if(nonNull(document)) { combined.put(UPDATE, document); }
70+
if(nonNull(sort)) { combined.put(SORT, sort); }
71+
return combined;
7972
}
8073

74+
/**
75+
* Executes the findAndModify operation
76+
* @param database the database to run against
77+
* @return 1 if a document was modified else 0
78+
*/
8179
@Override
8280
public int update(final MongoLiquibaseDatabase database) {
83-
final MongoCollection<Document> collection = database.getMongoDatabase().getCollection(getCollectionName());
84-
return isNull(collection.findOneAndUpdate(filter, document, new FindOneAndUpdateOptions().sort(sort))) ? 0 : 1;
81+
Document response = super.run(database);
82+
return isNull(response.get(VALUE)) ? 0 : 1;
8583
}
8684
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package liquibase.ext.mongodb.statement;
2+
3+
/*-
4+
* #%L
5+
* Liquibase MongoDB Extension
6+
* %%
7+
* Copyright (C) 2021 Mastercard
8+
* %%
9+
* Licensed under the Apache License, Version 2.0 (the "License").
10+
* You may not use this file except in compliance with the License.
11+
* You may obtain a copy of the License at
12+
*
13+
* http://www.apache.org/licenses/LICENSE-2.0
14+
*
15+
* Unless required by applicable law or agreed to in writing, software
16+
* distributed under the License is distributed on an "AS IS" BASIS,
17+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* See the License for the specific language governing permissions and
19+
* limitations under the License.
20+
* #L%
21+
*/
22+
23+
import com.mongodb.client.FindIterable;
24+
import com.mongodb.client.MongoCursor;
25+
import liquibase.ext.AbstractMongoIntegrationTest;
26+
import org.bson.Document;
27+
import org.junit.jupiter.api.BeforeEach;
28+
import org.junit.jupiter.api.Test;
29+
30+
import static liquibase.ext.mongodb.TestUtils.COLLECTION_NAME_1;
31+
import static org.assertj.core.api.Assertions.assertThat;
32+
33+
class FindOneAndUpdateStatementIT extends AbstractMongoIntegrationTest {
34+
35+
private final Document first = new Document("name", "first");
36+
private final Document second = new Document("name", "second");
37+
private final Document modified = new Document("name", "modified");
38+
private final Document update = new Document("$set", modified);
39+
private final Document sort = new Document("name", -1);
40+
private final Document emptyDocument = new Document();
41+
42+
private String collectionName;
43+
44+
@BeforeEach
45+
public void createCollectionName() {
46+
collectionName = COLLECTION_NAME_1 + System.nanoTime();
47+
}
48+
49+
@Test
50+
public void testUpdateWhenNoDocumentFound() {
51+
int updated = new FindOneAndUpdateStatement(collectionName, emptyDocument, update, emptyDocument)
52+
.update(database);
53+
assertThat(updated).isEqualTo(0);
54+
}
55+
56+
@Test
57+
public void testUpdateWhenDocumentFound() {
58+
59+
new InsertOneStatement(collectionName, first).execute(database);
60+
61+
int updated = new FindOneAndUpdateStatement(collectionName, emptyDocument, update, emptyDocument)
62+
.update(database);
63+
assertThat(updated).isEqualTo(1);
64+
65+
final FindIterable<Document> docs = mongoDatabase.getCollection(collectionName).find();
66+
assertThat(docs).hasSize(1);
67+
assertThat(docs.iterator().next())
68+
.containsEntry("name", "modified");
69+
}
70+
71+
@Test
72+
public void testUpdateWithMatchingFilter() {
73+
74+
new InsertOneStatement(collectionName, first).execute(database);
75+
new InsertOneStatement(collectionName, second).execute(database);
76+
77+
int updated = new FindOneAndUpdateStatement(collectionName, second, update, emptyDocument)
78+
.update(database);
79+
assertThat(updated).isEqualTo(1);
80+
81+
final FindIterable<Document> docs = mongoDatabase.getCollection(collectionName).find(modified);
82+
assertThat(docs).hasSize(1);
83+
assertThat(docs.iterator().next())
84+
.containsEntry("name", "modified");
85+
}
86+
87+
@Test
88+
public void testUpdateWhenDocumentFoundWithSort() {
89+
90+
new InsertOneStatement(collectionName, first).execute(database);
91+
new InsertOneStatement(collectionName, second).execute(database);
92+
93+
int updated = new FindOneAndUpdateStatement(collectionName, emptyDocument, update, sort)
94+
.update(database);
95+
assertThat(updated).isEqualTo(1);
96+
97+
final FindIterable<Document> docs = mongoDatabase.getCollection(collectionName).find()
98+
.sort(new Document("name",1));
99+
assertThat(docs).hasSize(2);
100+
MongoCursor<Document> iterator = docs.iterator();
101+
assertThat(iterator.next())
102+
.containsEntry("name", "first");
103+
assertThat(iterator.next())
104+
.containsEntry("name", "modified");
105+
}
106+
107+
@Test
108+
void toStringJs() {
109+
final FindOneAndUpdateStatement statement = new FindOneAndUpdateStatement(COLLECTION_NAME_1, first, modified, sort);
110+
assertThat(statement.toJs())
111+
.isEqualTo(statement.toString())
112+
.isEqualTo("db.runCommand({\"findAndModify\": \"collectionName\", " +
113+
"\"query\": {\"name\": \"first\"}, " +
114+
"\"update\": {\"name\": \"modified\"}, " +
115+
"\"sort\": {\"name\": -1}});");
116+
}
117+
}

0 commit comments

Comments
 (0)