diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java index 502aeb408..b73536dd9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java @@ -29,6 +29,7 @@ import java.util.stream.Collectors; import org.bson.BSON; import org.bson.BsonRegularExpression; import org.bson.Document; +import org.bson.types.Binary; import org.springframework.data.domain.Example; import org.springframework.data.geo.Circle; import org.springframework.data.geo.Point; @@ -41,6 +42,7 @@ import org.springframework.data.mongodb.core.schema.JsonSchemaProperty; import org.springframework.data.mongodb.core.schema.MongoJsonSchema; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.Base64Utils; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -357,7 +359,7 @@ public class Criteria implements CriteriaDefinition { /** * Creates a criterion using the {@literal $type} operator. * - * @param type must not be {@literal null}. + * @param types must not be {@literal null}. * @return this * @since 2.1 * @see MongoDB Query operator: $type @@ -623,6 +625,18 @@ public class Criteria implements CriteriaDefinition { return registerCriteriaChainElement(schemaCriteria); } + /** + * Use {@link BitwiseCriteriaOperators} as gateway to create a criterion using one of the + * bitwise operators like + * {@code $bitsAllClear}. + * + * @return new instance of {@link BitwiseCriteriaOperators}. Never {@literal null}. + * @since 2.1 + */ + public BitwiseCriteriaOperators bits() { + return new BitwiseCriteriaOperatorsImpl(this); + } + /** * Creates an 'or' criteria using the $or operator for all of the provided criteria *

@@ -664,118 +678,6 @@ public class Criteria implements CriteriaDefinition { BasicDBList bsonList = createCriteriaList(criteria); return registerCriteriaChainElement(new Criteria("$and").is(bsonList)); } - - /** - * Creates a criterion using the {@literal $bitsAllClear} operator. - * - * @param numericBitmask non-negative numeric bitmask - * @return - * @see MongoDB Query operator: - * $bitsAllClear - * @since 2.1 - */ - public Criteria bitsAllClear(int numericBitmask) { - criteria.put("$bitsAllClear", Integer.valueOf(numericBitmask)); - return this; - } - - /** - * Creates a criterion using the {@literal $bitsAllClear} operator. - * - * @param bitPositions positions of set bits - * @return - * @see MongoDB Query operator: - * $bitsAllClear - * @since 2.1 - */ - public Criteria bitsAllClear(Collection bitPositions) { - criteria.put("$bitsAllClear", bitPositions); - return this; - } - - /** - * Creates a criterion using the {@literal $bitsAllSet} operator. - * - * @param numericBitmask non-negative numeric bitmask - * @return - * @see MongoDB Query operator: - * $bitsAllSet - * @since 2.1 - */ - public Criteria bitsAllSet(int numericBitmask) { - criteria.put("$bitsAllSet", Integer.valueOf(numericBitmask)); - return this; - } - - /** - * Creates a criterion using the {@literal $bitsAllSet} operator. - * - * @param bitPositions positions of set bits - * @return - * @see MongoDB Query operator: - * $bitsAllSet - * @since 2.1 - */ - public Criteria bitsAllSet(Collection bitPositions) { - criteria.put("$bitsAllSet", bitPositions); - return this; - } - - /** - * Creates a criterion using the {@literal $bitsAnyClear} operator. - * - * @param numericBitmask non-negative numeric bitmask - * @return - * @see MongoDB Query operator: - * $bitsAnyClear - * @since 2.1 - */ - public Criteria bitsAnyClear(int numericBitmask) { - criteria.put("$bitsAnyClear", Integer.valueOf(numericBitmask)); - return this; - } - - /** - * Creates a criterion using the {@literal $bitsAnyClear} operator. - * - * @param bitPositions positions of set bits - * @return - * @see MongoDB Query operator: - * $bitsAnyClear - * @since 2.1 - */ - public Criteria bitsAnyClear(Collection bitPositions) { - criteria.put("$bitsAnyClear", bitPositions); - return this; - } - - /** - * Creates a criterion using the {@literal $bitsAnySet} operator. - * - * @param numericBitmask non-negative numeric bitmask - * @return - * @see MongoDB Query operator: - * $bitsAnySet - * @since 2.1 - */ - public Criteria bitsAnySet(int numericBitmask) { - criteria.put("$bitsAnySet", Integer.valueOf(numericBitmask)); - return this; - } - - /** - * Creates a criterion using the {@literal $bitsAnySet} operator. - * - * @param bitPositions positions of set bits - * @return - * @see MongoDB Query operator: - * $bitsAnySet - * @since 2.1 - */ - public Criteria bitsAnySet(Collection bitPositions) { - criteria.put("$bitsAnySet", bitPositions); - return this; - } private Criteria registerCriteriaChainElement(Criteria criteria) { @@ -993,4 +895,324 @@ public class Criteria implements CriteriaDefinition { return value instanceof GeoJson || (value instanceof GeoCommand && ((GeoCommand) value).getShape() instanceof GeoJson); } + + /** + * MongoDB specific bitwise query + * operators like {@code $bitsAllClear, $bitsAllSet,...} for usage with {@link Criteria#bits()} and {@link Query}. + * + * @author Christoph Strobl + * @since 2.1 + * @see https://docs.mongodb.com/manual/reference/operator/query-bitwise/ + * @currentRead Beyond the Shadows - Brent Weeks + */ + public interface BitwiseCriteriaOperators { + + /** + * Creates a criterion using {@literal $bitsAllClear} matching documents where all given bit positions are clear + * (i.e. 0). + * + * @param numericBitmask non-negative numeric bitmask. + * @return target {@link Criteria}. + * @see MongoDB Query operator: + * $bitsAllClear + * @since 2.1 + */ + Criteria allClear(int numericBitmask); + + /** + * Creates a criterion using {@literal $bitsAllClear} matching documents where all given bit positions are clear + * (i.e. 0). + * + * @param bitmask string representation of a bitmask that will be converted to its base64 encoded {@link Binary} + * representation. Must not be {@literal null} nor empty. + * @return target {@link Criteria}. + * @throws IllegalArgumentException when bitmask is {@literal null} or empty. + * @see MongoDB Query operator: + * $bitsAllClear + * @since 2.1 + */ + Criteria allClear(String bitmask); + + /** + * Creates a criterion using {@literal $bitsAllClear} matching documents where all given bit positions are clear + * (i.e. 0). + * + * @param positions list of non-negative integer positions. Positions start at 0 from the least significant bit. + * Must not be {@literal null} nor contain {@literal null} elements. + * @return target {@link Criteria}. + * @throws IllegalArgumentException when positions is {@literal null} or contains {@literal null} elements. + * @see MongoDB Query operator: + * $bitsAllClear + * @since 2.1 + */ + Criteria allClear(List positions); + + /** + * Creates a criterion using {@literal $bitsAllSet} matching documents where all given bit positions are set (i.e. + * 1). + * + * @param numericBitmask non-negative numeric bitmask. + * @return target {@link Criteria}. + * @see MongoDB Query operator: + * $bitsAllSet + * @since 2.1 + */ + Criteria allSet(int numericBitmask); + + /** + * Creates a criterion using {@literal $bitsAllSet} matching documents where all given bit positions are set (i.e. + * 1). + * + * @param bitmask string representation of a bitmask that will be converted to its base64 encoded {@link Binary} + * representation. Must not be {@literal null} nor empty. + * @return target {@link Criteria}. + * @throws IllegalArgumentException when bitmask is {@literal null} or empty. + * @see MongoDB Query operator: + * $bitsAllSet + * @since 2.1 + */ + Criteria allSet(String bitmask); + + /** + * Creates a criterion using {@literal $bitsAllSet} matching documents where all given bit positions are set (i.e. + * 1). + * + * @param positions list of non-negative integer positions. Positions start at 0 from the least significant bit. + * Must not be {@literal null} nor contain {@literal null} elements. + * @return target {@link Criteria}. + * @throws IllegalArgumentException when positions is {@literal null} or contains {@literal null} elements. + * @see MongoDB Query operator: + * $bitsAllSet + * @since 2.1 + */ + Criteria allSet(List positions); + + /** + * Creates a criterion using {@literal $bitsAllClear} matching documents where any given bit positions are clear + * (i.e. 0). + * + * @param numericBitmask non-negative numeric bitmask. + * @return target {@link Criteria}. + * @see MongoDB Query operator: + * $bitsAnyClear + * @since 2.1 + */ + Criteria anyClear(int numericBitmask); + + /** + * Creates a criterion using {@literal $bitsAllClear} matching documents where any given bit positions are clear + * (i.e. 0). + * + * @param bitmask string representation of a bitmask that will be converted to its base64 encoded {@link Binary} + * representation. Must not be {@literal null} nor empty. + * @return target {@link Criteria}. + * @throws IllegalArgumentException when bitmask is {@literal null} or empty. + * @see MongoDB Query operator: + * $bitsAnyClear + * @since 2.1 + */ + Criteria anyClear(String bitmask); + + /** + * Creates a criterion using {@literal $bitsAllClear} matching documents where any given bit positions are clear + * (i.e. 0). + * + * @param positions list of non-negative integer positions. Positions start at 0 from the least significant bit. + * Must not be {@literal null} nor contain {@literal null} elements. + * @return target {@link Criteria}. + * @throws IllegalArgumentException when positions is {@literal null} or contains {@literal null} elements. + * @see MongoDB Query operator: + * $bitsAnyClear + * @since 2.1 + */ + Criteria anyClear(List positions); + + /** + * Creates a criterion using {@literal $bitsAllSet} matching documents where any given bit positions are set (i.e. + * 1). + * + * @param numericBitmask non-negative numeric bitmask. + * @return target {@link Criteria}. + * @see MongoDB Query operator: + * $bitsAnySet + * @since 2.1 + */ + Criteria anySet(int numericBitmask); + + /** + * Creates a criterion using {@literal $bitsAnySet} matching documents where any given bit positions are set (i.e. + * 1). + * + * @param bitmask string representation of a bitmask that will be converted to its base64 encoded {@link Binary} + * representation. Must not be {@literal null} nor empty. + * @return target {@link Criteria}. + * @throws IllegalArgumentException when bitmask is {@literal null} or empty. + * @see MongoDB Query operator: + * $bitsAnySet + * @since 2.1 + */ + Criteria anySet(String bitmask); + + /** + * Creates a criterion using {@literal $bitsAnySet} matching documents where any given bit positions are set (i.e. + * 1). + * + * @param positions list of non-negative integer positions. Positions start at 0 from the least significant bit. + * Must not be {@literal null} nor contain {@literal null} elements. + * @return target {@link Criteria}. + * @throws IllegalArgumentException when positions is {@literal null} or contains {@literal null} elements. + * @see MongoDB Query operator: + * $bitsAnySet + * @since 2.1 + */ + Criteria anySet(List positions); + + } + + /** + * Default implementation of {@link BitwiseCriteriaOperators}. + * + * @author Christoph Strobl + * @currentRead Beyond the Shadows - Brent Weeks + */ + private static class BitwiseCriteriaOperatorsImpl implements BitwiseCriteriaOperators { + + private final Criteria target; + + BitwiseCriteriaOperatorsImpl(Criteria target) { + this.target = target; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.BitwiseCriteriaOperators#allClear(int) + */ + @Override + public Criteria allClear(int numericBitmask) { + return numericBitmask("$bitsAllClear", numericBitmask); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.BitwiseCriteriaOperators#allClear(java.lang.String) + */ + @Override + public Criteria allClear(String bitmask) { + return stringBitmask("$bitsAllClear", bitmask); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.BitwiseCriteriaOperators#allClear(java.util.List) + */ + @Override + public Criteria allClear(List positions) { + return positions("$bitsAllClear", positions); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.BitwiseCriteriaOperators#allSet(int) + */ + @Override + public Criteria allSet(int numericBitmask) { + return numericBitmask("$bitsAllSet", numericBitmask); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.BitwiseCriteriaOperators#allSet(java.lang.String) + */ + @Override + public Criteria allSet(String bitmask) { + return stringBitmask("$bitsAllSet", bitmask); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.BitwiseCriteriaOperators#allSet(java.util.List) + */ + @Override + public Criteria allSet(List positions) { + return positions("$bitsAllSet", positions); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.BitwiseCriteriaOperators#anyClear(int) + */ + @Override + public Criteria anyClear(int numericBitmask) { + return numericBitmask("$bitsAnyClear", numericBitmask); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.BitwiseCriteriaOperators#anyClear(java.lang.String) + */ + @Override + public Criteria anyClear(String bitmask) { + return stringBitmask("$bitsAnyClear", bitmask); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.BitwiseCriteriaOperators#anyClear(java.util.List) + */ + @Override + public Criteria anyClear(List positions) { + return positions("$bitsAnyClear", positions); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.BitwiseCriteriaOperators#anySet(int) + */ + @Override + public Criteria anySet(int numericBitmask) { + return numericBitmask("$bitsAnySet", numericBitmask); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.BitwiseCriteriaOperators#anySet(java.lang.String) + */ + @Override + public Criteria anySet(String bitmask) { + return stringBitmask("$bitsAnySet", bitmask); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.BitwiseCriteriaOperators#anySet(java.util.Collection) + */ + @Override + public Criteria anySet(List positions) { + return positions("$bitsAnySet", positions); + } + + private Criteria positions(String operator, List positions) { + + Assert.notNull(positions, "Positions must not be null!"); + Assert.noNullElements(positions.toArray(), "Positions must not contain null values."); + + target.criteria.put(operator, positions); + return target; + } + + private Criteria stringBitmask(String operator, String bitmask) { + + Assert.hasText(bitmask, "Bitmask must not be null!"); + + target.criteria.put(operator, new Binary(Base64Utils.decodeFromString(bitmask))); + return target; + } + + private Criteria numericBitmask(String operator, int bitmask) { + + target.criteria.put(operator, bitmask); + return target; + } + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index eab9e1d50..9aa7990b5 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -3361,61 +3361,6 @@ public class MongoTemplateTests { assertThat(template.count(query(where("field").is("stark")), Sample.class)).isEqualTo(0L); } - @Test // DATAMONGO-1808 - public void testFindByBitmasks() { - DocumentWithBitmask document = new DocumentWithBitmask(0b101); - template.insert(document); - - assertThat(template.find(Query.query(Criteria.where("intValue").bitsAllClear(0b010)), DocumentWithBitmask.class)) - .hasSize(1); - assertThat(template.find(Query.query(Criteria.where("intValue").bitsAllClear(Arrays.asList(1))), - DocumentWithBitmask.class)).hasSize(1); - assertThat(template.find(Query.query(Criteria.where("binaryValue").bitsAllClear(0b010)), DocumentWithBitmask.class)) - .hasSize(1); - assertThat(template.find(Query.query(Criteria.where("binaryValue").bitsAllClear(Arrays.asList(1))), - DocumentWithBitmask.class)).hasSize(1); - - assertThat(template.find(Query.query(Criteria.where("intValue").bitsAllSet(0b101)), DocumentWithBitmask.class)) - .hasSize(1); - assertThat(template.find(Query.query(Criteria.where("intValue").bitsAllSet(Arrays.asList(0, 2))), - DocumentWithBitmask.class)).hasSize(1); - assertThat(template.find(Query.query(Criteria.where("binaryValue").bitsAllSet(0b101)), DocumentWithBitmask.class)) - .hasSize(1); - assertThat(template.find(Query.query(Criteria.where("binaryValue").bitsAllSet(Arrays.asList(0, 2))), - DocumentWithBitmask.class)).hasSize(1); - - assertThat(template.find(Query.query(Criteria.where("intValue").bitsAnyClear(0b111)), DocumentWithBitmask.class)) - .hasSize(1); - assertThat(template.find(Query.query(Criteria.where("intValue").bitsAnyClear(Arrays.asList(0, 1, 2))), - DocumentWithBitmask.class)).hasSize(1); - assertThat(template.find(Query.query(Criteria.where("binaryValue").bitsAnyClear(0b111)), DocumentWithBitmask.class)) - .hasSize(1); - assertThat(template.find(Query.query(Criteria.where("binaryValue").bitsAnyClear(Arrays.asList(0, 1, 2))), - DocumentWithBitmask.class)).hasSize(1); - - assertThat(template.find(Query.query(Criteria.where("intValue").bitsAnySet(0b111)), DocumentWithBitmask.class)) - .hasSize(1); - assertThat(template.find(Query.query(Criteria.where("intValue").bitsAnySet(Arrays.asList(0, 1, 2))), - DocumentWithBitmask.class)).hasSize(1); - assertThat(template.find(Query.query(Criteria.where("binaryValue").bitsAnySet(0b111)), DocumentWithBitmask.class)) - .hasSize(1); - assertThat(template.find(Query.query(Criteria.where("binaryValue").bitsAnySet(Arrays.asList(0, 1, 2))), - DocumentWithBitmask.class)).hasSize(1); - } - - @NoArgsConstructor - static class DocumentWithBitmask { - @Id String id; - int intValue; - byte[] binaryValue; - - public DocumentWithBitmask(int value) { - this.intValue = value; - this.binaryValue = new byte[] { (byte) value }; - } - - } - static class TypeWithNumbers { @Id String id; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java index 1bbcd28fb..8830e4e42 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 the original author or authors. + * Copyright 2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,268 +15,151 @@ */ package org.springframework.data.mongodb.core.query; -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; -import static org.springframework.data.mongodb.test.util.IsBsonObject.*; +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.mongodb.core.query.Criteria.*; +import static org.springframework.data.mongodb.core.query.Query.*; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; import java.util.Arrays; -import java.util.Collections; -import org.bson.Document; +import org.bson.types.Binary; +import org.junit.Before; import org.junit.Test; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; -import org.springframework.data.mongodb.core.geo.GeoJsonLineString; -import org.springframework.data.mongodb.core.geo.GeoJsonPoint; -import org.springframework.data.mongodb.core.schema.MongoJsonSchema; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.util.Base64Utils; + +import com.mongodb.MongoClient; /** - * @author Oliver Gierke - * @author Thomas Darimont + * Integration tests for {@link Criteria} usage as part of a {@link Query}. + * * @author Christoph Strobl + * @author Andreas Zink */ public class CriteriaTests { - @Test - public void testSimpleCriteria() { - Criteria c = new Criteria("name").is("Bubba"); - assertEquals(Document.parse("{ \"name\" : \"Bubba\"}"), c.getCriteriaObject()); - } - - @Test - public void testNotEqualCriteria() { - Criteria c = new Criteria("name").ne("Bubba"); - assertEquals(Document.parse("{ \"name\" : { \"$ne\" : \"Bubba\"}}"), c.getCriteriaObject()); - } - - @Test - public void buildsIsNullCriteriaCorrectly() { - - Document reference = new Document("name", null); - - Criteria criteria = new Criteria("name").is(null); - assertThat(criteria.getCriteriaObject(), is(reference)); - } - - @Test - public void testChainedCriteria() { - Criteria c = new Criteria("name").is("Bubba").and("age").lt(21); - assertEquals(Document.parse("{ \"name\" : \"Bubba\" , \"age\" : { \"$lt\" : 21}}"), c.getCriteriaObject()); - } - - @Test(expected = InvalidMongoDbApiUsageException.class) - public void testCriteriaWithMultipleConditionsForSameKey() { - Criteria c = new Criteria("name").gte("M").and("name").ne("A"); - c.getCriteriaObject(); - } - - @Test - public void equalIfCriteriaMatches() { - - Criteria left = new Criteria("name").is("Foo").and("lastname").is("Bar"); - Criteria right = new Criteria("name").is("Bar").and("lastname").is("Bar"); - - assertThat(left, is(not(right))); - assertThat(right, is(not(left))); - } - - @Test(expected = IllegalArgumentException.class) // DATAMONGO-507 - public void shouldThrowExceptionWhenTryingToNegateAndOperation() { - - new Criteria() // - .not() // - .andOperator(Criteria.where("delete").is(true).and("_id").is(42)); // - } - - @Test(expected = IllegalArgumentException.class) // DATAMONGO-507 - public void shouldThrowExceptionWhenTryingToNegateOrOperation() { - - new Criteria() // - .not() // - .orOperator(Criteria.where("delete").is(true).and("_id").is(42)); // - } - - @Test(expected = IllegalArgumentException.class) // DATAMONGO-507 - public void shouldThrowExceptionWhenTryingToNegateNorOperation() { - - new Criteria() // - .not() // - .norOperator(Criteria.where("delete").is(true).and("_id").is(42)); // - } - - @Test // DATAMONGO-507 - public void shouldNegateFollowingSimpleExpression() { - - Criteria c = Criteria.where("age").not().gt(18).and("status").is("student"); - Document co = c.getCriteriaObject(); - - assertThat(co, is(notNullValue())); - assertThat(co, is(Document.parse("{ \"age\" : { \"$not\" : { \"$gt\" : 18}} , \"status\" : \"student\"}"))); - } - - @Test // DATAMONGO-1068 - public void getCriteriaObjectShouldReturnEmptyDocumentWhenNoCriteriaSpecified() { - - Document document = new Criteria().getCriteriaObject(); - - assertThat(document, equalTo(new Document())); - } - - @Test // DATAMONGO-1068 - public void getCriteriaObjectShouldUseCritieraValuesWhenNoKeyIsPresent() { + MongoOperations ops; + MongoClient client; - Document document = new Criteria().lt("foo").getCriteriaObject(); + static final DocumentWithBitmask FIFTY_FOUR/*00110110*/ = new DocumentWithBitmask("1", Integer.valueOf(54), + Integer.toBinaryString(54)); + static final DocumentWithBitmask TWENTY_INT/*00010100*/ = new DocumentWithBitmask("2", Integer.valueOf(20), + Integer.toBinaryString(20)); + static final DocumentWithBitmask TWENTY_FLOAT/*00010100*/ = new DocumentWithBitmask("3", Float.valueOf(20), + Integer.toBinaryString(20)); + static final DocumentWithBitmask ONE_HUNDRED_TWO/*01100110*/ = new DocumentWithBitmask("4", + new Binary(Base64Utils.decodeFromString("Zg==")), "01100110"); - assertThat(document, equalTo(new Document().append("$lt", "foo"))); - } + @Before + public void setUp() { - @Test // DATAMONGO-1068 - public void getCriteriaObjectShouldUseCritieraValuesWhenNoKeyIsPresentButMultipleCriteriasPresent() { + client = new MongoClient(); + ops = new MongoTemplate(client, "criteria-tests"); - Document document = new Criteria().lt("foo").gt("bar").getCriteriaObject(); + ops.dropCollection(DocumentWithBitmask.class); - assertThat(document, equalTo(new Document().append("$lt", "foo").append("$gt", "bar"))); + ops.insert(FIFTY_FOUR); + ops.insert(TWENTY_INT); + ops.insert(TWENTY_FLOAT); + ops.insert(ONE_HUNDRED_TWO); } - @Test // DATAMONGO-1068 - public void getCriteriaObjectShouldRespectNotWhenNoKeyPresent() { - - Document document = new Criteria().lt("foo").not().getCriteriaObject(); - - assertThat(document, equalTo(new Document().append("$not", new Document("$lt", "foo")))); - } - - @Test // DATAMONGO-1135 - public void geoJsonTypesShouldBeWrappedInGeometry() { - - Document document = new Criteria("foo").near(new GeoJsonPoint(100, 200)).getCriteriaObject(); - - assertThat(document, isBsonObject().containing("foo.$near.$geometry", new GeoJsonPoint(100, 200))); - } - - @Test // DATAMONGO-1135 - public void legacyCoordinateTypesShouldNotBeWrappedInGeometry() { - - Document document = new Criteria("foo").near(new Point(100, 200)).getCriteriaObject(); + @Test // DATAMONGO-1808 + public void bitsAllClearWithBitPositions() { - assertThat(document, isBsonObject().notContaining("foo.$near.$geometry")); + assertThat(ops.find(query(where("value").bits().allClear(Arrays.asList(1, 5))), DocumentWithBitmask.class)) + .containsExactlyInAnyOrder(TWENTY_INT, TWENTY_FLOAT); } - @Test // DATAMONGO-1135 - public void maxDistanceShouldBeMappedInsideNearWhenUsedAlongWithGeoJsonType() { - - Document document = new Criteria("foo").near(new GeoJsonPoint(100, 200)).maxDistance(50D).getCriteriaObject(); + @Test // DATAMONGO-1808 + public void bitsAllClearWithNumericBitmask() { - assertThat(document, isBsonObject().containing("foo.$near.$maxDistance", 50D)); + assertThat(ops.find(query(where("value").bits().allClear(35)), DocumentWithBitmask.class)) + .containsExactlyInAnyOrder(TWENTY_INT, TWENTY_FLOAT); } - @Test // DATAMONGO-1135 - public void maxDistanceShouldBeMappedInsideNearSphereWhenUsedAlongWithGeoJsonType() { - - Document document = new Criteria("foo").nearSphere(new GeoJsonPoint(100, 200)).maxDistance(50D).getCriteriaObject(); + @Test // DATAMONGO-1808 + public void bitsAllClearWithStringBitmask() { - assertThat(document, isBsonObject().containing("foo.$nearSphere.$maxDistance", 50D)); + assertThat(ops.find(query(where("value").bits().allClear("ID==")), DocumentWithBitmask.class)) + .containsExactlyInAnyOrder(TWENTY_INT, TWENTY_FLOAT); } - @Test // DATAMONGO-1110 - public void minDistanceShouldBeMappedInsideNearWhenUsedAlongWithGeoJsonType() { - - Document document = new Criteria("foo").near(new GeoJsonPoint(100, 200)).minDistance(50D).getCriteriaObject(); + @Test // DATAMONGO-1808 + public void bitsAllSetWithBitPositions() { - assertThat(document, isBsonObject().containing("foo.$near.$minDistance", 50D)); + assertThat(ops.find(query(where("value").bits().allSet(Arrays.asList(1, 5))), DocumentWithBitmask.class)) + .containsExactlyInAnyOrder(FIFTY_FOUR, ONE_HUNDRED_TWO); } - @Test // DATAMONGO-1110 - public void minDistanceShouldBeMappedInsideNearSphereWhenUsedAlongWithGeoJsonType() { - - Document document = new Criteria("foo").nearSphere(new GeoJsonPoint(100, 200)).minDistance(50D).getCriteriaObject(); + @Test // DATAMONGO-1808 + public void bitsAllSetWithNumericBitmask() { - assertThat(document, isBsonObject().containing("foo.$nearSphere.$minDistance", 50D)); + assertThat(ops.find(query(where("value").bits().allSet(50)), DocumentWithBitmask.class)) + .containsExactlyInAnyOrder(FIFTY_FOUR); } - @Test // DATAMONGO-1110 - public void minAndMaxDistanceShouldBeMappedInsideNearSphereWhenUsedAlongWithGeoJsonType() { - - Document document = new Criteria("foo").nearSphere(new GeoJsonPoint(100, 200)).minDistance(50D).maxDistance(100D) - .getCriteriaObject(); - - assertThat(document, isBsonObject().containing("foo.$nearSphere.$minDistance", 50D)); - assertThat(document, isBsonObject().containing("foo.$nearSphere.$maxDistance", 100D)); - } + @Test // DATAMONGO-1808 + public void bitsAllSetWithStringBitmask() { - @Test(expected = IllegalArgumentException.class) // DATAMONGO-1134 - public void intersectsShouldThrowExceptionWhenCalledWihtNullValue() { - new Criteria("foo").intersects(null); + assertThat(ops.find(query(where("value").bits().allSet("MC==")), DocumentWithBitmask.class)) + .containsExactlyInAnyOrder(FIFTY_FOUR); } - @Test // DATAMONGO-1134 - public void intersectsShouldWrapGeoJsonTypeInGeometryCorrectly() { - - GeoJsonLineString lineString = new GeoJsonLineString(new Point(0, 0), new Point(10, 10)); - Document document = new Criteria("foo").intersects(lineString).getCriteriaObject(); + @Test // DATAMONGO-1808 + public void bitsAnyClearWithBitPositions() { - assertThat(document, isBsonObject().containing("foo.$geoIntersects.$geometry", lineString)); + assertThat(ops.find(query(where("value").bits().anyClear(Arrays.asList(1, 5))), DocumentWithBitmask.class)) + .containsExactlyInAnyOrder(TWENTY_INT, TWENTY_FLOAT); } - @Test // DATAMONGO-1835 - public void extractsJsonSchemaInChainCorrectly() { - - MongoJsonSchema schema = MongoJsonSchema.builder().required("name").build(); - Criteria critera = Criteria.where("foo").is("bar").andDocumentStructureMatches(schema); + @Test // DATAMONGO-1808 + public void bitsAnyClearWithNumericBitmask() { - assertThat(critera.getCriteriaObject(), is(equalTo(new Document("foo", "bar").append("$jsonSchema", - new Document("type", "object").append("required", Collections.singletonList("name")))))); + assertThat(ops.find(query(where("value").bits().anyClear(35)), DocumentWithBitmask.class)) + .containsExactlyInAnyOrder(FIFTY_FOUR, TWENTY_INT, TWENTY_FLOAT, ONE_HUNDRED_TWO); } - @Test // DATAMONGO-1835 - public void extractsJsonSchemaFromFactoryMethodCorrectly() { - - MongoJsonSchema schema = MongoJsonSchema.builder().required("name").build(); - Criteria critera = Criteria.matchingDocumentStructure(schema); + @Test // DATAMONGO-1808 + public void bitsAnyClearWithStringBitmask() { - assertThat(critera.getCriteriaObject(), is(equalTo(new Document("$jsonSchema", - new Document("type", "object").append("required", Collections.singletonList("name")))))); + assertThat(ops.find(query(where("value").bits().anyClear("MC==")), DocumentWithBitmask.class)) + .containsExactlyInAnyOrder(TWENTY_INT, TWENTY_FLOAT, ONE_HUNDRED_TWO); } @Test // DATAMONGO-1808 - public void testBitsAllClear() { - Criteria numericBitmaskCriteria = new Criteria("field").bitsAllClear(0b101); - assertEquals(Document.parse("{ \"field\" : { \"$bitsAllClear\" : 5} }"), - numericBitmaskCriteria.getCriteriaObject()); - - Criteria bitPositionsBitmaskCriteria = new Criteria("field").bitsAllClear(Arrays.asList(0, 2)); - assertEquals(Document.parse("{ \"field\" : { \"$bitsAllClear\" : [ 0, 2 ]} }"), - bitPositionsBitmaskCriteria.getCriteriaObject()); + public void bitsAnySetWithBitPositions() { + + assertThat(ops.find(query(where("value").bits().anySet(Arrays.asList(1, 5))), DocumentWithBitmask.class)) + .containsExactlyInAnyOrder(FIFTY_FOUR, ONE_HUNDRED_TWO); } @Test // DATAMONGO-1808 - public void testBitsAllSet() { - Criteria numericBitmaskCriteria = new Criteria("field").bitsAllSet(0b101); - assertEquals(Document.parse("{ \"field\" : { \"$bitsAllSet\" : 5} }"), numericBitmaskCriteria.getCriteriaObject()); + public void bitsAnySetWithNumericBitmask() { - Criteria bitPositionsBitmaskCriteria = new Criteria("field").bitsAllSet(Arrays.asList(0, 2)); - assertEquals(Document.parse("{ \"field\" : { \"$bitsAllSet\" : [ 0, 2 ]} }"), - bitPositionsBitmaskCriteria.getCriteriaObject()); + assertThat(ops.find(query(where("value").bits().anySet(35)), DocumentWithBitmask.class)) + .containsExactlyInAnyOrder(FIFTY_FOUR, ONE_HUNDRED_TWO); } @Test // DATAMONGO-1808 - public void testBitsAnyClear() { - Criteria numericBitmaskCriteria = new Criteria("field").bitsAnyClear(0b101); - assertEquals(Document.parse("{ \"field\" : { \"$bitsAnyClear\" : 5} }"), - numericBitmaskCriteria.getCriteriaObject()); - - Criteria bitPositionsBitmaskCriteria = new Criteria("field").bitsAnyClear(Arrays.asList(0, 2)); - assertEquals(Document.parse("{ \"field\" : { \"$bitsAnyClear\" : [ 0, 2 ]} }"), - bitPositionsBitmaskCriteria.getCriteriaObject()); + public void bitsAnySetWithStringBitmask() { + + assertThat(ops.find(query(where("value").bits().anySet("MC==")), DocumentWithBitmask.class)) + .containsExactlyInAnyOrder(FIFTY_FOUR, TWENTY_INT, TWENTY_FLOAT, ONE_HUNDRED_TWO); } - @Test // DATAMONGO-1808 - public void testBitsAnySet() { - Criteria numericBitmaskCriteria = new Criteria("field").bitsAnySet(0b101); - assertEquals(Document.parse("{ \"field\" : { \"$bitsAnySet\" : 5} }"), numericBitmaskCriteria.getCriteriaObject()); + @Data + @EqualsAndHashCode(exclude = "value") + @AllArgsConstructor + static class DocumentWithBitmask { - Criteria bitPositionsBitmaskCriteria = new Criteria("field").bitsAnySet(Arrays.asList(0, 2)); - assertEquals(Document.parse("{ \"field\" : { \"$bitsAnySet\" : [ 0, 2 ]} }"), - bitPositionsBitmaskCriteria.getCriteriaObject()); + @Id String id; + Object value; + String binaryValue; } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java new file mode 100644 index 000000000..c492bd615 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java @@ -0,0 +1,313 @@ +/* + * Copyright 2010-2018 the original author or authors. + * + * 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 org.springframework.data.mongodb.core.query; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.springframework.data.mongodb.test.util.IsBsonObject.*; + +import java.util.Arrays; +import java.util.Collections; + +import org.bson.Document; +import org.junit.Test; +import org.springframework.data.geo.Point; +import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; +import org.springframework.data.mongodb.core.geo.GeoJsonLineString; +import org.springframework.data.mongodb.core.geo.GeoJsonPoint; +import org.springframework.data.mongodb.core.schema.MongoJsonSchema; + +/** + * @author Oliver Gierke + * @author Thomas Darimont + * @author Christoph Strobl + * @author Andreas Zink + */ +public class CriteriaUnitTests { + + @Test + public void testSimpleCriteria() { + Criteria c = new Criteria("name").is("Bubba"); + assertEquals(Document.parse("{ \"name\" : \"Bubba\"}"), c.getCriteriaObject()); + } + + @Test + public void testNotEqualCriteria() { + Criteria c = new Criteria("name").ne("Bubba"); + assertEquals(Document.parse("{ \"name\" : { \"$ne\" : \"Bubba\"}}"), c.getCriteriaObject()); + } + + @Test + public void buildsIsNullCriteriaCorrectly() { + + Document reference = new Document("name", null); + + Criteria criteria = new Criteria("name").is(null); + assertThat(criteria.getCriteriaObject(), is(reference)); + } + + @Test + public void testChainedCriteria() { + Criteria c = new Criteria("name").is("Bubba").and("age").lt(21); + assertEquals(Document.parse("{ \"name\" : \"Bubba\" , \"age\" : { \"$lt\" : 21}}"), c.getCriteriaObject()); + } + + @Test(expected = InvalidMongoDbApiUsageException.class) + public void testCriteriaWithMultipleConditionsForSameKey() { + Criteria c = new Criteria("name").gte("M").and("name").ne("A"); + c.getCriteriaObject(); + } + + @Test + public void equalIfCriteriaMatches() { + + Criteria left = new Criteria("name").is("Foo").and("lastname").is("Bar"); + Criteria right = new Criteria("name").is("Bar").and("lastname").is("Bar"); + + assertThat(left, is(not(right))); + assertThat(right, is(not(left))); + } + + @Test(expected = IllegalArgumentException.class) // DATAMONGO-507 + public void shouldThrowExceptionWhenTryingToNegateAndOperation() { + + new Criteria() // + .not() // + .andOperator(Criteria.where("delete").is(true).and("_id").is(42)); // + } + + @Test(expected = IllegalArgumentException.class) // DATAMONGO-507 + public void shouldThrowExceptionWhenTryingToNegateOrOperation() { + + new Criteria() // + .not() // + .orOperator(Criteria.where("delete").is(true).and("_id").is(42)); // + } + + @Test(expected = IllegalArgumentException.class) // DATAMONGO-507 + public void shouldThrowExceptionWhenTryingToNegateNorOperation() { + + new Criteria() // + .not() // + .norOperator(Criteria.where("delete").is(true).and("_id").is(42)); // + } + + @Test // DATAMONGO-507 + public void shouldNegateFollowingSimpleExpression() { + + Criteria c = Criteria.where("age").not().gt(18).and("status").is("student"); + Document co = c.getCriteriaObject(); + + assertThat(co, is(notNullValue())); + assertThat(co, is(Document.parse("{ \"age\" : { \"$not\" : { \"$gt\" : 18}} , \"status\" : \"student\"}"))); + } + + @Test // DATAMONGO-1068 + public void getCriteriaObjectShouldReturnEmptyDocumentWhenNoCriteriaSpecified() { + + Document document = new Criteria().getCriteriaObject(); + + assertThat(document, equalTo(new Document())); + } + + @Test // DATAMONGO-1068 + public void getCriteriaObjectShouldUseCritieraValuesWhenNoKeyIsPresent() { + + Document document = new Criteria().lt("foo").getCriteriaObject(); + + assertThat(document, equalTo(new Document().append("$lt", "foo"))); + } + + @Test // DATAMONGO-1068 + public void getCriteriaObjectShouldUseCritieraValuesWhenNoKeyIsPresentButMultipleCriteriasPresent() { + + Document document = new Criteria().lt("foo").gt("bar").getCriteriaObject(); + + assertThat(document, equalTo(new Document().append("$lt", "foo").append("$gt", "bar"))); + } + + @Test // DATAMONGO-1068 + public void getCriteriaObjectShouldRespectNotWhenNoKeyPresent() { + + Document document = new Criteria().lt("foo").not().getCriteriaObject(); + + assertThat(document, equalTo(new Document().append("$not", new Document("$lt", "foo")))); + } + + @Test // DATAMONGO-1135 + public void geoJsonTypesShouldBeWrappedInGeometry() { + + Document document = new Criteria("foo").near(new GeoJsonPoint(100, 200)).getCriteriaObject(); + + assertThat(document, isBsonObject().containing("foo.$near.$geometry", new GeoJsonPoint(100, 200))); + } + + @Test // DATAMONGO-1135 + public void legacyCoordinateTypesShouldNotBeWrappedInGeometry() { + + Document document = new Criteria("foo").near(new Point(100, 200)).getCriteriaObject(); + + assertThat(document, isBsonObject().notContaining("foo.$near.$geometry")); + } + + @Test // DATAMONGO-1135 + public void maxDistanceShouldBeMappedInsideNearWhenUsedAlongWithGeoJsonType() { + + Document document = new Criteria("foo").near(new GeoJsonPoint(100, 200)).maxDistance(50D).getCriteriaObject(); + + assertThat(document, isBsonObject().containing("foo.$near.$maxDistance", 50D)); + } + + @Test // DATAMONGO-1135 + public void maxDistanceShouldBeMappedInsideNearSphereWhenUsedAlongWithGeoJsonType() { + + Document document = new Criteria("foo").nearSphere(new GeoJsonPoint(100, 200)).maxDistance(50D).getCriteriaObject(); + + assertThat(document, isBsonObject().containing("foo.$nearSphere.$maxDistance", 50D)); + } + + @Test // DATAMONGO-1110 + public void minDistanceShouldBeMappedInsideNearWhenUsedAlongWithGeoJsonType() { + + Document document = new Criteria("foo").near(new GeoJsonPoint(100, 200)).minDistance(50D).getCriteriaObject(); + + assertThat(document, isBsonObject().containing("foo.$near.$minDistance", 50D)); + } + + @Test // DATAMONGO-1110 + public void minDistanceShouldBeMappedInsideNearSphereWhenUsedAlongWithGeoJsonType() { + + Document document = new Criteria("foo").nearSphere(new GeoJsonPoint(100, 200)).minDistance(50D).getCriteriaObject(); + + assertThat(document, isBsonObject().containing("foo.$nearSphere.$minDistance", 50D)); + } + + @Test // DATAMONGO-1110 + public void minAndMaxDistanceShouldBeMappedInsideNearSphereWhenUsedAlongWithGeoJsonType() { + + Document document = new Criteria("foo").nearSphere(new GeoJsonPoint(100, 200)).minDistance(50D).maxDistance(100D) + .getCriteriaObject(); + + assertThat(document, isBsonObject().containing("foo.$nearSphere.$minDistance", 50D)); + assertThat(document, isBsonObject().containing("foo.$nearSphere.$maxDistance", 100D)); + } + + @Test(expected = IllegalArgumentException.class) // DATAMONGO-1134 + public void intersectsShouldThrowExceptionWhenCalledWihtNullValue() { + new Criteria("foo").intersects(null); + } + + @Test // DATAMONGO-1134 + public void intersectsShouldWrapGeoJsonTypeInGeometryCorrectly() { + + GeoJsonLineString lineString = new GeoJsonLineString(new Point(0, 0), new Point(10, 10)); + Document document = new Criteria("foo").intersects(lineString).getCriteriaObject(); + + assertThat(document, isBsonObject().containing("foo.$geoIntersects.$geometry", lineString)); + } + + @Test // DATAMONGO-1835 + public void extractsJsonSchemaInChainCorrectly() { + + MongoJsonSchema schema = MongoJsonSchema.builder().required("name").build(); + Criteria criteria = Criteria.where("foo").is("bar").andDocumentStructureMatches(schema); + + assertThat(criteria.getCriteriaObject(), is(equalTo(new Document("foo", "bar").append("$jsonSchema", + new Document("type", "object").append("required", Collections.singletonList("name")))))); + } + + @Test // DATAMONGO-1835 + public void extractsJsonSchemaFromFactoryMethodCorrectly() { + + MongoJsonSchema schema = MongoJsonSchema.builder().required("name").build(); + Criteria criteria = Criteria.matchingDocumentStructure(schema); + + assertThat(criteria.getCriteriaObject(), is(equalTo(new Document("$jsonSchema", + new Document("type", "object").append("required", Collections.singletonList("name")))))); + } + + @Test // DATAMONGO-1808 + public void shouldAppendBitsAllClearWithIntBitmaskCorrectly() { + + Criteria numericBitmaskCriteria = new Criteria("field").bits().allClear(0b101); + + assertThat(numericBitmaskCriteria.getCriteriaObject(), + is(equalTo(Document.parse("{ \"field\" : { \"$bitsAllClear\" : 5} }")))); + } + + @Test // DATAMONGO-1808 + public void shouldAppendBitsAllClearWithPositionListCorrectly() { + + Criteria bitPositionsBitmaskCriteria = new Criteria("field").bits().allClear(Arrays.asList(0, 2)); + + assertThat(bitPositionsBitmaskCriteria.getCriteriaObject(), + is(equalTo(Document.parse("{ \"field\" : { \"$bitsAllClear\" : [ 0, 2 ]} }")))); + } + + @Test // DATAMONGO-1808 + public void shouldAppendBitsAllSetWithIntBitmaskCorrectly() { + + Criteria numericBitmaskCriteria = new Criteria("field").bits().allSet(0b101); + + assertThat(numericBitmaskCriteria.getCriteriaObject(), + is(equalTo(Document.parse("{ \"field\" : { \"$bitsAllSet\" : 5} }")))); + } + + @Test // DATAMONGO-1808 + public void shouldAppendBitsAllSetWithPositionListCorrectly() { + + Criteria bitPositionsBitmaskCriteria = new Criteria("field").bits().allSet(Arrays.asList(0, 2)); + + assertThat(bitPositionsBitmaskCriteria.getCriteriaObject(), + is(equalTo(Document.parse("{ \"field\" : { \"$bitsAllSet\" : [ 0, 2 ]} }")))); + } + + @Test // DATAMONGO-1808 + public void shouldAppendBitsAnyClearWithIntBitmaskCorrectly() { + + Criteria numericBitmaskCriteria = new Criteria("field").bits().anyClear(0b101); + + assertThat(numericBitmaskCriteria.getCriteriaObject(), + is(equalTo(Document.parse("{ \"field\" : { \"$bitsAnyClear\" : 5} }")))); + } + + @Test // DATAMONGO-1808 + public void shouldAppendBitsAnyClearWithPositionListCorrectly() { + + Criteria bitPositionsBitmaskCriteria = new Criteria("field").bits().anyClear(Arrays.asList(0, 2)); + + assertThat(bitPositionsBitmaskCriteria.getCriteriaObject(), + is(equalTo(Document.parse("{ \"field\" : { \"$bitsAnyClear\" : [ 0, 2 ]} }")))); + } + + @Test // DATAMONGO-1808 + public void shouldAppendBitsAnySetWithIntBitmaskCorrectly() { + + Criteria numericBitmaskCriteria = new Criteria("field").bits().anySet(0b101); + + assertThat(numericBitmaskCriteria.getCriteriaObject(), + is(equalTo(Document.parse("{ \"field\" : { \"$bitsAnySet\" : 5} }")))); + } + + @Test // DATAMONGO-1808 + public void shouldAppendBitsAnySetWithPositionListCorrectly() { + + Criteria bitPositionsBitmaskCriteria = new Criteria("field").bits().anySet(Arrays.asList(0, 2)); + + assertThat(bitPositionsBitmaskCriteria.getCriteriaObject(), + is(equalTo(Document.parse("{ \"field\" : { \"$bitsAnySet\" : [ 0, 2 ]} }")))); + } +} diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc index ed4241552..07ecdc9fd 100644 --- a/src/main/asciidoc/reference/mongodb.adoc +++ b/src/main/asciidoc/reference/mongodb.adoc @@ -1073,6 +1073,7 @@ As you can see most methods return the `Criteria` object to provide a fluent sty * `Criteria` *size* `(int s)` Creates a criterion using the `$size` operator * `Criteria` *type* `(int t)` Creates a criterion using the `$type` operator * `Criteria` *matchingDocumentStructure* `(MongoJsonSchema schema)` Creates a criterion using the `$jsonSchema` operator for <>. `$jsonSchema` can only be applied on the top level of a query and not property specific. Use the `properties` attribute of the schema to match against nested fields. +* `Criteria` *bits()* is the gateway to https://docs.mongodb.com/manual/reference/operator/query-bitwise/[MongoDB bitwise query operators] like `$bitsAllClear`. There are also methods on the Criteria class for geospatial queries. Here is a listing but look at the section on <> to see them in action.