From 9cb4599ec0febbe07fd859a7659a08020bb45739 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 13 Mar 2025 16:51:41 -0400 Subject: [PATCH 01/33] implement loading by primary key --- .../hibernate/BasicCrudIntegrationTests.java | 21 +++ .../resources/logback-test.xml | 2 + .../translate/AbstractMqlTranslator.java | 151 +++++++++++++----- .../translate/AstVisitorValueDescriptor.java | 16 +- .../translate/MongoTranslatorFactory.java | 3 +- .../SelectStatementMqlTranslator.java | 53 ++++++ .../translate/TableMutationMqlTranslator.java | 4 +- .../mongoast/command/AstAggregateCommand.java | 34 ++++ .../mongoast/command/AstCommand.java | 21 +++ .../mongoast/command/AstDeleteCommand.java | 3 +- .../mongoast/command/AstInsertCommand.java | 3 +- .../mongoast/command/AstUpdateCommand.java | 4 +- .../command/aggregate/AstPipeline.java | 32 ++++ .../mongoast/command/aggregate/AstStage.java | 21 +++ .../command/aggregate/package-info.java | 21 +++ .../aggregate/stage/AstMatchStage.java | 33 ++++ .../aggregate/stage/AstProjectStage.java | 37 +++++ .../stage/AstProjectStageSpecification.java | 32 ++++ .../command/aggregate/stage/package-info.java | 21 +++ .../mongoast/expression/AstExpression.java | 21 +++ .../expression/AstFieldPathExpression.java | 26 +++ .../mongoast/expression/package-info.java | 21 +++ .../filter/AstMatchesEverythingFilter.java | 32 ++++ .../command/AstAggregateCommandTests.java | 57 +++++++ 24 files changed, 618 insertions(+), 51 deletions(-) create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommand.java create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstCommand.java create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstPipeline.java create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstStage.java create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/package-info.java create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStage.java create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStage.java create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/package-info.java create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstExpression.java create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstFieldPathExpression.java create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/package-info.java create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilter.java create mode 100644 src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java diff --git a/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java index f61d4a01..d0f5b03d 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java @@ -198,6 +198,27 @@ void testDynamicUpdate() { } } + @Nested + class LoadByIdTests { + + @Test + void testLoad() { + var book = new Book(); + book.id = 1; + book.title = "In Search of Lost Time"; + book.publishYear = 1913; + + sessionFactoryScope.inTransaction(session -> session.persist(book)); + + var loadedBook = sessionFactoryScope.fromTransaction(session -> { + var loaded = new Book(); + session.load(loaded, 1); + return loaded; + }); + assertThat(loadedBook).usingRecursiveComparison().isEqualTo(book); + } + } + private static void assertCollectionContainsExactly(BsonDocument expectedDoc) { assertThat(mongoCollection.find()).containsExactly(expectedDoc); } diff --git a/src/integrationTest/resources/logback-test.xml b/src/integrationTest/resources/logback-test.xml index a70fad20..c6ffeff3 100644 --- a/src/integrationTest/resources/logback-test.xml +++ b/src/integrationTest/resources/logback-test.xml @@ -10,7 +10,9 @@ + + diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index 9b19380b..3bda01ce 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -19,10 +19,18 @@ import static com.mongodb.hibernate.internal.MongoAssertions.assertNotNull; import static com.mongodb.hibernate.internal.MongoAssertions.assertTrue; import static com.mongodb.hibernate.internal.MongoConstants.ID_FIELD_NAME; +import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_AGGREGATE; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_MUTATION; +import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_NAME; +import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.FIELD_NAME; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.FIELD_VALUE; +import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.FILTER; +import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.PROJECT_STAGE_SPECIFICATIONS; +import static com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification.include; import static com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator.EQ; import static java.lang.String.format; +import static org.hibernate.query.sqm.ComparisonOperator.EQUAL; +import static org.hibernate.sql.ast.SqlTreePrinter.logSqlAst; import com.mongodb.hibernate.internal.FeatureNotSupportedException; import com.mongodb.hibernate.internal.extension.service.StandardServiceRegistryScopedState; @@ -31,16 +39,25 @@ import com.mongodb.hibernate.internal.translate.mongoast.AstFieldUpdate; import com.mongodb.hibernate.internal.translate.mongoast.AstNode; import com.mongodb.hibernate.internal.translate.mongoast.AstParameterMarker; +import com.mongodb.hibernate.internal.translate.mongoast.command.AstAggregateCommand; +import com.mongodb.hibernate.internal.translate.mongoast.command.AstCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.AstDeleteCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.AstInsertCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.AstUpdateCommand; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstPipeline; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstStage; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstMatchStage; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStage; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperation; 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.AstFilterFieldPath; +import com.mongodb.hibernate.internal.translate.mongoast.filter.AstMatchesEverythingFilter; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import org.bson.json.JsonMode; @@ -48,6 +65,7 @@ import org.bson.json.JsonWriterSettings; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.collections.Stack; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.internal.SqlFragmentPredicate; import org.hibernate.query.sqm.tree.expression.Conversion; import org.hibernate.sql.ast.Clause; @@ -92,6 +110,7 @@ import org.hibernate.sql.ast.tree.from.FunctionTableReference; import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference; +import org.hibernate.sql.ast.tree.from.StandardTableGroup; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableReferenceJoin; @@ -143,6 +162,8 @@ abstract class AbstractMqlTranslator implements SqlAstT private final List parameterBinders = new ArrayList<>(); + private final Set affectedTableNames = new HashSet<>(); + AbstractMqlTranslator(SessionFactoryImplementor sessionFactory) { this.sessionFactory = sessionFactory; assertNotNull(sessionFactory @@ -178,7 +199,7 @@ public Stack getCurrentClauseStack() { @Override public Set getAffectedTableNames() { - throw new FeatureNotSupportedException("TODO-HIBERNATE-22 https://jira.mongodb.org/browse/HIBERNATE-22"); + return affectedTableNames; } List getParameterBinders() { @@ -197,12 +218,12 @@ static String renderMongoAstNode(AstNode rootAstNode) { } @SuppressWarnings("overloads") - R acceptAndYield(Statement statement, AstVisitorValueDescriptor resultDescriptor) { + R acceptAndYield(Statement statement, AstVisitorValueDescriptor resultDescriptor) { return astVisitorValueHolder.execute(resultDescriptor, () -> statement.accept(this)); } @SuppressWarnings("overloads") - R acceptAndYield(SqlAstNode node, AstVisitorValueDescriptor resultDescriptor) { + R acceptAndYield(SqlAstNode node, AstVisitorValueDescriptor resultDescriptor) { return astVisitorValueHolder.execute(resultDescriptor, () -> node.accept(this)); } @@ -214,20 +235,18 @@ public void visitStandardTableInsert(TableInsertStandard tableInsert) { if (tableInsert.getNumberOfReturningColumns() > 0) { throw new FeatureNotSupportedException(); } - var tableName = tableInsert.getTableName(); var astElements = new ArrayList(tableInsert.getNumberOfValueBindings()); for (var columnValueBinding : tableInsert.getValueBindings()) { var columnExpression = columnValueBinding.getColumnReference().getColumnExpression(); var valueExpression = columnValueBinding.getValueExpression(); - if (valueExpression == null) { - throw new FeatureNotSupportedException(); - } var astValue = acceptAndYield(valueExpression, FIELD_VALUE); astElements.add(new AstElement(columnExpression, astValue)); } - astVisitorValueHolder.yield(COLLECTION_MUTATION, new AstInsertCommand(tableName, new AstDocument(astElements))); + astVisitorValueHolder.yield( + COLLECTION_MUTATION, + new AstInsertCommand(tableInsert.getMutatingTable().getTableName(), new AstDocument(astElements))); } @Override @@ -300,8 +319,92 @@ public void visitParameter(JdbcParameter jdbcParameter) { } @Override + @SuppressWarnings("OperatorPrecedence") public void visitSelectStatement(SelectStatement selectStatement) { - throw new FeatureNotSupportedException("TODO-HIBERNATE-22 https://jira.mongodb.org/browse/HIBERNATE-22"); + logSqlAst(selectStatement); + if (!selectStatement.getQueryPart().isRoot()) { + throw new FeatureNotSupportedException("Subquery not supported"); + } + if (selectStatement.getCteStatements() != null + && !selectStatement.getCteStatements().isEmpty() + || selectStatement.getCteObjects() != null + && !selectStatement.getCteObjects().isEmpty()) { + throw new FeatureNotSupportedException("CTE feature not supported"); + } + selectStatement.getQueryPart().accept(this); + } + + @Override + public void visitQuerySpec(QuerySpec querySpec) { + var collection = acceptAndYield(assertNotNull(querySpec.getFromClause()), COLLECTION_NAME); + + var whereClauseRestrictions = querySpec.getWhereClauseRestrictions(); + var filter = whereClauseRestrictions == null || whereClauseRestrictions.isEmpty() + ? AstMatchesEverythingFilter.INSTANCE + : acceptAndYield(whereClauseRestrictions, FILTER); + + var projectStageSpecifications = acceptAndYield(querySpec.getSelectClause(), PROJECT_STAGE_SPECIFICATIONS); + + var stages = List.of(new AstMatchStage(filter), new AstProjectStage(projectStageSpecifications)); + astVisitorValueHolder.yield(COLLECTION_AGGREGATE, new AstAggregateCommand(collection, new AstPipeline(stages))); + } + + @Override + public void visitFromClause(FromClause fromClause) { + if (fromClause.getRoots().size() == 1 + && fromClause.getRoots().get(0) instanceof StandardTableGroup standardTableGroup + && standardTableGroup.getModelPart() instanceof EntityPersister entityPersister + && entityPersister.getQuerySpaces().length == 1) { + affectedTableNames.add(((String[]) entityPersister.getQuerySpaces())[0]); + standardTableGroup.getPrimaryTableReference().accept(this); + } else { + throw new FeatureNotSupportedException(); + } + } + + @Override + public void visitNamedTableReference(NamedTableReference namedTableReference) { + astVisitorValueHolder.yield(COLLECTION_NAME, namedTableReference.getTableExpression()); + } + + @Override + public void visitRelationalPredicate(ComparisonPredicate comparisonPredicate) { + var fieldName = acceptAndYield(comparisonPredicate.getLeftHandExpression(), FIELD_NAME); + var fieldValue = acceptAndYield(comparisonPredicate.getRightHandExpression(), FIELD_VALUE); + + if (comparisonPredicate.getOperator() != EQUAL) { + throw new FeatureNotSupportedException(); + } + var filter = new AstFieldOperationFilter( + new AstFilterFieldPath(fieldName), new AstComparisonFilterOperation(EQ, fieldValue)); + astVisitorValueHolder.yield(FILTER, filter); + } + + @Override + public void visitSelectClause(SelectClause selectClause) { + var projectStageSpecifications = new ArrayList( + selectClause.getSqlSelections().size()); + + for (SqlSelection sqlSelection : selectClause.getSqlSelections()) { + if (sqlSelection.isVirtual()) { + continue; + } + if (sqlSelection.getExpression() instanceof ColumnReference columnReference) { + var columnName = acceptAndYield(columnReference, FIELD_NAME); + projectStageSpecifications.add(include(columnName)); + } else { + throw new FeatureNotSupportedException(); + } + } + astVisitorValueHolder.yield(PROJECT_STAGE_SPECIFICATIONS, projectStageSpecifications); + } + + @Override + public void visitColumnReference(ColumnReference columnReference) { + if (columnReference.isColumnExpressionFormula()) { + throw new FeatureNotSupportedException(); + } + astVisitorValueHolder.yield(FIELD_NAME, columnReference.getColumnExpression()); } @Override @@ -329,11 +432,6 @@ public void visitQueryGroup(QueryGroup queryGroup) { throw new FeatureNotSupportedException(); } - @Override - public void visitQuerySpec(QuerySpec querySpec) { - throw new FeatureNotSupportedException(); - } - @Override public void visitSortSpecification(SortSpecification sortSpecification) { throw new FeatureNotSupportedException(); @@ -344,21 +442,11 @@ public void visitOffsetFetchClause(QueryPart queryPart) { throw new FeatureNotSupportedException(); } - @Override - public void visitSelectClause(SelectClause selectClause) { - throw new FeatureNotSupportedException(); - } - @Override public void visitSqlSelection(SqlSelection sqlSelection) { throw new FeatureNotSupportedException(); } - @Override - public void visitFromClause(FromClause fromClause) { - throw new FeatureNotSupportedException(); - } - @Override public void visitTableGroup(TableGroup tableGroup) { throw new FeatureNotSupportedException(); @@ -369,11 +457,6 @@ public void visitTableGroupJoin(TableGroupJoin tableGroupJoin) { throw new FeatureNotSupportedException(); } - @Override - public void visitNamedTableReference(NamedTableReference namedTableReference) { - throw new FeatureNotSupportedException(); - } - @Override public void visitValuesTableReference(ValuesTableReference valuesTableReference) { throw new FeatureNotSupportedException(); @@ -394,11 +477,6 @@ public void visitTableReferenceJoin(TableReferenceJoin tableReferenceJoin) { throw new FeatureNotSupportedException(); } - @Override - public void visitColumnReference(ColumnReference columnReference) { - throw new FeatureNotSupportedException(); - } - @Override public void visitNestedColumnReference(NestedColumnReference nestedColumnReference) { throw new FeatureNotSupportedException(); @@ -609,11 +687,6 @@ public void visitThruthnessPredicate(ThruthnessPredicate thruthnessPredicate) { throw new FeatureNotSupportedException(); } - @Override - public void visitRelationalPredicate(ComparisonPredicate comparisonPredicate) { - throw new FeatureNotSupportedException(); - } - @Override public void visitSelfRenderingPredicate(SelfRenderingPredicate selfRenderingPredicate) { throw new FeatureNotSupportedException(); diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AstVisitorValueDescriptor.java b/src/main/java/com/mongodb/hibernate/internal/translate/AstVisitorValueDescriptor.java index f261657d..2b86cd9a 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AstVisitorValueDescriptor.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AstVisitorValueDescriptor.java @@ -19,19 +19,31 @@ import static com.mongodb.hibernate.internal.MongoAssertions.assertNotNull; import static com.mongodb.hibernate.internal.MongoAssertions.fail; -import com.mongodb.hibernate.internal.translate.mongoast.AstNode; import com.mongodb.hibernate.internal.translate.mongoast.AstValue; +import com.mongodb.hibernate.internal.translate.mongoast.command.AstCommand; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification; +import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilter; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.IdentityHashMap; +import java.util.List; import java.util.Map; @SuppressWarnings("UnusedTypeParameter") final class AstVisitorValueDescriptor { - static final AstVisitorValueDescriptor COLLECTION_MUTATION = new AstVisitorValueDescriptor<>(); + static final AstVisitorValueDescriptor COLLECTION_MUTATION = new AstVisitorValueDescriptor<>(); + static final AstVisitorValueDescriptor COLLECTION_AGGREGATE = new AstVisitorValueDescriptor<>(); + + static final AstVisitorValueDescriptor COLLECTION_NAME = new AstVisitorValueDescriptor<>(); + + static final AstVisitorValueDescriptor FIELD_NAME = new AstVisitorValueDescriptor<>(); static final AstVisitorValueDescriptor FIELD_VALUE = new AstVisitorValueDescriptor<>(); + static final AstVisitorValueDescriptor> PROJECT_STAGE_SPECIFICATIONS = + new AstVisitorValueDescriptor<>(); + static final AstVisitorValueDescriptor FILTER = new AstVisitorValueDescriptor<>(); + private static final Map, String> CONSTANT_TOSTRING_CONTENT_MAP; static { diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/MongoTranslatorFactory.java b/src/main/java/com/mongodb/hibernate/internal/translate/MongoTranslatorFactory.java index b2a9c05f..31be705a 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/MongoTranslatorFactory.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/MongoTranslatorFactory.java @@ -30,8 +30,7 @@ public final class MongoTranslatorFactory implements SqlAstTranslatorFactory { @Override public SqlAstTranslator buildSelectTranslator( SessionFactoryImplementor sessionFactoryImplementor, SelectStatement selectStatement) { - // TODO-HIBERNATE-22 https://jira.mongodb.org/browse/HIBERNATE-22 - return new NoopSqlAstTranslator<>(); + return new SelectStatementMqlTranslator(sessionFactoryImplementor, selectStatement); } @Override diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java new file mode 100644 index 00000000..72c8a2a2 --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java @@ -0,0 +1,53 @@ +/* + * 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; + +import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_AGGREGATE; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.query.spi.QueryOptions; +import org.hibernate.sql.ast.tree.Statement; +import org.hibernate.sql.ast.tree.select.SelectStatement; +import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import org.jspecify.annotations.Nullable; + +final class SelectStatementMqlTranslator extends AbstractMqlTranslator { + + private final SelectStatement selectStatement; + + SelectStatementMqlTranslator(SessionFactoryImplementor sessionFactory, SelectStatement selectStatement) { + super(sessionFactory); + this.selectStatement = selectStatement; + } + + @Override + public JdbcOperationQuerySelect translate( + @Nullable JdbcParameterBindings jdbcParameterBindings, QueryOptions queryOptions) { + var aggregateCommand = acceptAndYield((Statement) selectStatement, COLLECTION_AGGREGATE); + var mql = renderMongoAstNode(aggregateCommand); + + var sessionFactory = getSessionFactory(); + var jdbcValuesMappingProducer = sessionFactory + .getFastSessionServices() + .getJdbcValuesMappingProducerProvider() + .buildMappingProducer(selectStatement, sessionFactory); + + return new JdbcOperationQuerySelect( + mql, getParameterBinders(), jdbcValuesMappingProducer, getAffectedTableNames()); + } +} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/TableMutationMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/TableMutationMqlTranslator.java index 7b9ecc27..adb4f54f 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/TableMutationMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/TableMutationMqlTranslator.java @@ -40,7 +40,7 @@ public O translate(@Nullable JdbcParameterBindings jdbcParameterBindings, QueryO assertNull(jdbcParameterBindings); // QueryOptions class is not applicable to table mutation so a dummy value is always passed in - var rootAstNode = acceptAndYield(tableMutation, COLLECTION_MUTATION); - return tableMutation.createMutationOperation(renderMongoAstNode(rootAstNode), getParameterBinders()); + var mutationCommand = acceptAndYield(tableMutation, COLLECTION_MUTATION); + return tableMutation.createMutationOperation(renderMongoAstNode(mutationCommand), getParameterBinders()); } } diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommand.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommand.java new file mode 100644 index 00000000..f6171306 --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommand.java @@ -0,0 +1,34 @@ +/* + * 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.command; + +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstPipeline; +import org.bson.BsonWriter; + +public record AstAggregateCommand(String collection, AstPipeline pipeline) implements AstCommand { + + @Override + public void render(BsonWriter writer) { + writer.writeStartDocument(); + { + writer.writeString("aggregate", collection); + writer.writeName("pipeline"); + pipeline.render(writer); + } + writer.writeEndDocument(); + } +} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstCommand.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstCommand.java new file mode 100644 index 00000000..c76c9083 --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstCommand.java @@ -0,0 +1,21 @@ +/* + * 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.command; + +import com.mongodb.hibernate.internal.translate.mongoast.AstNode; + +public interface AstCommand extends AstNode {} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstDeleteCommand.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstDeleteCommand.java index 75a83e5c..6c2e10f6 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstDeleteCommand.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstDeleteCommand.java @@ -16,11 +16,10 @@ package com.mongodb.hibernate.internal.translate.mongoast.command; -import com.mongodb.hibernate.internal.translate.mongoast.AstNode; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilter; import org.bson.BsonWriter; -public record AstDeleteCommand(String collection, AstFilter filter) implements AstNode { +public record AstDeleteCommand(String collection, AstFilter filter) implements AstCommand { @Override public void render(BsonWriter writer) { writer.writeStartDocument(); diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstInsertCommand.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstInsertCommand.java index 586b7e5e..a8358991 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstInsertCommand.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstInsertCommand.java @@ -17,10 +17,9 @@ package com.mongodb.hibernate.internal.translate.mongoast.command; import com.mongodb.hibernate.internal.translate.mongoast.AstDocument; -import com.mongodb.hibernate.internal.translate.mongoast.AstNode; import org.bson.BsonWriter; -public record AstInsertCommand(String collection, AstDocument document) implements AstNode { +public record AstInsertCommand(String collection, AstDocument document) implements AstCommand { @Override public void render(BsonWriter writer) { writer.writeStartDocument(); diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstUpdateCommand.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstUpdateCommand.java index 51967163..b08395cf 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstUpdateCommand.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstUpdateCommand.java @@ -17,12 +17,12 @@ package com.mongodb.hibernate.internal.translate.mongoast.command; import com.mongodb.hibernate.internal.translate.mongoast.AstFieldUpdate; -import com.mongodb.hibernate.internal.translate.mongoast.AstNode; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilter; import java.util.List; import org.bson.BsonWriter; -public record AstUpdateCommand(String collection, AstFilter filter, List updates) implements AstNode { +public record AstUpdateCommand(String collection, AstFilter filter, List updates) + implements AstCommand { @Override public void render(BsonWriter writer) { writer.writeStartDocument(); diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstPipeline.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstPipeline.java new file mode 100644 index 00000000..69cfbfc6 --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstPipeline.java @@ -0,0 +1,32 @@ +/* + * 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.command.aggregate; + +import com.mongodb.hibernate.internal.translate.mongoast.AstNode; +import java.util.List; +import org.bson.BsonWriter; + +public record AstPipeline(List stages) implements AstNode { + @Override + public void render(BsonWriter writer) { + writer.writeStartArray(); + { + stages.forEach(stage -> stage.render(writer)); + } + writer.writeEndArray(); + } +} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstStage.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstStage.java new file mode 100644 index 00000000..84751d5b --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstStage.java @@ -0,0 +1,21 @@ +/* + * 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.command.aggregate; + +import com.mongodb.hibernate.internal.translate.mongoast.AstNode; + +public interface AstStage extends AstNode {} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/package-info.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/package-info.java new file mode 100644 index 00000000..266c34c5 --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2024-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. + */ + +/** The program elements within this package are not part of the public API and may be removed or changed at any time */ +@NullMarked +package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStage.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStage.java new file mode 100644 index 00000000..ac07f9a3 --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStage.java @@ -0,0 +1,33 @@ +/* + * 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.command.aggregate.stage; + +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstStage; +import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilter; +import org.bson.BsonWriter; + +public record AstMatchStage(AstFilter filter) implements AstStage { + @Override + public void render(BsonWriter writer) { + writer.writeStartDocument(); + { + writer.writeName("$match"); + filter.render(writer); + } + writer.writeEndDocument(); + } +} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStage.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStage.java new file mode 100644 index 00000000..0c9b4cda --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStage.java @@ -0,0 +1,37 @@ +/* + * 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.command.aggregate.stage; + +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstStage; +import java.util.List; +import org.bson.BsonWriter; + +public record AstProjectStage(List specifications) implements AstStage { + @Override + public void render(BsonWriter writer) { + writer.writeStartDocument(); + { + writer.writeName("$project"); + writer.writeStartDocument(); + { + specifications.forEach(specification -> specification.render(writer)); + } + writer.writeEndDocument(); + } + writer.writeEndDocument(); + } +} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java new file mode 100644 index 00000000..f74c50b4 --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java @@ -0,0 +1,32 @@ +/* + * 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.command.aggregate.stage; + +import com.mongodb.hibernate.internal.translate.mongoast.AstNode; +import org.bson.BsonWriter; + +public abstract class AstProjectStageSpecification implements AstNode { + + public static AstProjectStageSpecification include(String path) { + return new AstProjectStageSpecification() { + @Override + public void render(BsonWriter writer) { + writer.writeInt32(path, 1); + } + }; + } +} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/package-info.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/package-info.java new file mode 100644 index 00000000..c572c03f --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2024-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. + */ + +/** The program elements within this package are not part of the public API and may be removed or changed at any time */ +@NullMarked +package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstExpression.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstExpression.java new file mode 100644 index 00000000..7a98d6f2 --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstExpression.java @@ -0,0 +1,21 @@ +/* + * 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.expression; + +import com.mongodb.hibernate.internal.translate.mongoast.AstNode; + +public interface AstExpression extends AstNode {} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstFieldPathExpression.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstFieldPathExpression.java new file mode 100644 index 00000000..a58aa798 --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstFieldPathExpression.java @@ -0,0 +1,26 @@ +/* + * 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.expression; + +import org.bson.BsonWriter; + +public record AstFieldPathExpression(String path) implements AstExpression { + @Override + public void render(BsonWriter writer) { + writer.writeString(path); + } +} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/package-info.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/package-info.java new file mode 100644 index 00000000..9830bae2 --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2024-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. + */ + +/** The program elements within this package are not part of the public API and may be removed or changed at any time */ +@NullMarked +package com.mongodb.hibernate.internal.translate.mongoast.expression; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilter.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilter.java new file mode 100644 index 00000000..ceadcf93 --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilter.java @@ -0,0 +1,32 @@ +/* + * 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; + +public final class AstMatchesEverythingFilter implements AstFilter { + + public static AstMatchesEverythingFilter INSTANCE = new AstMatchesEverythingFilter(); + + private AstMatchesEverythingFilter() {} + + @Override + public void render(BsonWriter writer) { + writer.writeStartDocument(); + writer.writeEndDocument(); + } +} diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java new file mode 100644 index 00000000..5d82b791 --- /dev/null +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java @@ -0,0 +1,57 @@ +/* + * 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.command; + +import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRender; +import static com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification.include; +import static com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator.EQ; + +import com.mongodb.hibernate.internal.translate.mongoast.AstLiteralValue; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstPipeline; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstStage; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstMatchStage; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStage; +import com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperation; +import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFieldOperationFilter; +import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilterFieldPath; +import java.util.List; +import org.bson.BsonString; +import org.junit.jupiter.api.Test; + +class AstAggregateCommandTests { + + @Test + void testRendering() { + var collection = "books"; + var filter = new AstFieldOperationFilter( + new AstFilterFieldPath("title"), + new AstComparisonFilterOperation(EQ, new AstLiteralValue(new BsonString("In Search of Lost Time")))); + + var projectStageSpecifications = + List.of(include("_id"), include("author"), include("title"), include("publishYear")); + + var stages = List.of(new AstMatchStage(filter), new AstProjectStage(projectStageSpecifications)); + var aggregateCommand = new AstAggregateCommand(collection, new AstPipeline(stages)); + + var expectedJson = + """ + {"aggregate": "books", "pipeline": [{"$match": {"title": {"$eq": "In Search of Lost Time"}}}, {"$project": {"_id": 1, "author": 1, "title": 1, "publishYear": 1}}]}\ + """; + + assertRender(expectedJson, aggregateCommand); + } +} From e6461997b34768c6940597c04fe7a71279eec09f Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Mon, 17 Mar 2025 09:32:04 -0400 Subject: [PATCH 02/33] add testing case to cover AstMatchesEverythingFilter in AstAggregateCommandTests; improve edge case triage logic --- .../translate/AbstractMqlTranslator.java | 67 +++++++++++-------- .../translate/AstVisitorValueDescriptor.java | 2 +- .../command/AstAggregateCommandTests.java | 22 +++++- 3 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index 3bda01ce..3cdf48ce 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -22,14 +22,13 @@ import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_AGGREGATE; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_MUTATION; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_NAME; -import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.FIELD_NAME; +import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.FIELD_PATH; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.FIELD_VALUE; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.FILTER; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.PROJECT_STAGE_SPECIFICATIONS; import static com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification.include; import static com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator.EQ; import static java.lang.String.format; -import static org.hibernate.query.sqm.ComparisonOperator.EQUAL; import static org.hibernate.sql.ast.SqlTreePrinter.logSqlAst; import com.mongodb.hibernate.internal.FeatureNotSupportedException; @@ -50,6 +49,7 @@ import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStage; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification; 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.AstFieldOperationFilter; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilter; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilterFieldPath; @@ -67,6 +67,7 @@ import org.hibernate.internal.util.collections.Stack; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.internal.SqlFragmentPredicate; +import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.tree.expression.Conversion; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; @@ -110,7 +111,6 @@ import org.hibernate.sql.ast.tree.from.FunctionTableReference; import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.QueryPartTableReference; -import org.hibernate.sql.ast.tree.from.StandardTableGroup; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableReferenceJoin; @@ -319,24 +319,28 @@ public void visitParameter(JdbcParameter jdbcParameter) { } @Override - @SuppressWarnings("OperatorPrecedence") public void visitSelectStatement(SelectStatement selectStatement) { logSqlAst(selectStatement); if (!selectStatement.getQueryPart().isRoot()) { throw new FeatureNotSupportedException("Subquery not supported"); } - if (selectStatement.getCteStatements() != null - && !selectStatement.getCteStatements().isEmpty() - || selectStatement.getCteObjects() != null - && !selectStatement.getCteObjects().isEmpty()) { - throw new FeatureNotSupportedException("CTE feature not supported"); + if (!selectStatement.getCteStatements().isEmpty() + || !selectStatement.getCteObjects().isEmpty()) { + throw new FeatureNotSupportedException("CTE not supported"); } selectStatement.getQueryPart().accept(this); } @Override public void visitQuerySpec(QuerySpec querySpec) { - var collection = acceptAndYield(assertNotNull(querySpec.getFromClause()), COLLECTION_NAME); + if (!querySpec.getGroupByClauseExpressions().isEmpty()) { + throw new FeatureNotSupportedException("GroupBy not supported"); + } + if (querySpec.hasSortSpecifications()) { + throw new FeatureNotSupportedException("Sorting not supported"); + } + + var collection = acceptAndYield(querySpec.getFromClause(), COLLECTION_NAME); var whereClauseRestrictions = querySpec.getWhereClauseRestrictions(); var filter = whereClauseRestrictions == null || whereClauseRestrictions.isEmpty() @@ -351,15 +355,18 @@ public void visitQuerySpec(QuerySpec querySpec) { @Override public void visitFromClause(FromClause fromClause) { - if (fromClause.getRoots().size() == 1 - && fromClause.getRoots().get(0) instanceof StandardTableGroup standardTableGroup - && standardTableGroup.getModelPart() instanceof EntityPersister entityPersister - && entityPersister.getQuerySpaces().length == 1) { - affectedTableNames.add(((String[]) entityPersister.getQuerySpaces())[0]); - standardTableGroup.getPrimaryTableReference().accept(this); - } else { + if (fromClause.getRoots().size() != 1) { + throw new FeatureNotSupportedException(); + } + var tableGroup = fromClause.getRoots().get(0); + + if (!(tableGroup.getModelPart() instanceof EntityPersister entityPersister) + || entityPersister.getQuerySpaces().length != 1) { throw new FeatureNotSupportedException(); } + + affectedTableNames.add(((String[]) entityPersister.getQuerySpaces())[0]); + tableGroup.getPrimaryTableReference().accept(this); } @Override @@ -369,17 +376,24 @@ public void visitNamedTableReference(NamedTableReference namedTableReference) { @Override public void visitRelationalPredicate(ComparisonPredicate comparisonPredicate) { - var fieldName = acceptAndYield(comparisonPredicate.getLeftHandExpression(), FIELD_NAME); + var astComparisonFilterOperator = getAstComparisonFilterOperator(comparisonPredicate.getOperator()); + + var fieldPath = acceptAndYield(comparisonPredicate.getLeftHandExpression(), FIELD_PATH); var fieldValue = acceptAndYield(comparisonPredicate.getRightHandExpression(), FIELD_VALUE); - if (comparisonPredicate.getOperator() != EQUAL) { - throw new FeatureNotSupportedException(); - } var filter = new AstFieldOperationFilter( - new AstFilterFieldPath(fieldName), new AstComparisonFilterOperation(EQ, fieldValue)); + new AstFilterFieldPath(fieldPath), + new AstComparisonFilterOperation(astComparisonFilterOperator, fieldValue)); astVisitorValueHolder.yield(FILTER, filter); } + private static AstComparisonFilterOperator getAstComparisonFilterOperator(ComparisonOperator operator) { + return switch (operator) { + case EQUAL -> EQ; + default -> throw new FeatureNotSupportedException("Unsupported operator: " + operator.name()); + }; + } + @Override public void visitSelectClause(SelectClause selectClause) { var projectStageSpecifications = new ArrayList( @@ -389,12 +403,11 @@ public void visitSelectClause(SelectClause selectClause) { if (sqlSelection.isVirtual()) { continue; } - if (sqlSelection.getExpression() instanceof ColumnReference columnReference) { - var columnName = acceptAndYield(columnReference, FIELD_NAME); - projectStageSpecifications.add(include(columnName)); - } else { + if (!(sqlSelection.getExpression() instanceof ColumnReference columnReference)) { throw new FeatureNotSupportedException(); } + var columnName = acceptAndYield(columnReference, FIELD_PATH); + projectStageSpecifications.add(include(columnName)); } astVisitorValueHolder.yield(PROJECT_STAGE_SPECIFICATIONS, projectStageSpecifications); } @@ -404,7 +417,7 @@ public void visitColumnReference(ColumnReference columnReference) { if (columnReference.isColumnExpressionFormula()) { throw new FeatureNotSupportedException(); } - astVisitorValueHolder.yield(FIELD_NAME, columnReference.getColumnExpression()); + astVisitorValueHolder.yield(FIELD_PATH, columnReference.getColumnExpression()); } @Override diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AstVisitorValueDescriptor.java b/src/main/java/com/mongodb/hibernate/internal/translate/AstVisitorValueDescriptor.java index 2b86cd9a..9d0f56ec 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AstVisitorValueDescriptor.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AstVisitorValueDescriptor.java @@ -37,7 +37,7 @@ final class AstVisitorValueDescriptor { static final AstVisitorValueDescriptor COLLECTION_NAME = new AstVisitorValueDescriptor<>(); - static final AstVisitorValueDescriptor FIELD_NAME = new AstVisitorValueDescriptor<>(); + static final AstVisitorValueDescriptor FIELD_PATH = new AstVisitorValueDescriptor<>(); static final AstVisitorValueDescriptor FIELD_VALUE = new AstVisitorValueDescriptor<>(); static final AstVisitorValueDescriptor> PROJECT_STAGE_SPECIFICATIONS = diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java index 5d82b791..df363db4 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java @@ -28,6 +28,7 @@ import com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperation; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFieldOperationFilter; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilterFieldPath; +import com.mongodb.hibernate.internal.translate.mongoast.filter.AstMatchesEverythingFilter; import java.util.List; import org.bson.BsonString; import org.junit.jupiter.api.Test; @@ -35,7 +36,7 @@ class AstAggregateCommandTests { @Test - void testRendering() { + void testRenderingWithExplicitFilter() { var collection = "books"; var filter = new AstFieldOperationFilter( new AstFilterFieldPath("title"), @@ -54,4 +55,23 @@ void testRendering() { assertRender(expectedJson, aggregateCommand); } + + @Test + void testRenderingWithoutFilter() { + var collection = "movies"; + + var projectStageSpecifications = List.of(include("_id"), include("title")); + + var stages = List.of( + new AstMatchStage(AstMatchesEverythingFilter.INSTANCE), + new AstProjectStage(projectStageSpecifications)); + var aggregateCommand = new AstAggregateCommand(collection, new AstPipeline(stages)); + + var expectedJson = + """ + {"aggregate": "movies", "pipeline": [{"$match": {}}, {"$project": {"_id": 1, "title": 1}}]}\ + """; + + assertRender(expectedJson, aggregateCommand); + } } From fcf5e761f3c424abc1d2d3c845737e844b7a016f Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Wed, 19 Mar 2025 15:47:42 -0400 Subject: [PATCH 03/33] add unit testing case to verify affectedTableNames content --- build.gradle.kts | 2 + gradle/libs.versions.toml | 2 + .../translate/AbstractMqlTranslator.java | 10 ++-- .../SelectStatementMqlTranslator.java | 11 +++- .../SelectStatementMqlTranslatorTests.java | 56 +++++++++++++++++++ 5 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 src/test/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslatorTests.java diff --git a/build.gradle.kts b/build.gradle.kts index de630b00..c73a5443 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -179,7 +179,9 @@ dependencies { testImplementation(libs.mockito.junit.jupiter) testRuntimeOnly(libs.junit.platform.launcher) + testCompileOnly(libs.checker.qual) + integrationTestImplementation(libs.mockito.junit.jupiter) @Suppress("UnstableApiUsage") integrationTestImplementation(libs.hibernate.testing) { exclude(group = "org.apache.logging.log4j", module = "log4j-core") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5090b65f..8a4d8a98 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,6 +17,7 @@ slf4j-api = "2.0.16" logback-classic = "1.5.16" mockito = "5.15.2" buildconfig = "5.5.1" +checker-qual = "3.49.1" [libraries] junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } @@ -31,6 +32,7 @@ mongo-java-driver-sync = { module = "org.mongodb:mongodb-driver-sync", version.r sl4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j-api" } logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback-classic" } mockito-junit-jupiter = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockito" } +checker-qual = { module = "org.checkerframework:checker-qual", version.ref = "checker-qual" } [plugins] spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index 3cdf48ce..cbbd51d0 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -16,7 +16,6 @@ package com.mongodb.hibernate.internal.translate; -import static com.mongodb.hibernate.internal.MongoAssertions.assertNotNull; import static com.mongodb.hibernate.internal.MongoAssertions.assertTrue; import static com.mongodb.hibernate.internal.MongoConstants.ID_FIELD_NAME; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_AGGREGATE; @@ -32,7 +31,6 @@ import static org.hibernate.sql.ast.SqlTreePrinter.logSqlAst; import com.mongodb.hibernate.internal.FeatureNotSupportedException; -import com.mongodb.hibernate.internal.extension.service.StandardServiceRegistryScopedState; import com.mongodb.hibernate.internal.translate.mongoast.AstDocument; import com.mongodb.hibernate.internal.translate.mongoast.AstElement; import com.mongodb.hibernate.internal.translate.mongoast.AstFieldUpdate; @@ -166,10 +164,10 @@ abstract class AbstractMqlTranslator implements SqlAstT AbstractMqlTranslator(SessionFactoryImplementor sessionFactory) { this.sessionFactory = sessionFactory; - assertNotNull(sessionFactory - .getServiceRegistry() - .requireService(StandardServiceRegistryScopedState.class) - .getConfiguration()); + /*assertNotNull(sessionFactory + .getServiceRegistry() + .requireService(StandardServiceRegistryScopedState.class) + .getConfiguration());*/ } @Override diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java index 72c8a2a2..bd235136 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java @@ -16,8 +16,11 @@ package com.mongodb.hibernate.internal.translate; +import static com.mongodb.hibernate.internal.VisibleForTesting.AccessModifier.PRIVATE; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_AGGREGATE; +import com.mongodb.hibernate.internal.VisibleForTesting; +import com.mongodb.hibernate.internal.translate.mongoast.command.AstCommand; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.spi.QueryOptions; import org.hibernate.sql.ast.tree.Statement; @@ -38,9 +41,8 @@ final class SelectStatementMqlTranslator extends AbstractMqlTranslator Date: Wed, 19 Mar 2025 19:33:35 -0400 Subject: [PATCH 04/33] Update src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java Co-authored-by: Valentin Kovalenko --- .../com/mongodb/hibernate/BasicCrudIntegrationTests.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java index d0f5b03d..aba0a391 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java @@ -215,7 +215,10 @@ void testLoad() { session.load(loaded, 1); return loaded; }); - assertThat(loadedBook).usingRecursiveComparison().isEqualTo(book); + assertThat(loadedBook) + .usingRecursiveComparison() + .withStrictTypeChecking() + .isEqualTo(book); } } From 6d0985a306a21260be8b454f05a938723343cf1f Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Wed, 19 Mar 2025 19:58:17 -0400 Subject: [PATCH 05/33] use get() instead of load() in loading by primary key testing case --- .../mongodb/hibernate/BasicCrudIntegrationTests.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java index aba0a391..67ed6bb3 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java @@ -199,10 +199,10 @@ void testDynamicUpdate() { } @Nested - class LoadByIdTests { + class LoadByPrimaryKeyTests { @Test - void testLoad() { + void testGetById() { var book = new Book(); book.id = 1; book.title = "In Search of Lost Time"; @@ -210,12 +210,9 @@ void testLoad() { sessionFactoryScope.inTransaction(session -> session.persist(book)); - var loadedBook = sessionFactoryScope.fromTransaction(session -> { - var loaded = new Book(); - session.load(loaded, 1); - return loaded; - }); + var loadedBook = sessionFactoryScope.fromTransaction(session -> session.get(Book.class, 1)); assertThat(loadedBook) + .isNotNull() .usingRecursiveComparison() .withStrictTypeChecking() .isEqualTo(book); From 6837cdc1f12d89b9b7d8e5b9f81593c47e787541 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 20 Mar 2025 09:39:40 -0400 Subject: [PATCH 06/33] improve as per code review comments --- .../hibernate/BasicCrudIntegrationTests.java | 20 +++++++++++- .../translate/AbstractMqlTranslator.java | 5 ++- .../{ => aggregate}/AstAggregateCommand.java | 13 +++++--- .../command/aggregate/AstPipeline.java | 32 ------------------- .../stage/AstProjectStageSpecification.java | 6 ++-- .../mongoast/expression/AstExpression.java | 21 ------------ .../expression/AstFieldPathExpression.java | 26 --------------- .../mongoast/expression/package-info.java | 21 ------------ .../command/AstAggregateCommandTests.java | 6 ++-- 9 files changed, 37 insertions(+), 113 deletions(-) rename src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/{ => aggregate}/AstAggregateCommand.java (76%) delete mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstPipeline.java delete mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstExpression.java delete mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstFieldPathExpression.java delete mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/package-info.java diff --git a/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java index 67ed6bb3..ade540aa 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java @@ -202,9 +202,10 @@ void testDynamicUpdate() { class LoadByPrimaryKeyTests { @Test - void testGetById() { + void testGetByIdWithoutNullValue() { var book = new Book(); book.id = 1; + book.author = "Marcel Proust"; book.title = "In Search of Lost Time"; book.publishYear = 1913; @@ -217,6 +218,23 @@ void testGetById() { .withStrictTypeChecking() .isEqualTo(book); } + + @Test + void testGetByIdWithNullValue() { + var book = new Book(); + book.id = 1; + book.title = "Brave New World"; + book.publishYear = 1932; + + sessionFactoryScope.inTransaction(session -> session.persist(book)); + + var loadedBook = sessionFactoryScope.fromTransaction(session -> session.get(Book.class, 1)); + assertThat(loadedBook) + .isNotNull() + .usingRecursiveComparison() + .withStrictTypeChecking() + .isEqualTo(book); + } } private static void assertCollectionContainsExactly(BsonDocument expectedDoc) { diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index cbbd51d0..3e3126fd 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -36,12 +36,11 @@ import com.mongodb.hibernate.internal.translate.mongoast.AstFieldUpdate; import com.mongodb.hibernate.internal.translate.mongoast.AstNode; import com.mongodb.hibernate.internal.translate.mongoast.AstParameterMarker; -import com.mongodb.hibernate.internal.translate.mongoast.command.AstAggregateCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.AstCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.AstDeleteCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.AstInsertCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.AstUpdateCommand; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstPipeline; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstAggregateCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstStage; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstMatchStage; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStage; @@ -348,7 +347,7 @@ public void visitQuerySpec(QuerySpec querySpec) { var projectStageSpecifications = acceptAndYield(querySpec.getSelectClause(), PROJECT_STAGE_SPECIFICATIONS); var stages = List.of(new AstMatchStage(filter), new AstProjectStage(projectStageSpecifications)); - astVisitorValueHolder.yield(COLLECTION_AGGREGATE, new AstAggregateCommand(collection, new AstPipeline(stages))); + astVisitorValueHolder.yield(COLLECTION_AGGREGATE, new AstAggregateCommand(collection, stages)); } @Override diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommand.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommand.java similarity index 76% rename from src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommand.java rename to src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommand.java index f6171306..73f51e91 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommand.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommand.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package com.mongodb.hibernate.internal.translate.mongoast.command; +package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstPipeline; +import com.mongodb.hibernate.internal.translate.mongoast.command.AstCommand; +import java.util.List; import org.bson.BsonWriter; -public record AstAggregateCommand(String collection, AstPipeline pipeline) implements AstCommand { +public record AstAggregateCommand(String collection, List stages) implements AstCommand { @Override public void render(BsonWriter writer) { @@ -27,7 +28,11 @@ public void render(BsonWriter writer) { { writer.writeString("aggregate", collection); writer.writeName("pipeline"); - pipeline.render(writer); + writer.writeStartArray(); + { + stages.forEach(stage -> stage.render(writer)); + } + writer.writeEndArray(); } writer.writeEndDocument(); } diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstPipeline.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstPipeline.java deleted file mode 100644 index 69cfbfc6..00000000 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstPipeline.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.command.aggregate; - -import com.mongodb.hibernate.internal.translate.mongoast.AstNode; -import java.util.List; -import org.bson.BsonWriter; - -public record AstPipeline(List stages) implements AstNode { - @Override - public void render(BsonWriter writer) { - writer.writeStartArray(); - { - stages.forEach(stage -> stage.render(writer)); - } - writer.writeEndArray(); - } -} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java index f74c50b4..0c185ad0 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java @@ -21,11 +21,13 @@ public abstract class AstProjectStageSpecification implements AstNode { - public static AstProjectStageSpecification include(String path) { + private AstProjectStageSpecification() {} + + public static AstProjectStageSpecification include(String fieldPath) { return new AstProjectStageSpecification() { @Override public void render(BsonWriter writer) { - writer.writeInt32(path, 1); + writer.writeInt32(fieldPath, 1); } }; } diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstExpression.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstExpression.java deleted file mode 100644 index 7a98d6f2..00000000 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstExpression.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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.expression; - -import com.mongodb.hibernate.internal.translate.mongoast.AstNode; - -public interface AstExpression extends AstNode {} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstFieldPathExpression.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstFieldPathExpression.java deleted file mode 100644 index a58aa798..00000000 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/AstFieldPathExpression.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.expression; - -import org.bson.BsonWriter; - -public record AstFieldPathExpression(String path) implements AstExpression { - @Override - public void render(BsonWriter writer) { - writer.writeString(path); - } -} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/package-info.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/package-info.java deleted file mode 100644 index 9830bae2..00000000 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/expression/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024-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. - */ - -/** The program elements within this package are not part of the public API and may be removed or changed at any time */ -@NullMarked -package com.mongodb.hibernate.internal.translate.mongoast.expression; - -import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java index df363db4..ed5ad98d 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java @@ -21,7 +21,7 @@ import static com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator.EQ; import com.mongodb.hibernate.internal.translate.mongoast.AstLiteralValue; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstPipeline; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstAggregateCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstStage; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstMatchStage; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStage; @@ -46,7 +46,7 @@ void testRenderingWithExplicitFilter() { List.of(include("_id"), include("author"), include("title"), include("publishYear")); var stages = List.of(new AstMatchStage(filter), new AstProjectStage(projectStageSpecifications)); - var aggregateCommand = new AstAggregateCommand(collection, new AstPipeline(stages)); + var aggregateCommand = new AstAggregateCommand(collection, stages); var expectedJson = """ @@ -65,7 +65,7 @@ void testRenderingWithoutFilter() { var stages = List.of( new AstMatchStage(AstMatchesEverythingFilter.INSTANCE), new AstProjectStage(projectStageSpecifications)); - var aggregateCommand = new AstAggregateCommand(collection, new AstPipeline(stages)); + var aggregateCommand = new AstAggregateCommand(collection, stages); var expectedJson = """ From 4f10cd632241aa813031b77d1e04a51065b11674 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 20 Mar 2025 09:44:09 -0400 Subject: [PATCH 07/33] use boolean bson type for AstProjectStageSpecification.include() --- .../command/aggregate/stage/AstProjectStageSpecification.java | 2 +- .../translate/mongoast/command/AstAggregateCommandTests.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java index 0c185ad0..45360108 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java @@ -27,7 +27,7 @@ public static AstProjectStageSpecification include(String fieldPath) { return new AstProjectStageSpecification() { @Override public void render(BsonWriter writer) { - writer.writeInt32(fieldPath, 1); + writer.writeBoolean(fieldPath, true); } }; } diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java index ed5ad98d..ccee5b59 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java @@ -50,7 +50,7 @@ void testRenderingWithExplicitFilter() { var expectedJson = """ - {"aggregate": "books", "pipeline": [{"$match": {"title": {"$eq": "In Search of Lost Time"}}}, {"$project": {"_id": 1, "author": 1, "title": 1, "publishYear": 1}}]}\ + {"aggregate": "books", "pipeline": [{"$match": {"title": {"$eq": "In Search of Lost Time"}}}, {"$project": {"_id": true, "author": true, "title": true, "publishYear": true}}]}\ """; assertRender(expectedJson, aggregateCommand); @@ -69,7 +69,7 @@ void testRenderingWithoutFilter() { var expectedJson = """ - {"aggregate": "movies", "pipeline": [{"$match": {}}, {"$project": {"_id": 1, "title": 1}}]}\ + {"aggregate": "movies", "pipeline": [{"$match": {}}, {"$project": {"_id": true, "title": true}}]}\ """; assertRender(expectedJson, aggregateCommand); From 2f2dd02b3d01ae613560effd8b2ea86068591468 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 20 Mar 2025 10:03:31 -0400 Subject: [PATCH 08/33] move AstMatchesEverythingFilterTests to its own testing case --- .../command/AstAggregateCommandTests.java | 22 +------------- .../AstMatchesEverythingFilterTests.java | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 src/test/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilterTests.java diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java index ccee5b59..6f2dfd99 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java @@ -28,7 +28,6 @@ import com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperation; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFieldOperationFilter; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilterFieldPath; -import com.mongodb.hibernate.internal.translate.mongoast.filter.AstMatchesEverythingFilter; import java.util.List; import org.bson.BsonString; import org.junit.jupiter.api.Test; @@ -36,7 +35,7 @@ class AstAggregateCommandTests { @Test - void testRenderingWithExplicitFilter() { + void testRendering() { var collection = "books"; var filter = new AstFieldOperationFilter( new AstFilterFieldPath("title"), @@ -55,23 +54,4 @@ void testRenderingWithExplicitFilter() { assertRender(expectedJson, aggregateCommand); } - - @Test - void testRenderingWithoutFilter() { - var collection = "movies"; - - var projectStageSpecifications = List.of(include("_id"), include("title")); - - var stages = List.of( - new AstMatchStage(AstMatchesEverythingFilter.INSTANCE), - new AstProjectStage(projectStageSpecifications)); - var aggregateCommand = new AstAggregateCommand(collection, stages); - - var expectedJson = - """ - {"aggregate": "movies", "pipeline": [{"$match": {}}, {"$project": {"_id": true, "title": true}}]}\ - """; - - assertRender(expectedJson, aggregateCommand); - } } diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilterTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilterTests.java new file mode 100644 index 00000000..e4ab1b89 --- /dev/null +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilterTests.java @@ -0,0 +1,30 @@ +/* + * 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.assertRender; + +import org.junit.jupiter.api.Test; + +class AstMatchesEverythingFilterTests { + + @Test + void testRendering() { + var expectedJson = "{}"; + assertRender(expectedJson, AstMatchesEverythingFilter.INSTANCE); + } +} From 064046f157be2ed6e18c13b57ba80daf12f1896e Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 20 Mar 2025 10:12:47 -0400 Subject: [PATCH 09/33] reinstate the `stage` package; improve SelectStatementMqlTranslatorTests --- .../translate/AbstractMqlTranslator.java | 12 +++-- .../SelectStatementMqlTranslator.java | 10 +--- .../aggregate/AstAggregateCommand.java | 1 + .../aggregate/stage/AstMatchStage.java | 1 - .../aggregate/stage/AstProjectStage.java | 1 - .../aggregate/{ => stage}/AstStage.java | 2 +- .../SelectStatementMqlTranslatorTests.java | 47 +++++++++++++++---- .../command/AstAggregateCommandTests.java | 2 +- 8 files changed, 48 insertions(+), 28 deletions(-) rename src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/{ => stage}/AstStage.java (97%) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index 3e3126fd..d69ed442 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -16,6 +16,7 @@ package com.mongodb.hibernate.internal.translate; +import static com.mongodb.hibernate.internal.MongoAssertions.assertNotNull; import static com.mongodb.hibernate.internal.MongoAssertions.assertTrue; import static com.mongodb.hibernate.internal.MongoConstants.ID_FIELD_NAME; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_AGGREGATE; @@ -31,6 +32,7 @@ import static org.hibernate.sql.ast.SqlTreePrinter.logSqlAst; import com.mongodb.hibernate.internal.FeatureNotSupportedException; +import com.mongodb.hibernate.internal.extension.service.StandardServiceRegistryScopedState; import com.mongodb.hibernate.internal.translate.mongoast.AstDocument; import com.mongodb.hibernate.internal.translate.mongoast.AstElement; import com.mongodb.hibernate.internal.translate.mongoast.AstFieldUpdate; @@ -41,10 +43,10 @@ import com.mongodb.hibernate.internal.translate.mongoast.command.AstInsertCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.AstUpdateCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstAggregateCommand; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstStage; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstMatchStage; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStage; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.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.AstFieldOperationFilter; @@ -163,10 +165,10 @@ abstract class AbstractMqlTranslator implements SqlAstT AbstractMqlTranslator(SessionFactoryImplementor sessionFactory) { this.sessionFactory = sessionFactory; - /*assertNotNull(sessionFactory - .getServiceRegistry() - .requireService(StandardServiceRegistryScopedState.class) - .getConfiguration());*/ + assertNotNull(sessionFactory + .getServiceRegistry() + .requireService(StandardServiceRegistryScopedState.class) + .getConfiguration()); } @Override diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java index bd235136..3480d983 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java @@ -16,11 +16,8 @@ package com.mongodb.hibernate.internal.translate; -import static com.mongodb.hibernate.internal.VisibleForTesting.AccessModifier.PRIVATE; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_AGGREGATE; -import com.mongodb.hibernate.internal.VisibleForTesting; -import com.mongodb.hibernate.internal.translate.mongoast.command.AstCommand; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.spi.QueryOptions; import org.hibernate.sql.ast.tree.Statement; @@ -41,7 +38,7 @@ final class SelectStatementMqlTranslator extends AbstractMqlTranslator Date: Thu, 20 Mar 2025 10:39:55 -0400 Subject: [PATCH 10/33] change per code review comments --- build.gradle.kts | 1 - .../hibernate/BasicCrudIntegrationTests.java | 6 ++--- .../command/AstAggregateCommandTests.java | 25 +++---------------- 3 files changed, 6 insertions(+), 26 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c73a5443..fa0354e8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -181,7 +181,6 @@ dependencies { testRuntimeOnly(libs.junit.platform.launcher) testCompileOnly(libs.checker.qual) - integrationTestImplementation(libs.mockito.junit.jupiter) @Suppress("UnstableApiUsage") integrationTestImplementation(libs.hibernate.testing) { exclude(group = "org.apache.logging.log4j", module = "log4j-core") diff --git a/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java index ade540aa..78c08eff 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/BasicCrudIntegrationTests.java @@ -199,10 +199,10 @@ void testDynamicUpdate() { } @Nested - class LoadByPrimaryKeyTests { + class SelectTests { @Test - void testGetByIdWithoutNullValue() { + void testGetByPrimaryKeyWithoutNullValueField() { var book = new Book(); book.id = 1; book.author = "Marcel Proust"; @@ -220,7 +220,7 @@ void testGetByIdWithoutNullValue() { } @Test - void testGetByIdWithNullValue() { + void testGetByPrimaryKeyWithNullValueField() { var book = new Book(); book.id = 1; book.title = "Brave New World"; diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java index 0fab0c21..68aadbc5 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java @@ -17,41 +17,22 @@ package com.mongodb.hibernate.internal.translate.mongoast.command; import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRender; -import static com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification.include; -import static com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator.EQ; -import com.mongodb.hibernate.internal.translate.mongoast.AstLiteralValue; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstAggregateCommand; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstMatchStage; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStage; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstStage; -import com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperation; -import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFieldOperationFilter; -import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilterFieldPath; import java.util.List; -import org.bson.BsonString; import org.junit.jupiter.api.Test; class AstAggregateCommandTests { @Test void testRendering() { - var collection = "books"; - var filter = new AstFieldOperationFilter( - new AstFilterFieldPath("title"), - new AstComparisonFilterOperation(EQ, new AstLiteralValue(new BsonString("In Search of Lost Time")))); - - var projectStageSpecifications = - List.of(include("_id"), include("author"), include("title"), include("publishYear")); - - var stages = List.of(new AstMatchStage(filter), new AstProjectStage(projectStageSpecifications)); - var aggregateCommand = new AstAggregateCommand(collection, stages); - + var aggregateCommand = new AstAggregateCommand( + "books", List.of(new AstProjectStage(List.of()), new AstProjectStage(List.of()))); var expectedJson = """ - {"aggregate": "books", "pipeline": [{"$match": {"title": {"$eq": "In Search of Lost Time"}}}, {"$project": {"_id": true, "author": true, "title": true, "publishYear": true}}]}\ + {"aggregate": "books", "pipeline": [{"$project": {}}, {"$project": {}}]}\ """; - assertRender(expectedJson, aggregateCommand); } } From bbc7655b3b762236f372c0c6fa490e237bf88200 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 20 Mar 2025 11:48:08 -0400 Subject: [PATCH 11/33] add more mongo AST rendering tests --- .../AstAggregateCommandTests.java | 3 +- .../aggregate/stage/AstMatchStageTests.java | 44 +++++++++++++++++++ .../aggregate/stage/AstProjectStageTests.java | 37 ++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) rename src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/{ => aggregate}/AstAggregateCommandTests.java (93%) create mode 100644 src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStageTests.java create mode 100644 src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageTests.java diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommandTests.java similarity index 93% rename from src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java rename to src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommandTests.java index 68aadbc5..90f8dff3 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/AstAggregateCommandTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommandTests.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.mongodb.hibernate.internal.translate.mongoast.command; +package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate; import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRender; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstAggregateCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStage; import java.util.List; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStageTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStageTests.java new file mode 100644 index 00000000..18288b1d --- /dev/null +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStageTests.java @@ -0,0 +1,44 @@ +/* + * 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.command.aggregate.stage; + +import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRender; +import static com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator.EQ; + +import com.mongodb.hibernate.internal.translate.mongoast.AstLiteralValue; +import com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperation; +import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFieldOperationFilter; +import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilterFieldPath; +import org.bson.BsonString; +import org.junit.jupiter.api.Test; + +class AstMatchStageTests { + + @Test + void testRendering() { + var astFilter = new AstFieldOperationFilter( + new AstFilterFieldPath("title"), + new AstComparisonFilterOperation(EQ, new AstLiteralValue(new BsonString("Jane Eyre")))); + var astMatchStage = new AstMatchStage(astFilter); + + var expectedJson = + """ + {"$match": {"title": {"$eq": "Jane Eyre"}}}\ + """; + assertRender(expectedJson, astMatchStage); + } +} diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageTests.java new file mode 100644 index 00000000..0e7c34cc --- /dev/null +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageTests.java @@ -0,0 +1,37 @@ +/* + * 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.command.aggregate.stage; + +import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRender; +import static com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification.include; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class AstProjectStageTests { + + @Test + void testRendering() { + var astProjectStageSpecifications = List.of(include("name"), include("address")); + var astProjectStage = new AstProjectStage(astProjectStageSpecifications); + var expectedJson = + """ + {"$project": {"name": true, "address": true}}\ + """; + assertRender(expectedJson, astProjectStage); + } +} From 9a15a74402f68c235bf6498e9a3995062b4e536a Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 20 Mar 2025 11:54:29 -0400 Subject: [PATCH 12/33] resolve conflict with main branch --- .../mongoast/command/aggregate/stage/AstMatchStageTests.java | 3 +-- .../mongoast/command/aggregate/stage/AstProjectStageTests.java | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStageTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStageTests.java index 18288b1d..c9f5a60f 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStageTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStageTests.java @@ -35,8 +35,7 @@ void testRendering() { new AstComparisonFilterOperation(EQ, new AstLiteralValue(new BsonString("Jane Eyre")))); var astMatchStage = new AstMatchStage(astFilter); - var expectedJson = - """ + var expectedJson = """ {"$match": {"title": {"$eq": "Jane Eyre"}}}\ """; assertRender(expectedJson, astMatchStage); diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageTests.java index 0e7c34cc..e1f63686 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageTests.java @@ -28,8 +28,7 @@ class AstProjectStageTests { void testRendering() { var astProjectStageSpecifications = List.of(include("name"), include("address")); var astProjectStage = new AstProjectStage(astProjectStageSpecifications); - var expectedJson = - """ + var expectedJson = """ {"$project": {"name": true, "address": true}}\ """; assertRender(expectedJson, astProjectStage); From d266dd7625c2d63df049c9573454845d01052377 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 20 Mar 2025 13:47:53 -0400 Subject: [PATCH 13/33] Update src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java Co-authored-by: Valentin Kovalenko --- .../hibernate/internal/translate/AbstractMqlTranslator.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index d69ed442..7bdefb53 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -343,12 +343,14 @@ public void visitQuerySpec(QuerySpec querySpec) { var whereClauseRestrictions = querySpec.getWhereClauseRestrictions(); var filter = whereClauseRestrictions == null || whereClauseRestrictions.isEmpty() - ? AstMatchesEverythingFilter.INSTANCE + ? null : acceptAndYield(whereClauseRestrictions, FILTER); var projectStageSpecifications = acceptAndYield(querySpec.getSelectClause(), PROJECT_STAGE_SPECIFICATIONS); - var stages = List.of(new AstMatchStage(filter), new AstProjectStage(projectStageSpecifications)); + var stages = filter == null + ? List.of(new AstProjectStage(projectStageSpecifications)) + : List.of(new AstMatchStage(filter), new AstProjectStage(projectStageSpecifications)); astVisitorValueHolder.yield(COLLECTION_AGGREGATE, new AstAggregateCommand(collection, stages)); } From 1040ff33cab0582230f629e40675e784b8a1e9e8 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 20 Mar 2025 13:33:21 -0400 Subject: [PATCH 14/33] rename columnName variable to fieldPath in AbstractMqlTranslator#visitSelectClause() --- .../hibernate/internal/translate/AbstractMqlTranslator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index 7bdefb53..b7d8968d 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -407,8 +407,8 @@ public void visitSelectClause(SelectClause selectClause) { if (!(sqlSelection.getExpression() instanceof ColumnReference columnReference)) { throw new FeatureNotSupportedException(); } - var columnName = acceptAndYield(columnReference, FIELD_PATH); - projectStageSpecifications.add(include(columnName)); + var fieldPath = acceptAndYield(columnReference, FIELD_PATH); + projectStageSpecifications.add(include(fieldPath)); } astVisitorValueHolder.yield(PROJECT_STAGE_SPECIFICATIONS, projectStageSpecifications); } From 5724937d264a670c74ec4dd4e8e554e76436f460 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 20 Mar 2025 13:52:54 -0400 Subject: [PATCH 15/33] remove unnecessary AstMatchesEverythingFilter --- .../translate/AbstractMqlTranslator.java | 2 -- .../aggregate/AstAggregateCommand.java | 2 +- .../aggregate/stage/AstProjectStage.java | 2 +- .../filter/AstMatchesEverythingFilter.java | 32 ------------------- .../AstMatchesEverythingFilterTests.java | 30 ----------------- 5 files changed, 2 insertions(+), 66 deletions(-) delete mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilter.java delete mode 100644 src/test/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilterTests.java diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index b7d8968d..4dfaa0ac 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -46,13 +46,11 @@ import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstMatchStage; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStage; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.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.AstFieldOperationFilter; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilter; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilterFieldPath; -import com.mongodb.hibernate.internal.translate.mongoast.filter.AstMatchesEverythingFilter; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommand.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommand.java index ffaf7ca4..18bf02fd 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommand.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommand.java @@ -21,7 +21,7 @@ import java.util.List; import org.bson.BsonWriter; -public record AstAggregateCommand(String collection, List stages) implements AstCommand { +public record AstAggregateCommand(String collection, List stages) implements AstCommand { @Override public void render(BsonWriter writer) { diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStage.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStage.java index a14f1f66..0f7cfb9d 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStage.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStage.java @@ -19,7 +19,7 @@ import java.util.List; import org.bson.BsonWriter; -public record AstProjectStage(List specifications) implements AstStage { +public record AstProjectStage(List specifications) implements AstStage { @Override public void render(BsonWriter writer) { writer.writeStartDocument(); diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilter.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilter.java deleted file mode 100644 index ceadcf93..00000000 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilter.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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; - -public final class AstMatchesEverythingFilter implements AstFilter { - - public static AstMatchesEverythingFilter INSTANCE = new AstMatchesEverythingFilter(); - - private AstMatchesEverythingFilter() {} - - @Override - public void render(BsonWriter writer) { - writer.writeStartDocument(); - writer.writeEndDocument(); - } -} diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilterTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilterTests.java deleted file mode 100644 index e4ab1b89..00000000 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/filter/AstMatchesEverythingFilterTests.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.assertRender; - -import org.junit.jupiter.api.Test; - -class AstMatchesEverythingFilterTests { - - @Test - void testRendering() { - var expectedJson = "{}"; - assertRender(expectedJson, AstMatchesEverythingFilter.INSTANCE); - } -} From 6e14c1b582ba84619c2fe27f5b6876b4945b8b56 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 20 Mar 2025 14:11:41 -0400 Subject: [PATCH 16/33] add logic to throw FeatureNotSupportedException in event of jdbcParameterBindings is non-null in SelectStatementMqlTranslator#translate() method --- .../internal/translate/SelectStatementMqlTranslator.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java index 3480d983..94b3d441 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java @@ -18,6 +18,7 @@ import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_AGGREGATE; +import com.mongodb.hibernate.internal.FeatureNotSupportedException; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.spi.QueryOptions; import org.hibernate.sql.ast.tree.Statement; @@ -38,6 +39,9 @@ final class SelectStatementMqlTranslator extends AbstractMqlTranslator Date: Thu, 20 Mar 2025 16:16:32 -0400 Subject: [PATCH 17/33] add validation to SelectStatementMqlTranslator#translate()'s two parameters --- .../internal/translate/SelectStatementMqlTranslator.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java index 94b3d441..b3372f6a 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java @@ -19,6 +19,7 @@ import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_AGGREGATE; import com.mongodb.hibernate.internal.FeatureNotSupportedException; +import org.hibernate.LockOptions; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.spi.QueryOptions; import org.hibernate.sql.ast.tree.Statement; @@ -42,6 +43,9 @@ public JdbcOperationQuerySelect translate( if (jdbcParameterBindings != null) { throw new FeatureNotSupportedException(); } + if (!LockOptions.NONE.equals(queryOptions.getLockOptions())) { + throw new FeatureNotSupportedException(); + } var aggregateCommand = acceptAndYield((Statement) selectStatement, COLLECTION_AGGREGATE); var mql = renderMongoAstNode(aggregateCommand); var sessionFactory = getSessionFactory(); From 21b832d70e00209874e02250f73ec8d258290e81 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 20 Mar 2025 16:26:18 -0400 Subject: [PATCH 18/33] get rid of usage of internal fastSessionServices; keep from exposing mql --- .../translate/SelectStatementMqlTranslator.java | 17 ++++++++++------- .../SelectStatementMqlTranslatorTests.java | 11 ++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java index b3372f6a..5f194ad1 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java @@ -26,15 +26,19 @@ import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducerProvider; import org.jspecify.annotations.Nullable; final class SelectStatementMqlTranslator extends AbstractMqlTranslator { private final SelectStatement selectStatement; + private final JdbcValuesMappingProducerProvider jdbcValuesMappingProducerProvider; SelectStatementMqlTranslator(SessionFactoryImplementor sessionFactory, SelectStatement selectStatement) { super(sessionFactory); this.selectStatement = selectStatement; + jdbcValuesMappingProducerProvider = + sessionFactory.getServiceRegistry().requireService(JdbcValuesMappingProducerProvider.class); } @Override @@ -47,14 +51,13 @@ public JdbcOperationQuerySelect translate( throw new FeatureNotSupportedException(); } var aggregateCommand = acceptAndYield((Statement) selectStatement, COLLECTION_AGGREGATE); - var mql = renderMongoAstNode(aggregateCommand); - var sessionFactory = getSessionFactory(); - var jdbcValuesMappingProducer = sessionFactory - .getFastSessionServices() - .getJdbcValuesMappingProducerProvider() - .buildMappingProducer(selectStatement, sessionFactory); + var jdbcValuesMappingProducer = + jdbcValuesMappingProducerProvider.buildMappingProducer(selectStatement, getSessionFactory()); return new JdbcOperationQuerySelect( - mql, getParameterBinders(), jdbcValuesMappingProducer, getAffectedTableNames()); + renderMongoAstNode(aggregateCommand), + getParameterBinders(), + jdbcValuesMappingProducer, + getAffectedTableNames()); } } diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslatorTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslatorTests.java index 7f99ca3e..92618a3f 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslatorTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslatorTests.java @@ -19,11 +19,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.when; import com.mongodb.hibernate.internal.extension.service.StandardServiceRegistryScopedState; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.internal.FastSessionServices; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.spi.QueryOptions; import org.hibernate.service.spi.ServiceRegistryImplementor; @@ -46,7 +44,6 @@ class SelectStatementMqlTranslatorTests { void testAffectedTableNames( @Mock EntityPersister entityPersister, @Mock(mockMaker = MockMakers.PROXY) SessionFactoryImplementor sessionFactory, - @Mock FastSessionServices fastSessionServices, @Mock JdbcValuesMappingProducerProvider jdbcValuesMappingProducerProvider, @Mock(mockMaker = MockMakers.PROXY) ServiceRegistryImplementor serviceRegistry, @Mock StandardServiceRegistryScopedState standardServiceRegistryScopedState) { @@ -65,10 +62,10 @@ void testAffectedTableNames( selectFromTableName = new SelectStatement(querySpec); } { // prepare `sessionFactory` - when(sessionFactory.getFastSessionServices()).thenReturn(fastSessionServices); - when(fastSessionServices.getJdbcValuesMappingProducerProvider()) - .thenReturn(jdbcValuesMappingProducerProvider); - when(sessionFactory.getServiceRegistry()).thenReturn(serviceRegistry); + doReturn(serviceRegistry).when(sessionFactory).getServiceRegistry(); + doReturn(jdbcValuesMappingProducerProvider) + .when(serviceRegistry) + .requireService(eq(JdbcValuesMappingProducerProvider.class)); doReturn(standardServiceRegistryScopedState) .when(serviceRegistry) .requireService(eq(StandardServiceRegistryScopedState.class)); From 31268bf3ee280577620aa8c7a6ceca5d8fd4be99 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Thu, 20 Mar 2025 16:40:32 -0400 Subject: [PATCH 19/33] improve SelectStatementMqlTranslatorTests to make the table group more similar to real usage (some fields were totally wrong) --- .../SelectStatementMqlTranslatorTests.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslatorTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslatorTests.java index 92618a3f..4cfd635f 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslatorTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslatorTests.java @@ -26,6 +26,7 @@ import org.hibernate.query.spi.QueryOptions; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.spi.NavigablePath; +import org.hibernate.sql.ast.spi.SqlAliasBaseImpl; import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.StandardTableGroup; import org.hibernate.sql.ast.tree.select.QuerySpec; @@ -53,11 +54,17 @@ void testAffectedTableNames( { // prepare `selectFromTableName` doReturn(new String[] {tableName}).when(entityPersister).getQuerySpaces(); - NamedTableReference namedTableReference = new NamedTableReference(tableName, "b"); + var namedTableReference = new NamedTableReference(tableName, "b1_0"); - QuerySpec querySpec = new QuerySpec(true); - StandardTableGroup tableGroup = new StandardTableGroup( - false, new NavigablePath("b"), entityPersister, "b", namedTableReference, null, null); + var querySpec = new QuerySpec(true); + var tableGroup = new StandardTableGroup( + false, + new NavigablePath("Book"), + entityPersister, + null, + namedTableReference, + new SqlAliasBaseImpl("b1"), + sessionFactory); querySpec.getFromClause().addRoot(tableGroup); selectFromTableName = new SelectStatement(querySpec); } @@ -71,7 +78,7 @@ void testAffectedTableNames( .requireService(eq(StandardServiceRegistryScopedState.class)); } - SelectStatementMqlTranslator translator = new SelectStatementMqlTranslator(sessionFactory, selectFromTableName); + var translator = new SelectStatementMqlTranslator(sessionFactory, selectFromTableName); translator.translate(null, QueryOptions.NONE); From 8381eab357932984b897fb35f0c4eb21d9deddda Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Fri, 21 Mar 2025 09:50:14 -0400 Subject: [PATCH 20/33] remove stage package --- .../translate/AbstractMqlTranslator.java | 8 +++---- .../translate/AstVisitorValueDescriptor.java | 2 +- .../aggregate/AstAggregateCommand.java | 1 - .../aggregate/{stage => }/AstMatchStage.java | 2 +- .../{stage => }/AstProjectStage.java | 2 +- .../AstProjectStageSpecification.java | 2 +- .../aggregate/{stage => }/AstStage.java | 2 +- .../command/aggregate/stage/package-info.java | 21 ------------------- .../aggregate/AstAggregateCommandTests.java | 1 - .../{stage => }/AstMatchStageTests.java | 2 +- .../{stage => }/AstProjectStageTests.java | 4 ++-- 11 files changed, 12 insertions(+), 35 deletions(-) rename src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/{stage => }/AstMatchStage.java (98%) rename src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/{stage => }/AstProjectStage.java (98%) rename src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/{stage => }/AstProjectStageSpecification.java (98%) rename src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/{stage => }/AstStage.java (97%) delete mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/package-info.java rename src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/{stage => }/AstMatchStageTests.java (98%) rename src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/{stage => }/AstProjectStageTests.java (94%) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index 4dfaa0ac..e71c1277 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -26,7 +26,7 @@ import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.FIELD_VALUE; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.FILTER; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.PROJECT_STAGE_SPECIFICATIONS; -import static com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification.include; +import static com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstProjectStageSpecification.include; import static com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator.EQ; import static java.lang.String.format; import static org.hibernate.sql.ast.SqlTreePrinter.logSqlAst; @@ -43,9 +43,9 @@ import com.mongodb.hibernate.internal.translate.mongoast.command.AstInsertCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.AstUpdateCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstAggregateCommand; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstMatchStage; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStage; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstMatchStage; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstProjectStage; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstProjectStageSpecification; 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.AstFieldOperationFilter; diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AstVisitorValueDescriptor.java b/src/main/java/com/mongodb/hibernate/internal/translate/AstVisitorValueDescriptor.java index 9d0f56ec..399a4931 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AstVisitorValueDescriptor.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AstVisitorValueDescriptor.java @@ -21,7 +21,7 @@ import com.mongodb.hibernate.internal.translate.mongoast.AstValue; import com.mongodb.hibernate.internal.translate.mongoast.command.AstCommand; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstProjectStageSpecification; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilter; import java.lang.reflect.Modifier; import java.util.Collections; diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommand.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommand.java index 18bf02fd..5e287332 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommand.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommand.java @@ -17,7 +17,6 @@ package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate; import com.mongodb.hibernate.internal.translate.mongoast.command.AstCommand; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstStage; import java.util.List; import org.bson.BsonWriter; diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStage.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstMatchStage.java similarity index 98% rename from src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStage.java rename to src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstMatchStage.java index d2e0f65f..d219aaf4 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStage.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstMatchStage.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage; +package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstFilter; import org.bson.BsonWriter; diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStage.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStage.java similarity index 98% rename from src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStage.java rename to src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStage.java index 0f7cfb9d..86806794 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStage.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStage.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage; +package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate; import java.util.List; import org.bson.BsonWriter; diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageSpecification.java similarity index 98% rename from src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java rename to src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageSpecification.java index 45360108..4fc566f0 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageSpecification.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageSpecification.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage; +package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate; import com.mongodb.hibernate.internal.translate.mongoast.AstNode; import org.bson.BsonWriter; diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstStage.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstStage.java similarity index 97% rename from src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstStage.java rename to src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstStage.java index b12faa63..84751d5b 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstStage.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstStage.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage; +package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate; import com.mongodb.hibernate.internal.translate.mongoast.AstNode; diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/package-info.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/package-info.java deleted file mode 100644 index c572c03f..00000000 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024-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. - */ - -/** The program elements within this package are not part of the public API and may be removed or changed at any time */ -@NullMarked -package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage; - -import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommandTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommandTests.java index 90f8dff3..66c9b80a 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommandTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstAggregateCommandTests.java @@ -18,7 +18,6 @@ import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRender; -import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStage; import java.util.List; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStageTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstMatchStageTests.java similarity index 98% rename from src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStageTests.java rename to src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstMatchStageTests.java index c9f5a60f..9d85a422 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstMatchStageTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstMatchStageTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage; +package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate; import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRender; import static com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator.EQ; diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageTests.java similarity index 94% rename from src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageTests.java rename to src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageTests.java index e1f63686..ad5a0285 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/stage/AstProjectStageTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageTests.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage; +package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate; import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRender; -import static com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.stage.AstProjectStageSpecification.include; +import static com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstProjectStageSpecification.include; import java.util.List; import org.junit.jupiter.api.Test; From cd59077563395c2f6fc16de55962ab3464b180dc Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Mon, 24 Mar 2025 09:18:02 -0400 Subject: [PATCH 21/33] rename translators as per factory method names --- ...tionMqlTranslator.java => ModelMutationMqlTranslator.java} | 4 ++-- .../hibernate/internal/translate/MongoTranslatorFactory.java | 4 ++-- ...ctStatementMqlTranslator.java => SelectMqlTranslator.java} | 4 ++-- ...tMqlTranslatorTests.java => SelectMqlTranslatorTests.java} | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) rename src/main/java/com/mongodb/hibernate/internal/translate/{TableMutationMqlTranslator.java => ModelMutationMqlTranslator.java} (93%) rename src/main/java/com/mongodb/hibernate/internal/translate/{SelectStatementMqlTranslator.java => SelectMqlTranslator.java} (92%) rename src/test/java/com/mongodb/hibernate/internal/translate/{SelectStatementMqlTranslatorTests.java => SelectMqlTranslatorTests.java} (96%) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/TableMutationMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/ModelMutationMqlTranslator.java similarity index 93% rename from src/main/java/com/mongodb/hibernate/internal/translate/TableMutationMqlTranslator.java rename to src/main/java/com/mongodb/hibernate/internal/translate/ModelMutationMqlTranslator.java index adb4f54f..e2a115f2 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/TableMutationMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/ModelMutationMqlTranslator.java @@ -26,11 +26,11 @@ import org.hibernate.sql.model.jdbc.JdbcMutationOperation; import org.jspecify.annotations.Nullable; -final class TableMutationMqlTranslator extends AbstractMqlTranslator { +final class ModelMutationMqlTranslator extends AbstractMqlTranslator { private final TableMutation tableMutation; - TableMutationMqlTranslator(TableMutation tableMutation, SessionFactoryImplementor sessionFactory) { + ModelMutationMqlTranslator(TableMutation tableMutation, SessionFactoryImplementor sessionFactory) { super(sessionFactory); this.tableMutation = tableMutation; } diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/MongoTranslatorFactory.java b/src/main/java/com/mongodb/hibernate/internal/translate/MongoTranslatorFactory.java index 31be705a..9a1a87fe 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/MongoTranslatorFactory.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/MongoTranslatorFactory.java @@ -30,7 +30,7 @@ public final class MongoTranslatorFactory implements SqlAstTranslatorFactory { @Override public SqlAstTranslator buildSelectTranslator( SessionFactoryImplementor sessionFactoryImplementor, SelectStatement selectStatement) { - return new SelectStatementMqlTranslator(sessionFactoryImplementor, selectStatement); + return new SelectMqlTranslator(sessionFactoryImplementor, selectStatement); } @Override @@ -43,6 +43,6 @@ public SqlAstTranslator buildMutationTrans @Override public SqlAstTranslator buildModelMutationTranslator( TableMutation tableMutation, SessionFactoryImplementor sessionFactoryImplementor) { - return new TableMutationMqlTranslator<>(tableMutation, sessionFactoryImplementor); + return new ModelMutationMqlTranslator<>(tableMutation, sessionFactoryImplementor); } } diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java similarity index 92% rename from src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java rename to src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java index 5f194ad1..6bff2d68 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java @@ -29,12 +29,12 @@ import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducerProvider; import org.jspecify.annotations.Nullable; -final class SelectStatementMqlTranslator extends AbstractMqlTranslator { +final class SelectMqlTranslator extends AbstractMqlTranslator { private final SelectStatement selectStatement; private final JdbcValuesMappingProducerProvider jdbcValuesMappingProducerProvider; - SelectStatementMqlTranslator(SessionFactoryImplementor sessionFactory, SelectStatement selectStatement) { + SelectMqlTranslator(SessionFactoryImplementor sessionFactory, SelectStatement selectStatement) { super(sessionFactory); this.selectStatement = selectStatement; jdbcValuesMappingProducerProvider = diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslatorTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslatorTests.java similarity index 96% rename from src/test/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslatorTests.java rename to src/test/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslatorTests.java index 4cfd635f..c7201eb1 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/SelectStatementMqlTranslatorTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslatorTests.java @@ -39,7 +39,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -class SelectStatementMqlTranslatorTests { +class SelectMqlTranslatorTests { @Test void testAffectedTableNames( @@ -78,7 +78,7 @@ void testAffectedTableNames( .requireService(eq(StandardServiceRegistryScopedState.class)); } - var translator = new SelectStatementMqlTranslator(sessionFactory, selectFromTableName); + var translator = new SelectMqlTranslator(sessionFactory, selectFromTableName); translator.translate(null, QueryOptions.NONE); From a5f72d35b7a033d6367677e20c5d6415234ac956 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Mon, 24 Mar 2025 09:53:47 -0400 Subject: [PATCH 22/33] refactor AstProjectStageSpecification to align with existing pattern --- .../translate/AbstractMqlTranslator.java | 4 +-- .../AstProjectStageIncludeSpecification.java | 26 ++++++++++++++ .../AstProjectStageSpecification.java | 15 +------- ...rojectStageIncludeSpeicificationTests.java | 35 +++++++++++++++++++ .../aggregate/AstProjectStageTests.java | 10 +++--- 5 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecification.java create mode 100644 src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpeicificationTests.java diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index f23dd6c4..f2d8fb7c 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -26,7 +26,6 @@ import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.FIELD_VALUE; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.FILTER; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.PROJECT_STAGE_SPECIFICATIONS; -import static com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstProjectStageSpecification.include; import static com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator.EQ; import static java.lang.String.format; import static org.hibernate.sql.ast.SqlTreePrinter.logSqlAst; @@ -45,6 +44,7 @@ import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstAggregateCommand; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstMatchStage; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstProjectStage; +import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstProjectStageIncludeSpecification; import com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstProjectStageSpecification; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperation; import com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator; @@ -403,7 +403,7 @@ public void visitSelectClause(SelectClause selectClause) { throw new FeatureNotSupportedException(); } var fieldPath = acceptAndYield(columnReference, FIELD_PATH); - projectStageSpecifications.add(include(fieldPath)); + projectStageSpecifications.add(new AstProjectStageIncludeSpecification(fieldPath)); } astVisitorValueHolder.yield(PROJECT_STAGE_SPECIFICATIONS, projectStageSpecifications); } diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecification.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecification.java new file mode 100644 index 00000000..baf3da62 --- /dev/null +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecification.java @@ -0,0 +1,26 @@ +/* + * 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.command.aggregate; + +import org.bson.BsonWriter; + +public record AstProjectStageIncludeSpecification(String fieldPath) implements AstProjectStageSpecification { + @Override + public void render(BsonWriter writer) { + writer.writeBoolean(fieldPath, true); + } +} diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageSpecification.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageSpecification.java index 4fc566f0..50959471 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageSpecification.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageSpecification.java @@ -17,18 +17,5 @@ package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate; import com.mongodb.hibernate.internal.translate.mongoast.AstNode; -import org.bson.BsonWriter; -public abstract class AstProjectStageSpecification implements AstNode { - - private AstProjectStageSpecification() {} - - public static AstProjectStageSpecification include(String fieldPath) { - return new AstProjectStageSpecification() { - @Override - public void render(BsonWriter writer) { - writer.writeBoolean(fieldPath, true); - } - }; - } -} +public interface AstProjectStageSpecification extends AstNode {} diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpeicificationTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpeicificationTests.java new file mode 100644 index 00000000..ab7efa35 --- /dev/null +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpeicificationTests.java @@ -0,0 +1,35 @@ +/* + * 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.command.aggregate; + +import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRender; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class AstProjectStageIncludeSpeicificationTests { + + @Test + void testRendering() { + var astProjectStageSpecifications = List.of(new AstProjectStageIncludeSpecification("name")); + var astProjectStage = new AstProjectStage(astProjectStageSpecifications); + var expectedJson = """ + {"$project": {"name": true}}\ + """; + assertRender(expectedJson, astProjectStage); + } +} diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageTests.java index ad5a0285..fcdde16a 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageTests.java @@ -17,20 +17,18 @@ package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate; import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRender; -import static com.mongodb.hibernate.internal.translate.mongoast.command.aggregate.AstProjectStageSpecification.include; -import java.util.List; +import java.util.Collections; import org.junit.jupiter.api.Test; class AstProjectStageTests { @Test void testRendering() { - var astProjectStageSpecifications = List.of(include("name"), include("address")); - var astProjectStage = new AstProjectStage(astProjectStageSpecifications); + var astProjectStage = new AstProjectStage(Collections.emptyList()); var expectedJson = """ - {"$project": {"name": true, "address": true}}\ - """; + {"$project": {}}\ + """; assertRender(expectedJson, astProjectStage); } } From 15f56bf1bda522386563749950501c5baa829255 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Mon, 24 Mar 2025 10:17:15 -0400 Subject: [PATCH 23/33] add QueryOptions assertion logic to both ModelMutationMqlTranslator and SelectMqlTranslator --- .../translate/AbstractMqlTranslator.java | 54 +++++++++++++++++++ .../translate/ModelMutationMqlTranslator.java | 2 +- .../translate/SelectMqlTranslator.java | 5 +- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index f2d8fb7c..490a0fb7 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -64,6 +64,8 @@ import org.hibernate.internal.util.collections.Stack; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.internal.SqlFragmentPredicate; +import org.hibernate.query.spi.Limit; +import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.tree.expression.Conversion; import org.hibernate.sql.ast.Clause; @@ -735,4 +737,56 @@ public void visitOptionalTableUpdate(OptionalTableUpdate optionalTableUpdate) { public void visitCustomTableUpdate(TableUpdateCustomSql tableUpdateCustomSql) { throw new FeatureNotSupportedException(); } + + void checkQueryOptionsSupportability(QueryOptions queryOptions) { + if (queryOptions.getTimeout() != null) { + throw new FeatureNotSupportedException("'timeout' inQueryOptions not supported"); + } + if (queryOptions.getFlushMode() != null) { + throw new FeatureNotSupportedException("'flushMode' in QueryOptions not supported"); + } + if (Boolean.TRUE.equals(queryOptions.isReadOnly())) { + throw new FeatureNotSupportedException("'readOnly' in QueryOptions not supported"); + } + if (queryOptions.getAppliedGraph() != null) { + throw new FeatureNotSupportedException("'appliedGraph' in QueryOptions not supported"); + } + if (queryOptions.getTupleTransformer() != null) { + throw new FeatureNotSupportedException("'tupleTransformer' in QueryOptions not supported"); + } + if (queryOptions.getResultListTransformer() != null) { + throw new FeatureNotSupportedException("'resultListTransformer' in QueryOptions not supported"); + } + if (Boolean.TRUE.equals(queryOptions.isResultCachingEnabled())) { + throw new FeatureNotSupportedException("'resultCaching' in QueryOptions not supported"); + } + if (queryOptions.getDisabledFetchProfiles() != null + && !queryOptions.getDisabledFetchProfiles().isEmpty()) { + throw new FeatureNotSupportedException("'disabledFetchProfiles' in QueryOptions not supported"); + } + if (queryOptions.getEnabledFetchProfiles() != null + && !queryOptions.getEnabledFetchProfiles().isEmpty()) { + throw new FeatureNotSupportedException("'enabledFetchProfiles' in QueryOptions not supported"); + } + if (Boolean.TRUE.equals(queryOptions.getQueryPlanCachingEnabled())) { + throw new FeatureNotSupportedException("'queryPlanCaching' in QueryOptions not supported"); + } + if (queryOptions.getLockOptions() != null + && !queryOptions.getLockOptions().isEmpty()) { + throw new FeatureNotSupportedException("'lockOptions' in QueryOptions not supported"); + } + if (queryOptions.getComment() != null) { + throw new FeatureNotSupportedException("TO-DO-HIBERNATE-53 https://jira.mongodb.org/browse/HIBERNATE-53"); + } + if (queryOptions.getDatabaseHints() != null + && !queryOptions.getDatabaseHints().isEmpty()) { + throw new FeatureNotSupportedException("'databaseHints' in QueryOptions not supported"); + } + if (queryOptions.getFetchSize() != null) { + throw new FeatureNotSupportedException("TO-DO-HIBERNATE-54 https://jira.mongodb.org/browse/HIBERNATE-54"); + } + if (queryOptions.getLimit() != null && !queryOptions.getLimit().isCompatible(Limit.NONE)) { + throw new FeatureNotSupportedException("TO-DO-HIBERNATE-70 https://jira.mongodb.org/browse/HIBERNATE-70"); + } + } } diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/ModelMutationMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/ModelMutationMqlTranslator.java index e2a115f2..071c8fdf 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/ModelMutationMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/ModelMutationMqlTranslator.java @@ -38,7 +38,7 @@ final class ModelMutationMqlTranslator extends @Override public O translate(@Nullable JdbcParameterBindings jdbcParameterBindings, QueryOptions queryOptions) { assertNull(jdbcParameterBindings); - // QueryOptions class is not applicable to table mutation so a dummy value is always passed in + checkQueryOptionsSupportability(queryOptions); var mutationCommand = acceptAndYield(tableMutation, COLLECTION_MUTATION); return tableMutation.createMutationOperation(renderMongoAstNode(mutationCommand), getParameterBinders()); diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java index 6bff2d68..b3bd7f2f 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java @@ -19,7 +19,6 @@ import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_AGGREGATE; import com.mongodb.hibernate.internal.FeatureNotSupportedException; -import org.hibernate.LockOptions; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.spi.QueryOptions; import org.hibernate.sql.ast.tree.Statement; @@ -47,9 +46,7 @@ public JdbcOperationQuerySelect translate( if (jdbcParameterBindings != null) { throw new FeatureNotSupportedException(); } - if (!LockOptions.NONE.equals(queryOptions.getLockOptions())) { - throw new FeatureNotSupportedException(); - } + checkQueryOptionsSupportability(queryOptions); var aggregateCommand = acceptAndYield((Statement) selectStatement, COLLECTION_AGGREGATE); var jdbcValuesMappingProducer = jdbcValuesMappingProducerProvider.buildMappingProducer(selectStatement, getSessionFactory()); From 4fca70d595ba2bd9f4e403e247144c0b67f055e4 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Mon, 24 Mar 2025 22:39:32 -0400 Subject: [PATCH 24/33] revert back nullness checking in AbstractMqlTranslator#visitStandardTableInsert() --- .../hibernate/internal/translate/AbstractMqlTranslator.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index 490a0fb7..11070c49 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -237,7 +237,11 @@ public void visitStandardTableInsert(TableInsertStandard tableInsert) { var astElements = new ArrayList(tableInsert.getNumberOfValueBindings()); for (var columnValueBinding : tableInsert.getValueBindings()) { var fieldName = columnValueBinding.getColumnReference().getColumnExpression(); - var fieldValue = acceptAndYield(columnValueBinding.getValueExpression(), FIELD_VALUE); + var valueExpression = columnValueBinding.getValueExpression(); + if (valueExpression == null) { + throw new FeatureNotSupportedException(); + } + var fieldValue = acceptAndYield(valueExpression, FIELD_VALUE); astElements.add(new AstElement(fieldName, fieldValue)); } astVisitorValueHolder.yield( From bf02e2dd83fa20399da6d4f1b0d0df4335467995 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Tue, 25 Mar 2025 09:42:04 -0400 Subject: [PATCH 25/33] Update src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpeicificationTests.java Co-authored-by: Valentin Kovalenko --- .../aggregate/AstProjectStageIncludeSpeicificationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpeicificationTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpeicificationTests.java index ab7efa35..9b0d5417 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpeicificationTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpeicificationTests.java @@ -21,7 +21,7 @@ import java.util.List; import org.junit.jupiter.api.Test; -class AstProjectStageIncludeSpeicificationTests { +class AstProjectStageIncludeSpecificationTests { @Test void testRendering() { From 8f834b254f0c672d3fa1218f7e318d8549792ec8 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Tue, 25 Mar 2025 10:03:27 -0400 Subject: [PATCH 26/33] move logSqlAst to SelectMqlTranslator#translate() method --- .../internal/translate/AbstractMqlTranslator.java | 1 - .../hibernate/internal/translate/SelectMqlTranslator.java | 4 ++++ ....java => AstProjectStageIncludeSpecificationTests.java} | 7 ++----- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/{AstProjectStageIncludeSpeicificationTests.java => AstProjectStageIncludeSpecificationTests.java} (75%) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index 11070c49..c58f2c22 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -320,7 +320,6 @@ public void visitParameter(JdbcParameter jdbcParameter) { @Override public void visitSelectStatement(SelectStatement selectStatement) { - logSqlAst(selectStatement); if (!selectStatement.getQueryPart().isRoot()) { throw new FeatureNotSupportedException("Subquery not supported"); } diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java index b3bd7f2f..9c00de86 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java @@ -17,6 +17,7 @@ package com.mongodb.hibernate.internal.translate; import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.COLLECTION_AGGREGATE; +import static org.hibernate.sql.ast.SqlTreePrinter.logSqlAst; import com.mongodb.hibernate.internal.FeatureNotSupportedException; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -47,6 +48,9 @@ public JdbcOperationQuerySelect translate( throw new FeatureNotSupportedException(); } checkQueryOptionsSupportability(queryOptions); + + logSqlAst(selectStatement); + var aggregateCommand = acceptAndYield((Statement) selectStatement, COLLECTION_AGGREGATE); var jdbcValuesMappingProducer = jdbcValuesMappingProducerProvider.buildMappingProducer(selectStatement, getSessionFactory()); diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpeicificationTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecificationTests.java similarity index 75% rename from src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpeicificationTests.java rename to src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecificationTests.java index 9b0d5417..af382e6c 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpeicificationTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecificationTests.java @@ -18,18 +18,15 @@ import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRender; -import java.util.List; import org.junit.jupiter.api.Test; class AstProjectStageIncludeSpecificationTests { @Test void testRendering() { - var astProjectStageSpecifications = List.of(new AstProjectStageIncludeSpecification("name")); - var astProjectStage = new AstProjectStage(astProjectStageSpecifications); var expectedJson = """ - {"$project": {"name": true}}\ + "name": true\ """; - assertRender(expectedJson, astProjectStage); + assertRender(expectedJson, new AstProjectStageIncludeSpecification("name")); } } From 6fa73e122cc8dfb871f12cd0c5718add12bcb545 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Tue, 25 Mar 2025 10:09:00 -0400 Subject: [PATCH 27/33] add validation of QuerySpec#hasOffsetOrFetchClause() in AbstractMqlTranslator#visitQuerySpec --- .../internal/translate/AbstractMqlTranslator.java | 4 +++- .../AstProjectStageIncludeSpecificationTests.java | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index c58f2c22..a2ad0d59 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -28,7 +28,6 @@ import static com.mongodb.hibernate.internal.translate.AstVisitorValueDescriptor.PROJECT_STAGE_SPECIFICATIONS; import static com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator.EQ; import static java.lang.String.format; -import static org.hibernate.sql.ast.SqlTreePrinter.logSqlAst; import com.mongodb.hibernate.internal.FeatureNotSupportedException; import com.mongodb.hibernate.internal.extension.service.StandardServiceRegistryScopedState; @@ -338,6 +337,9 @@ public void visitQuerySpec(QuerySpec querySpec) { if (querySpec.hasSortSpecifications()) { throw new FeatureNotSupportedException("Sorting not supported"); } + if (querySpec.hasOffsetOrFetchClause()) { + throw new FeatureNotSupportedException("TO-DO-HIBERNATE-70 https://jira.mongodb.org/browse/HIBERNATE-70"); + } var collection = acceptAndYield(querySpec.getFromClause(), COLLECTION_NAME); diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecificationTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecificationTests.java index af382e6c..9b0d5417 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecificationTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecificationTests.java @@ -18,15 +18,18 @@ import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRender; +import java.util.List; import org.junit.jupiter.api.Test; class AstProjectStageIncludeSpecificationTests { @Test void testRendering() { + var astProjectStageSpecifications = List.of(new AstProjectStageIncludeSpecification("name")); + var astProjectStage = new AstProjectStage(astProjectStageSpecifications); var expectedJson = """ - "name": true\ + {"$project": {"name": true}}\ """; - assertRender(expectedJson, new AstProjectStageIncludeSpecification("name")); + assertRender(expectedJson, astProjectStage); } } From 2c26854374e30e9b620c2ce91eeb10d2cef069cc Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Tue, 25 Mar 2025 10:14:34 -0400 Subject: [PATCH 28/33] add validation of SelectClause#isDistinct() in AbstractMqlTranslator#visitSelectClause() --- .../hibernate/internal/translate/AbstractMqlTranslator.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index a2ad0d59..9cbcc8d4 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -399,6 +399,9 @@ private static AstComparisonFilterOperator getAstComparisonFilterOperator(Compar @Override public void visitSelectClause(SelectClause selectClause) { + if (selectClause.isDistinct()) { + throw new FeatureNotSupportedException(); + } var projectStageSpecifications = new ArrayList( selectClause.getSqlSelections().size()); From d1e68a1ac0f7c031cadab35b1e7c97bd15243ba8 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Tue, 25 Mar 2025 10:22:41 -0400 Subject: [PATCH 29/33] simplify AbstractMqlTranslator#checkQueryOptionsSupportability by using 'queryOptions.getLimit().isEmpty()' --- .../hibernate/internal/translate/AbstractMqlTranslator.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index 9cbcc8d4..9fa75132 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -63,7 +63,6 @@ import org.hibernate.internal.util.collections.Stack; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.internal.SqlFragmentPredicate; -import org.hibernate.query.spi.Limit; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.tree.expression.Conversion; @@ -793,7 +792,7 @@ void checkQueryOptionsSupportability(QueryOptions queryOptions) { if (queryOptions.getFetchSize() != null) { throw new FeatureNotSupportedException("TO-DO-HIBERNATE-54 https://jira.mongodb.org/browse/HIBERNATE-54"); } - if (queryOptions.getLimit() != null && !queryOptions.getLimit().isCompatible(Limit.NONE)) { + if (queryOptions.getLimit() != null && !queryOptions.getLimit().isEmpty()) { throw new FeatureNotSupportedException("TO-DO-HIBERNATE-70 https://jira.mongodb.org/browse/HIBERNATE-70"); } } From e7c6c802be05a9b6494293c20eb55f71dda14c4a Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Tue, 25 Mar 2025 12:12:56 -0400 Subject: [PATCH 30/33] improve AstProjectStageIncludeSpecificationTests by introducing AstNodeAssertions#assertNameValueRender() method --- .../translate/mongoast/AstNodeAssertions.java | 14 ++++++++++++++ .../AstProjectStageIncludeSpecificationTests.java | 10 ++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/AstNodeAssertions.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/AstNodeAssertions.java index eab60d59..aff879e7 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/AstNodeAssertions.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/AstNodeAssertions.java @@ -27,9 +27,23 @@ public final class AstNodeAssertions { private AstNodeAssertions() {} public static void assertRender(String expectedJson, AstNode node) { + doAssertRender(expectedJson, node, false); + } + + public static void assertNameValueRender(String expectedJson, AstNode node) { + doAssertRender(expectedJson, node, true); + } + + private static void doAssertRender(String expectedJson, AstNode node, boolean isNameValue) { try (var stringWriter = new StringWriter(); var jsonWriter = new JsonWriter(stringWriter)) { + if (isNameValue) { + jsonWriter.writeStartDocument(); + } node.render(jsonWriter); + if (isNameValue) { + jsonWriter.writeEndDocument(); + } jsonWriter.flush(); var actualJson = stringWriter.toString(); assertEquals(expectedJson, actualJson); diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecificationTests.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecificationTests.java index 9b0d5417..c619d1ef 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecificationTests.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecificationTests.java @@ -16,20 +16,18 @@ package com.mongodb.hibernate.internal.translate.mongoast.command.aggregate; -import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertRender; +import static com.mongodb.hibernate.internal.translate.mongoast.AstNodeAssertions.assertNameValueRender; -import java.util.List; import org.junit.jupiter.api.Test; class AstProjectStageIncludeSpecificationTests { @Test void testRendering() { - var astProjectStageSpecifications = List.of(new AstProjectStageIncludeSpecification("name")); - var astProjectStage = new AstProjectStage(astProjectStageSpecifications); + var projectStageIncludeSpecification = new AstProjectStageIncludeSpecification("name"); var expectedJson = """ - {"$project": {"name": true}}\ + {"name": true}\ """; - assertRender(expectedJson, astProjectStage); + assertNameValueRender(expectedJson, projectStageIncludeSpecification); } } From 4a165c69c408dcbd95f80742bf4f24eeae4aae9e Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Tue, 25 Mar 2025 17:06:34 -0400 Subject: [PATCH 31/33] some changes per code review comments --- .../hibernate/internal/translate/SelectMqlTranslator.java | 5 +++-- .../internal/translate/mongoast/AstNodeAssertions.java | 2 +- .../aggregate/AstProjectStageIncludeSpecificationTests.java | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java index 9c00de86..dfa6e131 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/SelectMqlTranslator.java @@ -44,13 +44,14 @@ final class SelectMqlTranslator extends AbstractMqlTranslator Date: Tue, 25 Mar 2025 21:00:21 -0400 Subject: [PATCH 32/33] further changes per code review comments --- .../aggregate/AstProjectStageIncludeSpecification.java | 4 ++-- .../internal/translate/mongoast/AstNodeAssertions.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecification.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecification.java index baf3da62..b5acdcce 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecification.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/command/aggregate/AstProjectStageIncludeSpecification.java @@ -18,9 +18,9 @@ import org.bson.BsonWriter; -public record AstProjectStageIncludeSpecification(String fieldPath) implements AstProjectStageSpecification { +public record AstProjectStageIncludeSpecification(String field) implements AstProjectStageSpecification { @Override public void render(BsonWriter writer) { - writer.writeBoolean(fieldPath, true); + writer.writeBoolean(field, true); } } diff --git a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/AstNodeAssertions.java b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/AstNodeAssertions.java index 0ab86948..85dfc3a2 100644 --- a/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/AstNodeAssertions.java +++ b/src/test/java/com/mongodb/hibernate/internal/translate/mongoast/AstNodeAssertions.java @@ -34,14 +34,14 @@ public static void assertElementRender(String expectedJson, AstNode node) { doAssertRender(expectedJson, node, true); } - private static void doAssertRender(String expectedJson, AstNode node, boolean isNameValue) { + private static void doAssertRender(String expectedJson, AstNode node, boolean isElement) { try (var stringWriter = new StringWriter(); var jsonWriter = new JsonWriter(stringWriter)) { - if (isNameValue) { + if (isElement) { jsonWriter.writeStartDocument(); } node.render(jsonWriter); - if (isNameValue) { + if (isElement) { jsonWriter.writeEndDocument(); } jsonWriter.flush(); From c779cfd79453f0f49e80faf252901903e631aed6 Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Wed, 26 Mar 2025 07:57:03 -0400 Subject: [PATCH 33/33] rename 'fieldPath' variable to 'fielld' in AbstractMqlTranslator#visitSelectClause() --- .../hibernate/internal/translate/AbstractMqlTranslator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java index 9fa75132..b3fcff3d 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -411,8 +411,8 @@ public void visitSelectClause(SelectClause selectClause) { if (!(sqlSelection.getExpression() instanceof ColumnReference columnReference)) { throw new FeatureNotSupportedException(); } - var fieldPath = acceptAndYield(columnReference, FIELD_PATH); - projectStageSpecifications.add(new AstProjectStageIncludeSpecification(fieldPath)); + var field = acceptAndYield(columnReference, FIELD_PATH); + projectStageSpecifications.add(new AstProjectStageIncludeSpecification(field)); } astVisitorValueHolder.yield(PROJECT_STAGE_SPECIFICATIONS, projectStageSpecifications); }