diff --git a/src/integrationTest/java/com/mongodb/hibernate/query/select/AbstractSelectionQueryIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/query/select/AbstractSelectionQueryIntegrationTests.java new file mode 100644 index 00000000..d5131107 --- /dev/null +++ b/src/integrationTest/java/com/mongodb/hibernate/query/select/AbstractSelectionQueryIntegrationTests.java @@ -0,0 +1,142 @@ +/* + * 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.query.select; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.mongodb.hibernate.TestCommandListener; +import com.mongodb.hibernate.junit.MongoExtension; +import java.util.List; +import java.util.function.Consumer; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.bson.BsonDocument; +import org.hibernate.query.SelectionQuery; +import org.hibernate.testing.orm.junit.ServiceRegistryScope; +import org.hibernate.testing.orm.junit.ServiceRegistryScopeAware; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.SessionFactoryScopeAware; +import org.junit.jupiter.api.extension.ExtendWith; + +@SessionFactory(exportSchema = false) +@ExtendWith(MongoExtension.class) +abstract class AbstractSelectionQueryIntegrationTests implements SessionFactoryScopeAware, ServiceRegistryScopeAware { + + private SessionFactoryScope sessionFactoryScope; + + private TestCommandListener testCommandListener; + + SessionFactoryScope getSessionFactoryScope() { + return sessionFactoryScope; + } + + TestCommandListener getTestCommandListener() { + return testCommandListener; + } + + @Override + public void injectSessionFactoryScope(SessionFactoryScope sessionFactoryScope) { + this.sessionFactoryScope = sessionFactoryScope; + } + + @Override + public void injectServiceRegistryScope(ServiceRegistryScope serviceRegistryScope) { + this.testCommandListener = serviceRegistryScope.getRegistry().requireService(TestCommandListener.class); + } + + void assertSelectionQuery( + String hql, + Class resultType, + Consumer> queryPostProcessor, + String expectedMql, + List expectedResultList) { + assertSelectionQuery(hql, resultType, queryPostProcessor, expectedMql, resultList -> assertThat(resultList) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyElementsOf(expectedResultList)); + } + + void assertSelectionQuery(String hql, Class resultType, String expectedMql, List expectedResultList) { + assertSelectionQuery(hql, resultType, null, expectedMql, expectedResultList); + } + + void assertSelectionQuery( + String hql, + Class resultType, + Consumer> queryPostProcessor, + String expectedMql, + Consumer> resultListVerifier) { + sessionFactoryScope.inTransaction(session -> { + var selectionQuery = session.createSelectionQuery(hql, resultType); + if (queryPostProcessor != null) { + queryPostProcessor.accept(selectionQuery); + } + var resultList = selectionQuery.getResultList(); + + assertActualCommand(BsonDocument.parse(expectedMql)); + + resultListVerifier.accept(resultList); + }); + } + + void assertSelectionQuery( + String hql, Class resultType, String expectedMql, Consumer> resultListVerifier) { + assertSelectionQuery(hql, resultType, null, expectedMql, resultListVerifier); + } + + void assertSelectQueryFailure( + String hql, + Class resultType, + Consumer> queryPostProcessor, + Class expectedExceptionType, + String expectedExceptionMessage, + Object... expectedExceptionMessageParameters) { + sessionFactoryScope.inTransaction(session -> assertThatThrownBy(() -> { + var selectionQuery = session.createSelectionQuery(hql, resultType); + if (queryPostProcessor != null) { + queryPostProcessor.accept(selectionQuery); + } + selectionQuery.getResultList(); + }) + .isInstanceOf(expectedExceptionType) + .hasMessage(expectedExceptionMessage, expectedExceptionMessageParameters)); + } + + void assertSelectQueryFailure( + String hql, + Class resultType, + Class expectedExceptionType, + String expectedExceptionMessage, + Object... expectedExceptionMessageParameters) { + assertSelectQueryFailure( + hql, + resultType, + null, + expectedExceptionType, + expectedExceptionMessage, + expectedExceptionMessageParameters); + } + + void assertActualCommand(BsonDocument expectedCommand) { + var capturedCommands = testCommandListener.getStartedCommands(); + + assertThat(capturedCommands) + .singleElement() + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsAllEntriesOf(expectedCommand); + } +} diff --git a/src/integrationTest/java/com/mongodb/hibernate/query/select/Book.java b/src/integrationTest/java/com/mongodb/hibernate/query/select/Book.java index 82e8ef62..c0f92706 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/query/select/Book.java +++ b/src/integrationTest/java/com/mongodb/hibernate/query/select/Book.java @@ -30,12 +30,12 @@ public class Book { @ObjectIdGenerator ObjectId id; - public Book() {} + Book() {} - String title; - Boolean outOfStock; - Integer publishYear; - Long isbn13; - Double discount; - BigDecimal price; + String title = ""; + Boolean outOfStock = false; + Integer publishYear = 0; + Long isbn13 = 0L; + Double discount = 0.0D; + BigDecimal price = new BigDecimal("0.0"); } diff --git a/src/integrationTest/java/com/mongodb/hibernate/query/select/BooleanExpressionWhereClauseIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/query/select/BooleanExpressionWhereClauseIntegrationTests.java new file mode 100644 index 00000000..2bdb7d5e --- /dev/null +++ b/src/integrationTest/java/com/mongodb/hibernate/query/select/BooleanExpressionWhereClauseIntegrationTests.java @@ -0,0 +1,70 @@ +/* + * 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.query.select; + +import static java.util.Collections.singletonList; + +import com.mongodb.hibernate.internal.FeatureNotSupportedException; +import org.hibernate.testing.orm.junit.DomainModel; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +@DomainModel(annotatedClasses = Book.class) +class BooleanExpressionWhereClauseIntegrationTests extends AbstractSelectionQueryIntegrationTests { + + private Book bookOutOfStock; + private Book bookInStock; + + @BeforeEach + void beforeEach() { + bookOutOfStock = new Book(); + bookOutOfStock.outOfStock = true; + + bookInStock = new Book(); + bookInStock.outOfStock = false; + + getSessionFactoryScope().inTransaction(session -> { + session.persist(bookOutOfStock); + session.persist(bookInStock); + }); + + getTestCommandListener().clear(); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testBooleanFieldPathExpression(boolean negated) { + assertSelectionQuery( + "from Book where" + (negated ? " not " : " ") + "outOfStock", + Book.class, + "{'aggregate': 'books', 'pipeline': [{'$match': {'outOfStock': {'$eq': " + + (negated ? "false" : "true") + + "}}}, {'$project': {'_id': true, 'discount': true, 'isbn13': true, 'outOfStock': true, 'price': true, 'publishYear': true, 'title': true}}]}", + negated ? singletonList(bookInStock) : singletonList(bookOutOfStock)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testNonFieldPathExpressionNotSupported(final boolean booleanLiteral) { + assertSelectQueryFailure( + "from Book where " + booleanLiteral, + Book.class, + FeatureNotSupportedException.class, + "Expression not of field path not supported"); + } +} diff --git a/src/integrationTest/java/com/mongodb/hibernate/query/select/SimpleSelectQueryIntegrationTests.java b/src/integrationTest/java/com/mongodb/hibernate/query/select/SimpleSelectQueryIntegrationTests.java index 42a86e76..30dde19d 100644 --- a/src/integrationTest/java/com/mongodb/hibernate/query/select/SimpleSelectQueryIntegrationTests.java +++ b/src/integrationTest/java/com/mongodb/hibernate/query/select/SimpleSelectQueryIntegrationTests.java @@ -17,55 +17,25 @@ package com.mongodb.hibernate.query.select; import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import com.mongodb.hibernate.TestCommandListener; import com.mongodb.hibernate.internal.FeatureNotSupportedException; -import com.mongodb.hibernate.junit.MongoExtension; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; import java.math.BigDecimal; import java.util.Arrays; import java.util.List; -import java.util.function.Consumer; -import org.assertj.core.api.InstanceOfAssertFactories; -import org.bson.BsonDocument; -import org.hibernate.query.SelectionQuery; import org.hibernate.query.SemanticException; import org.hibernate.testing.orm.junit.DomainModel; -import org.hibernate.testing.orm.junit.ServiceRegistryScope; -import org.hibernate.testing.orm.junit.ServiceRegistryScopeAware; -import org.hibernate.testing.orm.junit.SessionFactory; -import org.hibernate.testing.orm.junit.SessionFactoryScope; -import org.hibernate.testing.orm.junit.SessionFactoryScopeAware; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -@SessionFactory(exportSchema = false) @DomainModel(annotatedClasses = {SimpleSelectQueryIntegrationTests.Contact.class, Book.class}) -@ExtendWith(MongoExtension.class) -class SimpleSelectQueryIntegrationTests implements SessionFactoryScopeAware, ServiceRegistryScopeAware { - - private SessionFactoryScope sessionFactoryScope; - - private TestCommandListener testCommandListener; - - @Override - public void injectSessionFactoryScope(SessionFactoryScope sessionFactoryScope) { - this.sessionFactoryScope = sessionFactoryScope; - } - - @Override - public void injectServiceRegistryScope(ServiceRegistryScope serviceRegistryScope) { - this.testCommandListener = serviceRegistryScope.getRegistry().requireService(TestCommandListener.class); - } +class SimpleSelectQueryIntegrationTests extends AbstractSelectionQueryIntegrationTests { @Nested class QueryTests { @@ -82,14 +52,14 @@ private static List getTestingContacts(int... ids) { .mapToObj(id -> testingContacts.stream() .filter(c -> c.id == id) .findFirst() - .orElseThrow(() -> new IllegalArgumentException("id not exists: " + id))) + .orElseThrow(() -> new IllegalArgumentException("id does not exist: " + id))) .toList(); } @BeforeEach void beforeEach() { - sessionFactoryScope.inTransaction(session -> testingContacts.forEach(session::persist)); - testCommandListener.clear(); + getSessionFactoryScope().inTransaction(session -> testingContacts.forEach(session::persist)); + getTestCommandListener().clear(); } @ParameterizedTest @@ -183,7 +153,6 @@ void testSingleNegation() { assertSelectionQuery( "from Contact where age > 18 and not (country = 'USA')", Contact.class, - null, "{'aggregate': 'contacts', 'pipeline': [{'$match': {'$and': [{'age': {'$gt': 18}}, {'$nor': [{'country': {'$eq': 'USA'}}]}]}}, {'$project': {'_id': true, 'age': true, 'country': true, 'name': true}}]}", getTestingContacts(2, 4)); } @@ -193,7 +162,6 @@ void testSingleNegationWithAnd() { assertSelectionQuery( "from Contact where not (country = 'USA' and age > 18)", Contact.class, - null, "{'aggregate': 'contacts', 'pipeline': [{'$match': {'$nor': [{'$and': [{'country': {'$eq': 'USA'}}, {'age': {'$gt': {'$numberInt': '18'}}}]}]}}, {'$project': {'_id': true, 'age': true, 'country': true, 'name': true}}]}", getTestingContacts(1, 2, 3, 4)); } @@ -203,7 +171,6 @@ void testSingleNegationWithOr() { assertSelectionQuery( "from Contact where not (country = 'USA' or age > 18)", Contact.class, - null, "{'aggregate': 'contacts', 'pipeline': [{'$match': {'$nor': [{'$or': [{'country': {'$eq': 'USA'}}, {'age': {'$gt': {'$numberInt': '18'}}}]}]}}, {'$project': {'_id': true, 'age': true, 'country': true, 'name': true}}]}", getTestingContacts(3)); } @@ -213,7 +180,6 @@ void testSingleNegationWithAndOr() { assertSelectionQuery( "from Contact where not (country = 'USA' and age > 18 or age < 25)", Contact.class, - null, "{'aggregate': 'contacts', 'pipeline': [{'$match': {'$nor': [{'$or': [{'$and': [{'country': {'$eq': 'USA'}}, {'age': {'$gt': {'$numberInt': '18'}}}]}," + " {'age': {'$lt': {'$numberInt': '25'}}}]}]}}, {'$project': {'_id': true, 'age': true, 'country': true, 'name': true}}]}", getTestingContacts(2, 4)); @@ -224,7 +190,6 @@ void testDoubleNegation() { assertSelectionQuery( "from Contact where age > 18 and not ( not (country = 'USA') )", Contact.class, - null, "{'aggregate': 'contacts', 'pipeline': [{'$match': {'$and': [{'age': {'$gt': 18}}, {'$nor': [{'$nor': [{'country': {'$eq': 'USA'}}]}]}]}}, {'$project': {'_id': true, 'age': true, 'country': true, 'name': true}}]}", getTestingContacts(5)); } @@ -254,7 +219,6 @@ void testProjectUsingWrongAlias() { assertSelectQueryFailure( "select k.name, c.age from Contact as c where c.country = :country", Contact.class, - null, SemanticException.class, "Could not interpret path expression '%s'", "k.name"); @@ -268,7 +232,6 @@ void testComparisonBetweenFieldAndNonValueNotSupported1() { assertSelectQueryFailure( "from Contact as c where c.age = c.id + 1", Contact.class, - null, FeatureNotSupportedException.class, "Only the following comparisons are supported: field vs literal, field vs parameter"); } @@ -278,7 +241,6 @@ void testComparisonBetweenValuesNotSupported() { assertSelectQueryFailure( "from Contact where 1 = 1", Contact.class, - null, FeatureNotSupportedException.class, "Only the following comparisons are supported: field vs literal, field vs parameter"); } @@ -288,7 +250,6 @@ void testComparisonBetweenFieldsNotSupported() { assertSelectQueryFailure( "from Contact where age = id", Contact.class, - null, FeatureNotSupportedException.class, "Only the following comparisons are supported: field vs literal, field vs parameter"); } @@ -325,11 +286,11 @@ void testNullParameterNotSupported() { @Test void testQueryPlanCacheIsSupported() { - sessionFactoryScope.inTransaction( - session -> assertThatCode(() -> session.createSelectionQuery("from Contact", Contact.class) + getSessionFactoryScope().inTransaction(session -> assertThatCode( + () -> session.createSelectionQuery("from Contact", Contact.class) .setQueryPlanCacheable(true) .getResultList()) - .doesNotThrowAnyException()); + .doesNotThrowAnyException()); } } @@ -347,9 +308,9 @@ void beforeEach() { testingBook.isbn13 = 9780310904168L; testingBook.discount = 0.25; testingBook.price = new BigDecimal("123.50"); - sessionFactoryScope.inTransaction(session -> session.persist(testingBook)); + getSessionFactoryScope().inTransaction(session -> session.persist(testingBook)); - testCommandListener.clear(); + getTestCommandListener().clear(); } @Test @@ -357,7 +318,6 @@ void testBoolean() { assertSelectionQuery( "from Book where outOfStock = true", Book.class, - null, "{'aggregate': 'books', 'pipeline': [{'$match': {'outOfStock': {'$eq': true}}}, {'$project': {'_id': true, 'discount': true, 'isbn13': true, 'outOfStock': true, 'price': true, 'publishYear': true, 'title': true}}]}", singletonList(testingBook)); } @@ -367,7 +327,6 @@ void testInteger() { assertSelectionQuery( "from Book where publishYear = 1995", Book.class, - null, "{'aggregate': 'books', 'pipeline': [{'$match': {'publishYear': {'$eq': 1995}}}, {'$project': {'_id': true, 'discount': true, 'isbn13': true, 'outOfStock': true, 'price': true, 'publishYear': true, 'title': true}}]}", singletonList(testingBook)); } @@ -377,7 +336,6 @@ void testLong() { assertSelectionQuery( "from Book where isbn13 = 9780310904168L", Book.class, - null, "{'aggregate': 'books', 'pipeline': [{'$match': {'isbn13': {'$eq': 9780310904168}}}, {'$project': {'_id': true, 'discount': true, 'isbn13': true, 'outOfStock': true, 'price': true, 'publishYear': true, 'title': true}}]}", singletonList(testingBook)); } @@ -387,7 +345,6 @@ void testDouble() { assertSelectionQuery( "from Book where discount = 0.25D", Book.class, - null, "{'aggregate': 'books', 'pipeline': [{'$match': {'discount': {'$eq': 0.25}}}, {'$project': {'_id': true, 'discount': true, 'isbn13': true, 'outOfStock': true, 'price': true, 'publishYear': true, 'title': true}}]}", singletonList(testingBook)); } @@ -397,7 +354,6 @@ void testString() { assertSelectionQuery( "from Book where title = 'Holy Bible'", Book.class, - null, "{'aggregate': 'books', 'pipeline': [{'$match': {'title': {'$eq': 'Holy Bible'}}}, {'$project': {'_id': true, 'discount': true, 'isbn13': true, 'outOfStock': true, 'price': true, 'publishYear': true, 'title': true}}]}", singletonList(testingBook)); } @@ -407,60 +363,11 @@ void testBigDecimal() { assertSelectionQuery( "from Book where price = 123.50BD", Book.class, - null, "{'aggregate': 'books', 'pipeline': [{'$match': {'price': {'$eq': {'$numberDecimal': '123.50'}}}}, {'$project': {'_id': true, 'discount': true, 'isbn13': true, 'outOfStock': true, 'price': true, 'publishYear': true, 'title': true}}]}", singletonList(testingBook)); } } - private void assertSelectionQuery( - String hql, - Class resultType, - Consumer> queryPostProcessor, - String expectedMql, - List expectedResultList) { - sessionFactoryScope.inTransaction(session -> { - var selectionQuery = session.createSelectionQuery(hql, resultType); - if (queryPostProcessor != null) { - queryPostProcessor.accept(selectionQuery); - } - var resultList = selectionQuery.getResultList(); - - assertActualCommand(BsonDocument.parse(expectedMql)); - - assertThat(resultList) - .usingRecursiveFieldByFieldElementComparator() - .containsExactlyElementsOf(expectedResultList); - }); - } - - private void assertSelectQueryFailure( - String hql, - Class resultType, - Consumer> queryPostProcessor, - Class expectedExceptionType, - String expectedExceptionMessage, - Object... expectedExceptionMessageParameters) { - sessionFactoryScope.inTransaction(session -> assertThatThrownBy(() -> { - var selectionQuery = session.createSelectionQuery(hql, resultType); - if (queryPostProcessor != null) { - queryPostProcessor.accept(selectionQuery); - } - selectionQuery.getResultList(); - }) - .isInstanceOf(expectedExceptionType) - .hasMessage(expectedExceptionMessage, expectedExceptionMessageParameters)); - } - - private void assertActualCommand(BsonDocument expectedCommand) { - var capturedCommands = testCommandListener.getStartedCommands(); - - assertThat(capturedCommands) - .singleElement() - .asInstanceOf(InstanceOfAssertFactories.MAP) - .containsAllEntriesOf(expectedCommand); - } - @Entity(name = "Contact") @Table(name = "contacts") static class Contact { 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 3d929453..5ccf118c 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/AbstractMqlTranslator.java @@ -27,6 +27,8 @@ 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.AstLiteralValue.FALSE; +import static com.mongodb.hibernate.internal.translate.mongoast.AstLiteralValue.TRUE; import static com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator.EQ; import static com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator.GT; import static com.mongodb.hibernate.internal.translate.mongoast.filter.AstComparisonFilterOperator.GTE; @@ -498,6 +500,18 @@ public void visitUnparsedNumericLiteral(UnparsedNumericLitera FIELD_VALUE, new AstLiteralValue(toBsonValue(unparsedNumericLiteral.getLiteralValue()))); } + @Override + public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) { + if (!isFieldPathExpression(booleanExpressionPredicate.getExpression())) { + throw new FeatureNotSupportedException("Expression not of field path not supported"); + } + var fieldPath = acceptAndYield(booleanExpressionPredicate.getExpression(), FIELD_PATH); + var astFilterOperation = + new AstComparisonFilterOperation(EQ, booleanExpressionPredicate.isNegated() ? FALSE : TRUE); + var filter = new AstFieldOperationFilter(new AstFilterFieldPath(fieldPath), astFilterOperation); + astVisitorValueHolder.yield(FILTER, filter); + } + @Override public void visitDeleteStatement(DeleteStatement deleteStatement) { throw new FeatureNotSupportedException("TODO-HIBERNATE-46 https://jira.mongodb.org/browse/HIBERNATE-46"); @@ -693,11 +707,6 @@ public void visitModifiedSubQueryExpression(ModifiedSubQueryExpression modifiedS throw new FeatureNotSupportedException(); } - @Override - public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) { - throw new FeatureNotSupportedException(); - } - @Override public void visitBetweenPredicate(BetweenPredicate betweenPredicate) { throw new FeatureNotSupportedException(); diff --git a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/AstLiteralValue.java b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/AstLiteralValue.java index f5f721d0..7c45ea9e 100644 --- a/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/AstLiteralValue.java +++ b/src/main/java/com/mongodb/hibernate/internal/translate/mongoast/AstLiteralValue.java @@ -16,6 +16,7 @@ package com.mongodb.hibernate.internal.translate.mongoast; +import org.bson.BsonBoolean; import org.bson.BsonValue; import org.bson.BsonWriter; import org.bson.codecs.BsonValueCodec; @@ -27,6 +28,9 @@ public record AstLiteralValue(BsonValue literalValue) implements AstValue { private static final EncoderContext DEFAULT_CONTEXT = EncoderContext.builder().build(); + public static final AstLiteralValue TRUE = new AstLiteralValue(BsonBoolean.TRUE); + public static final AstLiteralValue FALSE = new AstLiteralValue(BsonBoolean.FALSE); + @Override public void render(BsonWriter writer) { BSON_VALUE_CODEC.encode(writer, literalValue, DEFAULT_CONTEXT);