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.
This commit is contained in:
Mark Paluch
2021-03-18 12:02:42 +01:00
parent af6d1eff0c
commit f4556406bd
7 changed files with 61 additions and 57 deletions

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb;
import java.util.Arrays;
import org.bson.Document;
import org.bson.codecs.DocumentCodec;
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
* {@link #toDocument()}.
* <p />
*
*
* <pre class="code">
* $toUpper : $name -> { '$toUpper' : '$name' }
*
*
* { '$toUpper' : '$name' } -> { '$toUpper' : '$name' }
*
*
* { '$toUpper' : '?0' }, "$name" -> { '$toUpper' : '$name' }
* </pre>
*
*
* 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)}.
*
@@ -49,11 +51,9 @@ public class BindableMongoExpression implements MongoExpression {
private final String expressionString;
@Nullable //
private final CodecRegistryProvider codecRegistryProvider;
private final @Nullable CodecRegistryProvider codecRegistryProvider;
@Nullable //
private final Object[] args;
private final @Nullable Object[] args;
private final Lazy<Document> target;
@@ -118,16 +118,8 @@ public class BindableMongoExpression implements MongoExpression {
*/
@Override
public String toString() {
return "BindableMongoExpression{" + "expressionString='" + expressionString + '\'' + ", args=" + args + '}';
}
private String wrapJsonIfNecessary(String json) {
if (StringUtils.hasText(json) && (json.startsWith("{") && json.endsWith("}"))) {
return json;
}
return "{" + json + "}";
return "BindableMongoExpression{" + "expressionString='" + expressionString + '\'' + ", args="
+ Arrays.toString(args) + '}';
}
private Document parse() {
@@ -148,4 +140,13 @@ public class BindableMongoExpression implements MongoExpression {
: new ParameterBindingDocumentCodec(codecRegistryProvider.getCodecRegistry());
return codec.decode(expression, args);
}
private static String wrapJsonIfNecessary(String json) {
if (StringUtils.hasText(json) && (json.startsWith("{") && json.endsWith("}"))) {
return json;
}
return "{" + json + "}";
}
}

View File

@@ -39,13 +39,6 @@ package org.springframework.data.mongodb;
@FunctionalInterface
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 />
* 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) {
return new BindableMongoExpression(expression, args);
}
/**
* Obtain the native {@link org.bson.Document} representation.
*
* @return never {@literal null}.
*/
org.bson.Document toDocument();
}

View File

@@ -28,18 +28,6 @@ import org.springframework.data.mongodb.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
* {@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());
}
/**
* 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
* {@link AggregationOperationContext}.

View File

@@ -63,21 +63,21 @@ public class Field {
* result.
*
* <pre class="code">
*
*
* // { 'name' : { '$toUpper' : '$name' } }
*
*
* // native MongoDB expression
* .project(MongoExpression.expressionFromString("'$toUpper' : '$name'")).as("name");
*
*
* // Aggregation Framework expression
* .project(StringOperators.valueOf("name").toUpper()).as("name");
*
*
* // Aggregation Framework SpEL expression
* .project(AggregationSpELExpression.expressionOf("toUpper(name)")).as("name");
* </pre>
*
* @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)}.
* @since 3.2
*/
@@ -277,15 +277,15 @@ public class Field {
/**
* Intermediate builder part for projecting a {@link MongoExpression} to a result field.
*
*
* @since 3.2
* @author Christoph Strobl
*/
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}.
* @return the calling instance {@link Field}.
*/

View File

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

View File

@@ -5,6 +5,7 @@
== What's New in Spring Data MongoDB 3.2
* 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]]
== What's New in Spring Data MongoDB 3.1

View File

@@ -1251,7 +1251,7 @@ The `Query` class has some additional methods that provide options for the query
==== Selecting fields
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
====
@@ -1268,13 +1268,13 @@ public class Person {
Address address;
}
query.fields().include("lastname"); <1>
query.fields().include("lastname"); <1>
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 }`.
====
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]
----
query.fields()
.project(MongoExpression.create("'$toUpper' : '$last_name'")) <1>
.as("last_name"); <2>
.project(MongoExpression.create("'$toUpper' : '$last_name'")) <1>
.as("last_name"); <2>
query.fields()
.project(StringOperators.valueOf("lastname").toUpper()) <3>
.project(StringOperators.valueOf("lastname").toUpper()) <3>
.as("last_name");
query.fields()
.project(AggregationSpELExpression.expressionOf("toUpper(lastname)")) <4>
.as("last_name");
----
<1> Use a native expression. The used field names must refer to the ones of the document within the database.
<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.
<1> Use a native expression. The used field name must refer to field names within the database document.
<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.
<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]]
=== Methods for Querying for Documents