Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,23 @@ protected void assertActualCommand(BsonDocument expectedCommand) {
.containsAllEntriesOf(expectedCommand);
}

protected void assertMutationQuery(
String hql,
int expectedMutationCount,
String expectedMql,
MongoCollection<BsonDocument> collection,
Iterable<? extends BsonDocument> expectedDocuments,
Set<String> expectedAffectedCollections) {
assertMutationQuery(
hql,
null,
expectedMutationCount,
expectedMql,
collection,
expectedDocuments,
expectedAffectedCollections);
}

protected void assertMutationQuery(
String hql,
Consumer<MutationQuery> queryPostProcessor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.mongodb.hibernate.query.mutation;

import static java.util.Collections.emptyList;

import com.mongodb.client.MongoCollection;
import com.mongodb.hibernate.junit.InjectMongoCollection;
import com.mongodb.hibernate.query.AbstractQueryIntegrationTests;
Expand Down Expand Up @@ -193,4 +195,25 @@ void testDeletionWithZeroMutationCount() {
""")),
Set.of(Book.COLLECTION_NAME));
}

@Test
void testDeleteNoFilter() {
assertMutationQuery(
"delete from Book",
5,
"""
{
"delete": "books",
"deletes": [
{
"q": {},
"limit": 0
}
]
}
""",
mongoCollection,
emptyList(),
Set.of(Book.COLLECTION_NAME));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,93 @@ void testUpdateWithZeroMutationCount() {
Set.of(Book.COLLECTION_NAME));
}

@Test
void testUpdateNoFilter() {
assertMutationQuery(
"update Book set title = :newTitle",
q -> q.setParameter("newTitle", "Unknown"),
5,
"""
{
"update": "books",
"updates": [
{
"multi": true,
"q": {},
"u": {
"$set": {
"title": "Unknown"
}
}
}
]
}
""",
mongoCollection,
List.of(
BsonDocument.parse(
"""
{
"_id": 1,
"title": "Unknown",
"outOfStock": true,
"publishYear": 1869,
"isbn13": null,
"discount": null,
"price": null
}
"""),
BsonDocument.parse(
"""
{
"_id": 2,
"title": "Unknown",
"outOfStock": false,
"publishYear": 1866,
"isbn13": null,
"discount": null,
"price": null
}
"""),
BsonDocument.parse(
"""
{
"_id": 3,
"title": "Unknown",
"outOfStock": false,
"publishYear": 1877,
"isbn13": null,
"discount": null,
"price": null
}
"""),
BsonDocument.parse(
"""
{
"_id": 4,
"title": "Unknown",
"outOfStock": false,
"publishYear": 1880,
"isbn13": null,
"discount": null,
"price": null
}
"""),
BsonDocument.parse(
"""
{
"_id": 5,
"title": "Unknown",
"outOfStock": false,
"publishYear": 2025,
"isbn13": null,
"discount": null,
"price": null
}
""")),
Set.of(Book.COLLECTION_NAME));
}

@Nested
class Unsupported {
@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstStage;
import com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperation;
import com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator;
import com.mongodb.hibernate.internal.translate.mongoast.filter.AstEmptyFilter;
import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFieldOperationFilter;
import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilter;
import com.mongodb.hibernate.internal.translate.mongoast.filter.AstLogicalFilter;
Expand Down Expand Up @@ -305,7 +306,7 @@ public void visitStandardTableDelete(TableDeleteStandard tableDelete) {
if (tableDelete.getWhereFragment() != null) {
throw new FeatureNotSupportedException();
}
var keyFilter = getKeyFilter(tableDelete);
var keyFilter = createKeyFilter(tableDelete);
astVisitorValueHolder.yield(
MODEL_MUTATION_RESULT,
ModelMutationMqlTranslator.Result.create(
Expand All @@ -321,7 +322,7 @@ public void visitStandardTableUpdate(TableUpdateStandard tableUpdate) {
if (tableUpdate.getWhereFragment() != null) {
throw new FeatureNotSupportedException();
}
var keyFilter = getKeyFilter(tableUpdate);
var keyFilter = createKeyFilter(tableUpdate);
var updates = new ArrayList<AstFieldUpdate>(tableUpdate.getNumberOfValueBindings());
for (var valueBinding : tableUpdate.getValueBindings()) {
var fieldName = valueBinding.getColumnReference().getColumnExpression();
Expand All @@ -335,7 +336,7 @@ public void visitStandardTableUpdate(TableUpdateStandard tableUpdate) {
parameterBinders));
}

private AstFilter getKeyFilter(AbstractRestrictedTableMutation<? extends MutationOperation> tableMutation) {
private AstFilter createKeyFilter(AbstractRestrictedTableMutation<? extends MutationOperation> tableMutation) {
Copy link
Member Author

@vbabanin vbabanin Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have several helper methods for creating Mongo AST elements:
getAstComparisonFilterOperator, getKeyFilter, createMatchStage, createSortStage, createProjectStage, createAstSortField.

Their naming pattern is inconsistent - some use get, others create.
To make them consistent and clearer in intent, we could use a common prefix, and create i think better reflects their behavior.

This PR also adds createAstFilter following that convention.

if (tableMutation.getNumberOfOptimisticLockBindings() > 0) {
throw new FeatureNotSupportedException("TODO-HIBERNATE-51 https://jira.mongodb.org/browse/HIBERNATE-51");
}
Expand Down Expand Up @@ -524,7 +525,7 @@ public void visitRelationalPredicate(ComparisonPredicate comparisonPredicate) {
var operator = isFieldOnLeftHandSide
? comparisonPredicate.getOperator()
: comparisonPredicate.getOperator().invert();
var astComparisonFilterOperator = getAstComparisonFilterOperator(operator);
var astComparisonFilterOperator = createAstComparisonFilterOperator(operator);

var astFilterOperation = new AstComparisonFilterOperation(astComparisonFilterOperator, comparisonValue);
var filter = new AstFieldOperationFilter(fieldPath, astFilterOperation);
Expand Down Expand Up @@ -674,7 +675,7 @@ public void visitTuple(SqlTuple sqlTuple) {
public void visitDeleteStatement(DeleteStatement deleteStatement) {
checkMutationStatementSupportability(deleteStatement);
var collection = addToAffectedTableNames(deleteStatement.getTargetTable());
var filter = acceptAndYield(deleteStatement.getRestriction(), FILTER);
var filter = createAstFilter(deleteStatement);

astVisitorValueHolder.yield(
MUTATION_RESULT,
Expand All @@ -686,7 +687,7 @@ public void visitDeleteStatement(DeleteStatement deleteStatement) {
public void visitUpdateStatement(UpdateStatement updateStatement) {
checkMutationStatementSupportability(updateStatement);
var collection = addToAffectedTableNames(updateStatement.getTargetTable());
var filter = acceptAndYield(updateStatement.getRestriction(), FILTER);
var filter = createAstFilter(updateStatement);

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

private AstFilter createAstFilter(final AbstractUpdateOrDeleteStatement updateOrDeleteStatement) {
var restriction = updateOrDeleteStatement.getRestriction();
return restriction == null ? AstEmptyFilter.INSTANCE : acceptAndYield(restriction, FILTER);
}

@Override
public void visitInsertStatement(InsertSelectStatement insertStatement) {
checkMutationStatementSupportability(insertStatement);
Expand Down Expand Up @@ -1075,7 +1081,7 @@ private static void checkQueryOptionsSupportability(QueryOptions queryOptions) {
}
}

private static AstComparisonFilterOperator getAstComparisonFilterOperator(ComparisonOperator operator) {
private static AstComparisonFilterOperator createAstComparisonFilterOperator(ComparisonOperator operator) {
return switch (operator) {
case EQUAL -> EQ;
case NOT_EQUAL -> NE;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2025-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.mongodb.hibernate.internal.translate.mongoast.filter;

import org.bson.BsonWriter;

/**
* Matches all documents.
*
* <p>{@link AstFieldOperationFilter} is used for specifying a concrete filter to match documents.
*/
public final class AstEmptyFilter implements AstFilter {
public static final AstEmptyFilter INSTANCE = new AstEmptyFilter();

private AstEmptyFilter() {}

@Override
public void render(BsonWriter writer) {
writer.writeStartDocument();
writer.writeEndDocument();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
/**
* See <a href="https://www.mongodb.com/docs/manual/reference/glossary/#std-term-query-predicate">query predicate</a>,
* <a href="https://www.mongodb.com/docs/manual/tutorial/query-documents/">Query Documents</a>.
*
* @see AstEmptyFilter
*/
public record AstFieldOperationFilter(String fieldPath, AstFilterOperation filterOperation) implements AstFilter {
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2025-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.mongodb.hibernate.internal.translate.mongoast.filter;

import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRendering;

import org.junit.jupiter.api.Test;

class AstEmptyFilterTests {

@Test
void testRendering() {
assertRendering("{}", AstEmptyFilter.INSTANCE);
}
}