Support expressions in query field projections.

// explicit via dedicated AggregationExpression
query.fields()
  .project(StringOperators.valueOf("name").toUpper())
  .as("name");

// with a user provided expression parsed from a String
query.fields().project(MongoExpression.create("'$toUpper' : '$name'"))
  .as("name")

// using SpEL support
query.fields().project(AggregationSpELExpression.expressionOf("toUpper(name)"))
  .as("name");

// with parameter binding
query.fields().project(
    MongoExpression.create("'$toUpper' : '?0'").bind("$name")
  ).as("name")

// via the @Query annotation on repositories
@Query(value = "{ 'id' : ?0 }", fields = "{ 'name': { '$toUpper': '$name' } }")

Closes: #3583
Original pull request: #3585.
This commit is contained in:
Christoph Strobl
2021-03-10 13:27:43 +01:00
committed by Mark Paluch
parent 191993caef
commit af6d1eff0c
10 changed files with 627 additions and 7 deletions

View File

@@ -1247,6 +1247,69 @@ The `Query` class has some additional methods that provide options for the query
* `Query` *skip* `(int skip)` used to skip the provided number of documents in the results (used for paging)
* `Query` *with* `(Sort sort)` used to provide sort definition for the results
[[mongo-template.querying.field-selection]]
==== 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.
.Selecting result fields
====
[source,java]
----
public class Person {
@Id String id;
String firstname;
@Field("last_name")
String lastname;
Address address;
}
query.fields().include("lastname"); <1>
query.fields().exclude("id").include("lastname") <2>
query.fields().include("address") <3>
query.fields().include("address.city") <4>
----
<1> Result will contain both `_id` and `last_name` via `{ "last_name" : 1 }`.
<2> Result will only contain the `last_name` via `{ "_id" : 0, "last_name" : 1 }`.
<3> Result will contain the `_id` and entire `address` object via `{ "address" : 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.
.Computing result fields with expressions
====
[source,java]
----
query.fields()
.project(MongoExpression.create("'$toUpper' : '$last_name'")) <1>
.as("last_name"); <2>
query.fields()
.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.
<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>>.
[[mongo-template.querying]]
=== Methods for Querying for Documents