diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java index eeaed6114..8c822e715 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java @@ -15,19 +15,19 @@ */ package org.springframework.data.mongodb.core.query; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -import lombok.EqualsAndHashCode; - import org.bson.Document; + import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** + * Field projection. + * * @author Thomas Risberg * @author Oliver Gierke * @author Patryk Wasik @@ -37,50 +37,112 @@ import org.springframework.util.ObjectUtils; */ public class Field { - private final Map criteria = new HashMap(); - private final Map slices = new HashMap(); - private final Map elemMatchs = new HashMap(); + private final Map criteria = new HashMap<>(); + private final Map slices = new HashMap<>(); + private final Map elemMatchs = new HashMap<>(); private @Nullable String positionKey; private int positionValue; - public Field include(String key) { - criteria.put(key, Integer.valueOf(1)); + /** + * Include a single {@code field} to be returned by the query operation. + * + * @param field the document field name to be included. + * @return {@code this} field projection instance. + */ + public Field include(String field) { + + Assert.notNull(field, "Key must not be null!"); + + criteria.put(field, 1); + return this; } - public Field includes(String... keys) { - Assert.notNull(keys, "Keys must not be null!"); - Assert.notEmpty(keys, "Keys must not be empty!"); + /** + * Include one or more {@code fields} to be returned by the query operation. + * + * @param fields the document field names to be included. + * @return {@code this} field projection instance. + * @since 3.1 + */ + public Field include(String... fields) { + + Assert.notNull(fields, "Keys must not be null!"); + + for (String key : fields) { + criteria.put(key, 1); + } - Arrays.asList(keys).stream().forEach(this::include); return this; } - public Field exclude(String key) { - criteria.put(key, Integer.valueOf(0)); + /** + * Exclude a single {@code field} from being returned by the query operation. + * + * @param field the document field name to be included. + * @return {@code this} field projection instance. + */ + public Field exclude(String field) { + + Assert.notNull(field, "Key must not be null!"); + + criteria.put(field, 0); + return this; } - public Field excludes(String... keys) { - Assert.notNull(keys, "Keys must not be null!"); - Assert.notEmpty(keys, "Keys must not be empty!"); + /** + * Exclude one or more {@code fields} from being returned by the query operation. + * + * @param fields the document field names to be included. + * @return {@code this} field projection instance. + * @since 3.1 + */ + public Field exclude(String... fields) { + + Assert.notNull(fields, "Keys must not be null!"); + + for (String key : fields) { + criteria.put(key, 0); + } - Arrays.asList(keys).stream().forEach(this::exclude); return this; } - public Field slice(String key, int size) { - slices.put(key, Integer.valueOf(size)); + /** + * Project a {@code $slice} of the array {@code field} using the first {@code size} elements. + * + * @param field the document field name to project, must be an array field. + * @param size the number of elements to include. + * @return {@code this} field projection instance. + */ + public Field slice(String field, int size) { + + Assert.notNull(field, "Key must not be null!"); + + slices.put(field, size); + return this; } - public Field slice(String key, int offset, int size) { - slices.put(key, new Integer[] { Integer.valueOf(offset), Integer.valueOf(size) }); + /** + * Project a {@code $slice} of the array {@code field} using the first {@code size} elements starting at + * {@code offset}. + * + * @param field the document field name to project, must be an array field. + * @param offset the offset to start at. + * @param size the number of elements to include. + * @return {@code this} field projection instance. + */ + public Field slice(String field, int offset, int size) { + + slices.put(field, new Integer[] { offset, size }); return this; } - public Field elemMatch(String key, Criteria elemMatchCriteria) { - elemMatchs.put(key, elemMatchCriteria); + public Field elemMatch(String field, Criteria elemMatchCriteria) { + + elemMatchs.put(field, elemMatchCriteria); return this; } @@ -90,7 +152,7 @@ public class Field { * * @param field query array field, must not be {@literal null} or empty. * @param value - * @return + * @return {@code this} field projection instance. */ public Field position(String field, int value) { 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 96b6e1001..8865d5a61 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 @@ -15,18 +15,18 @@ */ package org.springframework.data.mongodb.core; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.*; -import static org.junit.Assume.*; 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 static org.springframework.data.mongodb.core.query.Update.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.Value; +import lombok.With; + import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; import java.math.BigInteger; @@ -38,13 +38,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.IntStream; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.Value; -import lombok.experimental.Wither; - import org.bson.types.ObjectId; import org.joda.time.DateTime; import org.junit.jupiter.api.AfterEach; @@ -98,7 +91,6 @@ import org.springframework.util.StringUtils; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; import com.mongodb.DBRef; -import com.mongodb.MongoClient; import com.mongodb.MongoException; import com.mongodb.ReadPreference; import com.mongodb.WriteConcern; @@ -1847,7 +1839,7 @@ public class MongoTemplateTests { assertThat(result.property3).isEqualTo(obj.property3); } - @Test // DATAMONGO-702 + @Test // DATAMONGO-702, DATAMONGO-2294 public void queryShouldSupportRealAndAliasedPropertyNamesForFieldExclusions() { ObjectWith3AliasedFields obj = new ObjectWith3AliasedFields(); @@ -1860,8 +1852,7 @@ public class MongoTemplateTests { Query query = new Query(Criteria.where("id").is(obj.id)); query.fields() // - .exclude("property2") // real property name - .exclude("prop3"); // aliased property name + .exclude("property2", "prop3"); // real property name, aliased property name ObjectWith3AliasedFields result = template.findOne(query, ObjectWith3AliasedFields.class); @@ -3688,7 +3679,7 @@ public class MongoTemplateTests { queryByChainedInclude.fields().include("id").include("name"); Query queryByCollectionInclude = query(where("name").is("Walter")); - queryByCollectionInclude.fields().includes("id", "name"); + queryByCollectionInclude.fields().include("id", "name"); MyPerson first = template.findAndReplace(queryByChainedInclude, new MyPerson("Walter")); MyPerson second = template.findAndReplace(queryByCollectionInclude, new MyPerson("Walter")); @@ -4209,7 +4200,7 @@ public class MongoTemplateTests { // DATAMONGO-1992 @AllArgsConstructor - @Wither + @With static class ImmutableVersioned { final @Id String id; @@ -4222,7 +4213,7 @@ public class MongoTemplateTests { } @Value - @Wither + @With static class ImmutableAudited { @Id String id; @LastModifiedDate Instant modified; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/FieldUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/FieldUnitTests.java index e48dae245..05e39f429 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/FieldUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/FieldUnitTests.java @@ -15,40 +15,40 @@ */ package org.springframework.data.mongodb.core.query; -import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.mongodb.test.util.Assertions.*; import org.junit.jupiter.api.Test; /** - * Unit tests for {@link DocumentField}. + * Unit tests for {@link Field}. * * @author Oliver Gierke * @author Owen Q + * @author Mark Paluch */ -public class FieldUnitTests { +class FieldUnitTests { @Test - public void sameObjectSetupCreatesEqualField() { + void sameObjectSetupCreatesEqualField() { Field left = new Field().elemMatch("key", Criteria.where("foo").is("bar")); Field right = new Field().elemMatch("key", Criteria.where("foo").is("bar")); assertThat(left).isEqualTo(right); assertThat(right).isEqualTo(left); + assertThat(left.getFieldsObject()).isEqualTo("{key: { $elemMatch: {foo:\"bar\"}}}"); } @Test // DATAMONGO-2294 - public void sameObjectSetupCreatesEqualFieldByCollections() { + void rendersInclusionCorrectly() { - Field left = new Field().includes("foo", "bar"); - Field right = new Field().include("foo").include("bar"); + Field fields = new Field().include("foo", "bar").include("baz"); - assertThat(left, is(right)); - assertThat(right, is(left)); + assertThat(fields.getFieldsObject()).isEqualTo("{foo:1, bar:1, baz:1}"); } @Test - public void differentObjectSetupCreatesEqualField() { + void differentObjectSetupCreatesEqualField() { Field left = new Field().elemMatch("key", Criteria.where("foo").is("bar")); Field right = new Field().elemMatch("key", Criteria.where("foo").is("foo")); @@ -58,12 +58,10 @@ public class FieldUnitTests { } @Test // DATAMONGO-2294 - public void differentObjectSetupCreatesEqualFieldByCollections() { + void rendersExclusionCorrectly() { - Field left = new Field().includes("foo", "bar"); - Field right = new Field().include("foo").include("zoo"); + Field fields = new Field().exclude("foo", "bar").exclude("baz"); - assertThat(left, is(not(right))); - assertThat(right, is(not(left))); + assertThat(fields.getFieldsObject()).isEqualTo("{foo:0, bar:0, baz:0}"); } }