Skip to content

Commit 6910923

Browse files
vbabaninstIncMale
andauthored
Add support for delete and update operations without filters (#135)
HIBERNATE-130 --------- Co-authored-by: Valentin Kovalenko <[email protected]>
1 parent a091338 commit 6910923

File tree

7 files changed

+207
-7
lines changed

7 files changed

+207
-7
lines changed

src/integrationTest/java/com/mongodb/hibernate/query/AbstractQueryIntegrationTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,23 @@ protected void assertActualCommand(BsonDocument expectedCommand) {
187187
.containsAllEntriesOf(expectedCommand);
188188
}
189189

190+
protected void assertMutationQuery(
191+
String hql,
192+
int expectedMutationCount,
193+
String expectedMql,
194+
MongoCollection<BsonDocument> collection,
195+
Iterable<? extends BsonDocument> expectedDocuments,
196+
Set<String> expectedAffectedCollections) {
197+
assertMutationQuery(
198+
hql,
199+
null,
200+
expectedMutationCount,
201+
expectedMql,
202+
collection,
203+
expectedDocuments,
204+
expectedAffectedCollections);
205+
}
206+
190207
protected void assertMutationQuery(
191208
String hql,
192209
Consumer<MutationQuery> queryPostProcessor,

src/integrationTest/java/com/mongodb/hibernate/query/mutation/DeletionIntegrationTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package com.mongodb.hibernate.query.mutation;
1818

19+
import static java.util.Collections.emptyList;
20+
1921
import com.mongodb.client.MongoCollection;
2022
import com.mongodb.hibernate.junit.InjectMongoCollection;
2123
import com.mongodb.hibernate.query.AbstractQueryIntegrationTests;
@@ -193,4 +195,25 @@ void testDeletionWithZeroMutationCount() {
193195
""")),
194196
Set.of(Book.COLLECTION_NAME));
195197
}
198+
199+
@Test
200+
void testDeleteNoFilter() {
201+
assertMutationQuery(
202+
"delete from Book",
203+
5,
204+
"""
205+
{
206+
"delete": "books",
207+
"deletes": [
208+
{
209+
"q": {},
210+
"limit": 0
211+
}
212+
]
213+
}
214+
""",
215+
mongoCollection,
216+
emptyList(),
217+
Set.of(Book.COLLECTION_NAME));
218+
}
196219
}

src/integrationTest/java/com/mongodb/hibernate/query/mutation/UpdatingIntegrationTests.java

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,93 @@ void testUpdateWithZeroMutationCount() {
231231
Set.of(Book.COLLECTION_NAME));
232232
}
233233

234+
@Test
235+
void testUpdateNoFilter() {
236+
assertMutationQuery(
237+
"update Book set title = :newTitle",
238+
q -> q.setParameter("newTitle", "Unknown"),
239+
5,
240+
"""
241+
{
242+
"update": "books",
243+
"updates": [
244+
{
245+
"multi": true,
246+
"q": {},
247+
"u": {
248+
"$set": {
249+
"title": "Unknown"
250+
}
251+
}
252+
}
253+
]
254+
}
255+
""",
256+
mongoCollection,
257+
List.of(
258+
BsonDocument.parse(
259+
"""
260+
{
261+
"_id": 1,
262+
"title": "Unknown",
263+
"outOfStock": true,
264+
"publishYear": 1869,
265+
"isbn13": null,
266+
"discount": null,
267+
"price": null
268+
}
269+
"""),
270+
BsonDocument.parse(
271+
"""
272+
{
273+
"_id": 2,
274+
"title": "Unknown",
275+
"outOfStock": false,
276+
"publishYear": 1866,
277+
"isbn13": null,
278+
"discount": null,
279+
"price": null
280+
}
281+
"""),
282+
BsonDocument.parse(
283+
"""
284+
{
285+
"_id": 3,
286+
"title": "Unknown",
287+
"outOfStock": false,
288+
"publishYear": 1877,
289+
"isbn13": null,
290+
"discount": null,
291+
"price": null
292+
}
293+
"""),
294+
BsonDocument.parse(
295+
"""
296+
{
297+
"_id": 4,
298+
"title": "Unknown",
299+
"outOfStock": false,
300+
"publishYear": 1880,
301+
"isbn13": null,
302+
"discount": null,
303+
"price": null
304+
}
305+
"""),
306+
BsonDocument.parse(
307+
"""
308+
{
309+
"_id": 5,
310+
"title": "Unknown",
311+
"outOfStock": false,
312+
"publishYear": 2025,
313+
"isbn13": null,
314+
"discount": null,
315+
"price": null
316+
}
317+
""")),
318+
Set.of(Book.COLLECTION_NAME));
319+
}
320+
234321
@Nested
235322
class Unsupported {
236323
@Test

src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstStage;
7575
import com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperation;
7676
import com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator;
77+
import com.mongodb.hibernate.internal.translate.mongoast.filter.AstEmptyFilter;
7778
import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFieldOperationFilter;
7879
import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilter;
7980
import com.mongodb.hibernate.internal.translate.mongoast.filter.AstLogicalFilter;
@@ -305,7 +306,7 @@ public void visitStandardTableDelete(TableDeleteStandard tableDelete) {
305306
if (tableDelete.getWhereFragment() != null) {
306307
throw new FeatureNotSupportedException();
307308
}
308-
var keyFilter = getKeyFilter(tableDelete);
309+
var keyFilter = createKeyFilter(tableDelete);
309310
astVisitorValueHolder.yield(
310311
MODEL_MUTATION_RESULT,
311312
ModelMutationMqlTranslator.Result.create(
@@ -321,7 +322,7 @@ public void visitStandardTableUpdate(TableUpdateStandard tableUpdate) {
321322
if (tableUpdate.getWhereFragment() != null) {
322323
throw new FeatureNotSupportedException();
323324
}
324-
var keyFilter = getKeyFilter(tableUpdate);
325+
var keyFilter = createKeyFilter(tableUpdate);
325326
var updates = new ArrayList<AstFieldUpdate>(tableUpdate.getNumberOfValueBindings());
326327
for (var valueBinding : tableUpdate.getValueBindings()) {
327328
var fieldName = valueBinding.getColumnReference().getColumnExpression();
@@ -335,7 +336,7 @@ public void visitStandardTableUpdate(TableUpdateStandard tableUpdate) {
335336
parameterBinders));
336337
}
337338

338-
private AstFilter getKeyFilter(AbstractRestrictedTableMutation<? extends MutationOperation> tableMutation) {
339+
private AstFilter createKeyFilter(AbstractRestrictedTableMutation<? extends MutationOperation> tableMutation) {
339340
if (tableMutation.getNumberOfOptimisticLockBindings() > 0) {
340341
throw new FeatureNotSupportedException("TODO-HIBERNATE-51 https://jira.mongodb.org/browse/HIBERNATE-51");
341342
}
@@ -524,7 +525,7 @@ public void visitRelationalPredicate(ComparisonPredicate comparisonPredicate) {
524525
var operator = isFieldOnLeftHandSide
525526
? comparisonPredicate.getOperator()
526527
: comparisonPredicate.getOperator().invert();
527-
var astComparisonFilterOperator = getAstComparisonFilterOperator(operator);
528+
var astComparisonFilterOperator = createAstComparisonFilterOperator(operator);
528529

529530
var astFilterOperation = new AstComparisonFilterOperation(astComparisonFilterOperator, comparisonValue);
530531
var filter = new AstFieldOperationFilter(fieldPath, astFilterOperation);
@@ -674,7 +675,7 @@ public void visitTuple(SqlTuple sqlTuple) {
674675
public void visitDeleteStatement(DeleteStatement deleteStatement) {
675676
checkMutationStatementSupportability(deleteStatement);
676677
var collection = addToAffectedTableNames(deleteStatement.getTargetTable());
677-
var filter = acceptAndYield(deleteStatement.getRestriction(), FILTER);
678+
var filter = createAstFilter(deleteStatement);
678679

679680
astVisitorValueHolder.yield(
680681
MUTATION_RESULT,
@@ -686,7 +687,7 @@ public void visitDeleteStatement(DeleteStatement deleteStatement) {
686687
public void visitUpdateStatement(UpdateStatement updateStatement) {
687688
checkMutationStatementSupportability(updateStatement);
688689
var collection = addToAffectedTableNames(updateStatement.getTargetTable());
689-
var filter = acceptAndYield(updateStatement.getRestriction(), FILTER);
690+
var filter = createAstFilter(updateStatement);
690691

691692
var assignments = updateStatement.getAssignments();
692693
var fieldUpdates = new ArrayList<AstFieldUpdate>(assignments.size());
@@ -715,6 +716,11 @@ private String addToAffectedTableNames(NamedTableReference tableRef) {
715716
return collection;
716717
}
717718

719+
private AstFilter createAstFilter(final AbstractUpdateOrDeleteStatement updateOrDeleteStatement) {
720+
var restriction = updateOrDeleteStatement.getRestriction();
721+
return restriction == null ? AstEmptyFilter.INSTANCE : acceptAndYield(restriction, FILTER);
722+
}
723+
718724
@Override
719725
public void visitInsertStatement(InsertSelectStatement insertStatement) {
720726
checkMutationStatementSupportability(insertStatement);
@@ -1075,7 +1081,7 @@ private static void checkQueryOptionsSupportability(QueryOptions queryOptions) {
10751081
}
10761082
}
10771083

1078-
private static AstComparisonFilterOperator getAstComparisonFilterOperator(ComparisonOperator operator) {
1084+
private static AstComparisonFilterOperator createAstComparisonFilterOperator(ComparisonOperator operator) {
10791085
return switch (operator) {
10801086
case EQUAL -> EQ;
10811087
case NOT_EQUAL -> NE;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2025-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mongodb.hibernate.internal.translate.mongoast.filter;
18+
19+
import org.bson.BsonWriter;
20+
21+
/**
22+
* Matches all documents.
23+
*
24+
* <p>{@link AstFieldOperationFilter} is used for specifying a concrete filter to match documents.
25+
*/
26+
public final class AstEmptyFilter implements AstFilter {
27+
public static final AstEmptyFilter INSTANCE = new AstEmptyFilter();
28+
29+
private AstEmptyFilter() {}
30+
31+
@Override
32+
public void render(BsonWriter writer) {
33+
writer.writeStartDocument();
34+
writer.writeEndDocument();
35+
}
36+
}

src/main/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstFieldOperationFilter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
/**
2222
* See <a href="https://www.mongodb.com/docs/manual/reference/glossary/#std-term-query-predicate">query predicate</a>,
2323
* <a href="https://www.mongodb.com/docs/manual/tutorial/query-documents/">Query Documents</a>.
24+
*
25+
* @see AstEmptyFilter
2426
*/
2527
public record AstFieldOperationFilter(String fieldPath, AstFilterOperation filterOperation) implements AstFilter {
2628
@Override
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2025-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mongodb.hibernate.internal.translate.mongoast.filter;
18+
19+
import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRendering;
20+
21+
import org.junit.jupiter.api.Test;
22+
23+
class AstEmptyFilterTests {
24+
25+
@Test
26+
void testRendering() {
27+
assertRendering("{}", AstEmptyFilter.INSTANCE);
28+
}
29+
}

0 commit comments

Comments
 (0)