Browse Source

Polishing.

Reorder methods. Tweak Javadoc and documentation wording. Mention projection expressions in the what's new section. Reformat code.

See #3583
Original pull request: #3585.
pull/3604/head
Mark Paluch 5 years ago
parent
commit
f4556406bd
No known key found for this signature in database
GPG Key ID: 4406B84C1661DCD1
  1. 37
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BindableMongoExpression.java
  2. 14
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoExpression.java
  3. 24
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpression.java
  4. 16
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java
  5. 2
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateFieldProjectionTests.java
  6. 1
      src/main/asciidoc/new-features.adoc
  7. 24
      src/main/asciidoc/reference/mongodb.adoc

37
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BindableMongoExpression.java

@ -15,6 +15,8 @@
*/ */
package org.springframework.data.mongodb; package org.springframework.data.mongodb;
import java.util.Arrays;
import org.bson.Document; import org.bson.Document;
import org.bson.codecs.DocumentCodec; import org.bson.codecs.DocumentCodec;
import org.bson.codecs.configuration.CodecRegistry; import org.bson.codecs.configuration.CodecRegistry;
@ -30,15 +32,15 @@ import org.springframework.util.StringUtils;
* binding of placeholders like {@code ?0} is delayed upon first call on the the target {@link Document} via * binding of placeholders like {@code ?0} is delayed upon first call on the the target {@link Document} via
* {@link #toDocument()}. * {@link #toDocument()}.
* <p /> * <p />
* *
* <pre class="code"> * <pre class="code">
* $toUpper : $name -> { '$toUpper' : '$name' } * $toUpper : $name -> { '$toUpper' : '$name' }
* *
* { '$toUpper' : '$name' } -> { '$toUpper' : '$name' } * { '$toUpper' : '$name' } -> { '$toUpper' : '$name' }
* *
* { '$toUpper' : '?0' }, "$name" -> { '$toUpper' : '$name' } * { '$toUpper' : '?0' }, "$name" -> { '$toUpper' : '$name' }
* </pre> * </pre>
* *
* Some types might require a special {@link org.bson.codecs.Codec}. If so, make sure to provide a {@link CodecRegistry} * Some types might require a special {@link org.bson.codecs.Codec}. If so, make sure to provide a {@link CodecRegistry}
* containing the required {@link org.bson.codecs.Codec codec} via {@link #withCodecRegistry(CodecRegistry)}. * containing the required {@link org.bson.codecs.Codec codec} via {@link #withCodecRegistry(CodecRegistry)}.
* *
@ -49,11 +51,9 @@ public class BindableMongoExpression implements MongoExpression {
private final String expressionString; private final String expressionString;
@Nullable // private final @Nullable CodecRegistryProvider codecRegistryProvider;
private final CodecRegistryProvider codecRegistryProvider;
@Nullable // private final @Nullable Object[] args;
private final Object[] args;
private final Lazy<Document> target; private final Lazy<Document> target;
@ -118,16 +118,8 @@ public class BindableMongoExpression implements MongoExpression {
*/ */
@Override @Override
public String toString() { public String toString() {
return "BindableMongoExpression{" + "expressionString='" + expressionString + '\'' + ", args=" + args + '}'; return "BindableMongoExpression{" + "expressionString='" + expressionString + '\'' + ", args="
} + Arrays.toString(args) + '}';
private String wrapJsonIfNecessary(String json) {
if (StringUtils.hasText(json) && (json.startsWith("{") && json.endsWith("}"))) {
return json;
}
return "{" + json + "}";
} }
private Document parse() { private Document parse() {
@ -148,4 +140,13 @@ public class BindableMongoExpression implements MongoExpression {
: new ParameterBindingDocumentCodec(codecRegistryProvider.getCodecRegistry()); : new ParameterBindingDocumentCodec(codecRegistryProvider.getCodecRegistry());
return codec.decode(expression, args); return codec.decode(expression, args);
} }
private static String wrapJsonIfNecessary(String json) {
if (StringUtils.hasText(json) && (json.startsWith("{") && json.endsWith("}"))) {
return json;
}
return "{" + json + "}";
}
} }

14
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoExpression.java

@ -39,13 +39,6 @@ package org.springframework.data.mongodb;
@FunctionalInterface @FunctionalInterface
public interface MongoExpression { public interface MongoExpression {
/**
* Obtain the native {@link org.bson.Document} representation.
*
* @return never {@literal null}.
*/
org.bson.Document toDocument();
/** /**
* Create a new {@link MongoExpression} from plain {@link String} (eg. {@code $toUpper : $name}). <br /> * Create a new {@link MongoExpression} from plain {@link String} (eg. {@code $toUpper : $name}). <br />
* The given expression will be wrapped with <code>{ ... }</code> to match an actual MongoDB {@link org.bson.Document} * The given expression will be wrapped with <code>{ ... }</code> to match an actual MongoDB {@link org.bson.Document}
@ -70,4 +63,11 @@ public interface MongoExpression {
static MongoExpression create(String expression, Object... args) { static MongoExpression create(String expression, Object... args) {
return new BindableMongoExpression(expression, args); return new BindableMongoExpression(expression, args);
} }
/**
* Obtain the native {@link org.bson.Document} representation.
*
* @return never {@literal null}.
*/
org.bson.Document toDocument();
} }

24
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpression.java

@ -28,18 +28,6 @@ import org.springframework.data.mongodb.MongoExpression;
*/ */
public interface AggregationExpression extends MongoExpression { public interface AggregationExpression extends MongoExpression {
/**
* Obtain the as is (unmapped) representation of the {@link AggregationExpression}. Use
* {@link #toDocument(AggregationOperationContext)} with a matching {@link AggregationOperationContext context} to
* engage domain type mapping including field name resolution.
*
* @see org.springframework.data.mongodb.MongoExpression#toDocument()
*/
@Override
default Document toDocument() {
return toDocument(Aggregation.DEFAULT_CONTEXT);
}
/** /**
* Create an {@link AggregationExpression} out of a given {@link MongoExpression} to ensure the resulting * Create an {@link AggregationExpression} out of a given {@link MongoExpression} to ensure the resulting
* {@link MongoExpression#toDocument() Document} is mapped against the {@link AggregationOperationContext}. <br /> * {@link MongoExpression#toDocument() Document} is mapped against the {@link AggregationOperationContext}. <br />
@ -58,6 +46,18 @@ public interface AggregationExpression extends MongoExpression {
return (context) -> context.getMappedObject(expression.toDocument()); return (context) -> context.getMappedObject(expression.toDocument());
} }
/**
* Obtain the as is (unmapped) representation of the {@link AggregationExpression}. Use
* {@link #toDocument(AggregationOperationContext)} with a matching {@link AggregationOperationContext context} to
* engage domain type mapping including field name resolution.
*
* @see org.springframework.data.mongodb.MongoExpression#toDocument()
*/
@Override
default Document toDocument() {
return toDocument(Aggregation.DEFAULT_CONTEXT);
}
/** /**
* Turns the {@link AggregationExpression} into a {@link Document} within the given * Turns the {@link AggregationExpression} into a {@link Document} within the given
* {@link AggregationOperationContext}. * {@link AggregationOperationContext}.

16
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java

@ -63,21 +63,21 @@ public class Field {
* result. * result.
* *
* <pre class="code"> * <pre class="code">
* *
* // { 'name' : { '$toUpper' : '$name' } } * // { 'name' : { '$toUpper' : '$name' } }
* *
* // native MongoDB expression * // native MongoDB expression
* .project(MongoExpression.expressionFromString("'$toUpper' : '$name'")).as("name"); * .project(MongoExpression.expressionFromString("'$toUpper' : '$name'")).as("name");
* *
* // Aggregation Framework expression * // Aggregation Framework expression
* .project(StringOperators.valueOf("name").toUpper()).as("name"); * .project(StringOperators.valueOf("name").toUpper()).as("name");
* *
* // Aggregation Framework SpEL expression * // Aggregation Framework SpEL expression
* .project(AggregationSpELExpression.expressionOf("toUpper(name)")).as("name"); * .project(AggregationSpELExpression.expressionOf("toUpper(name)")).as("name");
* </pre> * </pre>
* *
* @param expression must not be {@literal null}. * @param expression must not be {@literal null}.
* @return new instance of {@link FieldProjectionExpression} - you still need to define the target field name via * @return new instance of {@link FieldProjectionExpression}. Define the target field name through
* {@link FieldProjectionExpression#as(String) as(String)}. * {@link FieldProjectionExpression#as(String) as(String)}.
* @since 3.2 * @since 3.2
*/ */
@ -277,15 +277,15 @@ public class Field {
/** /**
* Intermediate builder part for projecting a {@link MongoExpression} to a result field. * Intermediate builder part for projecting a {@link MongoExpression} to a result field.
* *
* @since 3.2 * @since 3.2
* @author Christoph Strobl * @author Christoph Strobl
*/ */
public interface FieldProjectionExpression { public interface FieldProjectionExpression {
/** /**
* Set the name to be used in the result. * Set the name to be used in the result and return a {@link Field}.
* *
* @param name must not be {@literal null}. * @param name must not be {@literal null}.
* @return the calling instance {@link Field}. * @return the calling instance {@link Field}.
*/ */

2
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateFieldProjectionTests.java

@ -39,6 +39,8 @@ import org.springframework.data.mongodb.test.util.MongoTestTemplate;
import org.springframework.data.mongodb.test.util.Template; import org.springframework.data.mongodb.test.util.Template;
/** /**
* Integration tests for {@link org.springframework.data.mongodb.core.query.Field}.
*
* @author Christoph Strobl * @author Christoph Strobl
*/ */
@ExtendWith(MongoTemplateExtension.class) @ExtendWith(MongoTemplateExtension.class)

1
src/main/asciidoc/new-features.adoc

@ -5,6 +5,7 @@
== What's New in Spring Data MongoDB 3.2 == What's New in Spring Data MongoDB 3.2
* Support for <<embedded-entities,Embedded Types>> to unwrap nested objects into the parent `Document`. * Support for <<embedded-entities,Embedded Types>> to unwrap nested objects into the parent `Document`.
* <<mongo-template.querying.field-selection,Support expressions to define field projections>>.
[[new-features.3.1]] [[new-features.3.1]]
== What's New in Spring Data MongoDB 3.1 == What's New in Spring Data MongoDB 3.1

24
src/main/asciidoc/reference/mongodb.adoc

@ -1251,7 +1251,7 @@ The `Query` class has some additional methods that provide options for the query
==== Selecting fields ==== Selecting fields
MongoDB supports https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results/[projecting fields] returned by a query. MongoDB supports https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results/[projecting fields] returned by a query.
A projection can in- & exclude fields (the `_id` field is always included unless explicitly excluded) based on their name. A projection can include and exclude fields (the `_id` field is always included unless explicitly excluded) based on their name.
.Selecting result fields .Selecting result fields
==== ====
@ -1268,13 +1268,13 @@ public class Person {
Address address; Address address;
} }
query.fields().include("lastname"); <1> query.fields().include("lastname"); <1>
query.fields().exclude("id").include("lastname") <2> query.fields().exclude("id").include("lastname") <2>
query.fields().include("address") <3> query.fields().include("address") <3>
query.fields().include("address.city") <4> query.fields().include("address.city") <4>
---- ----
@ -1284,31 +1284,31 @@ query.fields().include("address.city") <4>
<4> Result will contain the `_id` and and `address` object that only contains the `city` field via `{ "address.city" : 1 }`. <4> Result will contain the `_id` and and `address` object that only contains the `city` field via `{ "address.city" : 1 }`.
==== ====
Starting with MongoDB 4.4 it is possible to use the aggregation expressions syntax for field projections as shown below. Starting with MongoDB 4.4 you can use aggregation expressions for field projections as shown below:
.Computing result fields with expressions .Computing result fields using expressions
==== ====
[source,java] [source,java]
---- ----
query.fields() query.fields()
.project(MongoExpression.create("'$toUpper' : '$last_name'")) <1> .project(MongoExpression.create("'$toUpper' : '$last_name'")) <1>
.as("last_name"); <2> .as("last_name"); <2>
query.fields() query.fields()
.project(StringOperators.valueOf("lastname").toUpper()) <3> .project(StringOperators.valueOf("lastname").toUpper()) <3>
.as("last_name"); .as("last_name");
query.fields() query.fields()
.project(AggregationSpELExpression.expressionOf("toUpper(lastname)")) <4> .project(AggregationSpELExpression.expressionOf("toUpper(lastname)")) <4>
.as("last_name"); .as("last_name");
---- ----
<1> Use a native expression. The used field names must refer to the ones of the document within the database. <1> Use a native expression. The used field name must refer to field names within the database document.
<2> Assign the field name that shall hold the expression result in the target document. The resulting field name will never be mapped against the domain model. <2> Assign the field name to which the expression result is projected. The resulting field name is not mapped against the domain model.
<3> Use an `AggregationExpression`. Other than native `MongoExpression`, field names are mapped to the ones used in the domain model. <3> Use an `AggregationExpression`. Other than native `MongoExpression`, field names are mapped to the ones used in the domain model.
<4> Use SpEL along with an `AggregationExpression` to invoke expression functions. Field names are mapped to the ones used in the domain model. <4> Use SpEL along with an `AggregationExpression` to invoke expression functions. Field names are mapped to the ones used in the domain model.
==== ====
`@Query(fields='...')` allows usage of expression field projections at `Repository` level as described in <<mongodb.repositories.queries.json-based>>. `@Query(fields="…")` allows usage of expression field projections at `Repository` level as described in <<mongodb.repositories.queries.json-based>>.
[[mongo-template.querying]] [[mongo-template.querying]]
=== Methods for Querying for Documents === Methods for Querying for Documents

Loading…
Cancel
Save