Restructure documentation.
Move examples into test source root to ensure proper compilation. Remove lingering asciidoc resources. Merge entity callbacks into lifecycle events. Add links from MongoDB overview. See: #4497
This commit is contained in:
committed by
Christoph Strobl
parent
5acb174837
commit
7c50f991ed
@@ -16,12 +16,10 @@
|
||||
// tag::file[]
|
||||
package org.springframework.data.mongodb.example;
|
||||
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.where;
|
||||
import static org.springframework.data.mongodb.core.query.Query.query;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
|
||||
import com.mongodb.client.MongoClients;
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.data.mongodb.example;
|
||||
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 64 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 8.9 KiB |
1
src/main/antora/modules/ROOT/examples/example
Symbolic link
1
src/main/antora/modules/ROOT/examples/example
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../../spring-data-mongodb/src/test/java/org/springframework/data/mongodb/example
|
||||
@@ -7,40 +7,40 @@
|
||||
* xref:mongodb.adoc[]
|
||||
** xref:mongodb/getting-started.adoc[]
|
||||
** xref:mongodb/configuration.adoc[]
|
||||
|
||||
** xref:mongodb/template-api.adoc[]
|
||||
*** xref:mongodb/template-config.adoc[]
|
||||
*** xref:mongodb/template-collection-management.adoc[]
|
||||
*** xref:mongodb/template-crud-operations.adoc[]
|
||||
*** xref:mongodb/template-query-operations.adoc[]
|
||||
**** xref:mongodb/template-query-options.adoc[]
|
||||
*** xref:mongodb/template-document-count.adoc[]
|
||||
*** xref:mongodb/aggregation-framework.adoc[]
|
||||
*** xref:mongodb/template-collection-management.adoc[]
|
||||
**** xref:mongodb/template-collection-schema.adoc[]
|
||||
*** xref:mongodb/template-gridfs.adoc[]
|
||||
*** xref:mongodb/template-document-count.adoc[]
|
||||
|
||||
** xref:mongodb/template-gridfs.adoc[]
|
||||
** xref:mongodb/mapping/mapping.adoc[]
|
||||
*** xref:mongodb/mapping/mapping-schema.adoc[]
|
||||
*** xref:mongodb/mapping/custom-conversions.adoc[Type based Converter]
|
||||
*** xref:mongodb/mapping/property-converters.adoc[]
|
||||
*** xref:mongodb/mapping/unwrapping-entities.adoc[]
|
||||
*** xref:mongodb/mapping/document-references.adoc[Object References]
|
||||
*** xref:mongodb/mapping/entity-callbacks.adoc[Entity Callbacks]
|
||||
*** xref:mongodb/mapping/lifecycle-events.adoc[]
|
||||
*** xref:mongodb/mapping/mapping-index-management.adoc[]
|
||||
|
||||
** xref:mongodb/lifecycle-events.adoc[]
|
||||
** xref:mongodb/auditing.adoc[]
|
||||
** xref:mongodb/client-session-transactions.adoc[]
|
||||
** xref:mongodb/change-streams.adoc[]
|
||||
** xref:mongodb/tailable-cursors.adoc[]
|
||||
** xref:mongodb/sharding.adoc[]
|
||||
** xref:mongodb/mongo-encryption.adoc[]
|
||||
** xref:mongodb/auditing.adoc[]
|
||||
|
||||
// Repository
|
||||
* xref:repositories.adoc[]
|
||||
** xref:repositories/core-concepts.adoc[]
|
||||
** xref:repositories/definition.adoc[]
|
||||
** xref:mongodb/repositories/repositories.adoc[]
|
||||
*** xref:mongodb/repositories/repositories-index-hints.adoc[]
|
||||
*** xref:mongodb/repositories/repositories-collation.adoc[]
|
||||
** xref:repositories/create-instances.adoc[]
|
||||
** xref:repositories/query-methods-details.adoc[]
|
||||
** xref:mongodb/repositories/query-methods.adoc[]
|
||||
** xref:repositories/projections.adoc[]
|
||||
** xref:repositories/custom-implementations.adoc[]
|
||||
** xref:repositories/core-domain-events.adoc[]
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
// include::{commons}@data-commons::page$kotlin/object-mapping.adoc[]
|
||||
@@ -4,17 +4,18 @@
|
||||
|
||||
Spring Data support for MongoDB contains a wide range of features:
|
||||
|
||||
* Spring configuration support with Java-based `@Configuration` classes or an XML namespace for a Mongo driver instance and replica sets.
|
||||
* `MongoTemplate` helper class that increases productivity when performing common Mongo operations. Includes integrated object mapping between documents and POJOs.
|
||||
* Exception translation into Spring's portable Data Access Exception hierarchy.
|
||||
* Feature-rich Object Mapping integrated with Spring's Conversion Service.
|
||||
* Annotation-based mapping metadata that is extensible to support other metadata formats.
|
||||
* Persistence and mapping lifecycle events.
|
||||
* Java-based Query, Criteria, and Update DSLs.
|
||||
* Automatic implementation of Repository interfaces, including support for custom finder methods.
|
||||
* QueryDSL integration to support type-safe queries.
|
||||
* Multi Document Transactions.
|
||||
* GeoSpatial integration.
|
||||
* xref:mongodb/template-config.adoc[Spring configuration support] with Java-based `@Configuration` classes or an XML namespace for a Mongo driver instance and replica sets.
|
||||
* xref:mongodb/template-api.adoc[`MongoTemplate` helper class] that increases productivity when performing common Mongo operations.
|
||||
Includes integrated object mapping between documents and POJOs.
|
||||
* xref:mongodb/template-api.adoc#mongo-template.exception-translation[Exception translation] into Spring's portable Data Access Exception hierarchy.
|
||||
* Feature-rich xref:mongodb/mapping/mapping.adoc[Object Mapping] integrated with Spring's Conversion Service.
|
||||
* xref:mongodb/mapping/mapping.adoc#mapping-usage-annotations[Annotation-based mapping metadata] that is extensible to support other metadata formats.
|
||||
* xref:mongodb/lifecycle-events.adoc[Persistence and mapping lifecycle events].
|
||||
* xref:mongodb/template-query-operations.adoc[Java-based Query, Criteria, and Update DSLs].
|
||||
* Automatic implementation of xref:repositories.adoc[Repository interfaces], including support for custom query methods.
|
||||
* xref:mongodb/repositories/repositories.adoc#mongodb.repositories.queries.type-safe[QueryDSL integration] to support type-safe queries.
|
||||
* xref:mongodb/client-session-transactions.adoc[Multi-Document Transactions].
|
||||
* xref:mongodb/template-query-operations.adoc#mongo.geo-json[GeoSpatial integration].
|
||||
|
||||
For most tasks, you should use `MongoTemplate` or the Repository support, which both leverage the rich mapping functionality.
|
||||
`MongoTemplate` is the place to look for accessing functionality such as incrementing counters or ad-hoc CRUD operations.
|
||||
|
||||
@@ -19,7 +19,7 @@ Then you can create a `Person` class to persist:
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
include::example$Person.java[tags=file]
|
||||
include::example$example/Person.java[tags=file]
|
||||
----
|
||||
====
|
||||
|
||||
@@ -31,14 +31,14 @@ Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
include::example$MongoApplication.java[tags=file]
|
||||
include::example$example/MongoApplication.java[tags=file]
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
include::example$ReactiveMongoApplication.java[tags=file]
|
||||
include::example$example/ReactiveMongoApplication.java[tags=file]
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
@@ -49,8 +49,58 @@ Declaring these beans in your Spring ApplicationContext causes them to be invoke
|
||||
* `onAfterConvert`: Called in `MongoTemplate` `find`, `findAndRemove`, `findOne`, and `getCollection` methods after the `Document` has been retrieved from the database was converted to a POJO.
|
||||
====
|
||||
|
||||
NOTE: Lifecycle events are only emitted for root level types. Complex types used as properties within a document root are not subject to event publication unless they are document references annotated with `@DBRef`.
|
||||
NOTE: Lifecycle events are only emitted for root level types.
|
||||
Complex types used as properties within a document root are not subject to event publication unless they are document references annotated with `@DBRef`.
|
||||
|
||||
WARNING: Lifecycle events depend on an `ApplicationEventMulticaster`, which in case of the `SimpleApplicationEventMulticaster` can be configured with a `TaskExecutor`, and therefore gives no guarantees when an Event is processed.
|
||||
|
||||
include::{commons}@data-commons::page$entity-callbacks.adoc[leveloffset=+1]
|
||||
|
||||
[[mongo.entity-callbacks]]
|
||||
== Store specific EntityCallbacks
|
||||
|
||||
Spring Data MongoDB uses the `EntityCallback` API for its auditing support and reacts on the following callbacks.
|
||||
|
||||
.Supported Entity Callbacks
|
||||
[%header,cols="4"]
|
||||
|===
|
||||
| Callback
|
||||
| Method
|
||||
| Description
|
||||
| Order
|
||||
|
||||
| `ReactiveBeforeConvertCallback`
|
||||
`BeforeConvertCallback`
|
||||
| `onBeforeConvert(T entity, String collection)`
|
||||
| Invoked before a domain object is converted to `org.bson.Document`.
|
||||
| `Ordered.LOWEST_PRECEDENCE`
|
||||
|
||||
| `ReactiveAfterConvertCallback`
|
||||
`AfterConvertCallback`
|
||||
| `onAfterConvert(T entity, org.bson.Document target, String collection)`
|
||||
| Invoked after a domain object is loaded. +
|
||||
Can modify the domain object after reading it from a `org.bson.Document`.
|
||||
| `Ordered.LOWEST_PRECEDENCE`
|
||||
|
||||
| `ReactiveAuditingEntityCallback`
|
||||
`AuditingEntityCallback`
|
||||
| `onBeforeConvert(Object entity, String collection)`
|
||||
| Marks an auditable entity _created_ or _modified_
|
||||
| 100
|
||||
|
||||
| `ReactiveBeforeSaveCallback`
|
||||
`BeforeSaveCallback`
|
||||
| `onBeforeSave(T entity, org.bson.Document target, String collection)`
|
||||
| Invoked before a domain object is saved. +
|
||||
Can modify the target, to be persisted, `Document` containing all mapped entity information.
|
||||
| `Ordered.LOWEST_PRECEDENCE`
|
||||
|
||||
| `ReactiveAfterSaveCallback`
|
||||
`AfterSaveCallback`
|
||||
| `onAfterSave(T entity, org.bson.Document target, String collection)`
|
||||
| Invoked before a domain object is saved. +
|
||||
Can modify the domain object, to be returned after save, `Document` containing all mapped entity information.
|
||||
| `Ordered.LOWEST_PRECEDENCE`
|
||||
|
||||
|===
|
||||
|
||||
@@ -30,7 +30,7 @@ public class Payment {
|
||||
"date" : ISODate("2019-04-03T12:11:01.870Z") <3>
|
||||
}
|
||||
----
|
||||
<1> String _id_ values that represent a valid `ObjectId` are converted automatically. See xref:mongodb/template-id-handling.adoc#mongo-template.id-handling[How the `_id` Field is Handled in the Mapping Layer]
|
||||
<1> String _id_ values that represent a valid `ObjectId` are converted automatically. See xref:mongodb/template-crud-operations.adoc#mongo-template.id-handling[How the `_id` Field is Handled in the Mapping Layer]
|
||||
for details.
|
||||
<2> The desired target type is explicitly defined as `Decimal128` which translates to `NumberDecimal`. Otherwise the
|
||||
`BigDecimal` value would have been truned into a `String`.
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
include::{commons}@data-commons::page$entity-callbacks.adoc[]
|
||||
|
||||
[[mongo.entity-callbacks]]
|
||||
== Store specific EntityCallbacks
|
||||
|
||||
Spring Data MongoDB uses the `EntityCallback` API for its auditing support and reacts on the following callbacks.
|
||||
|
||||
.Supported Entity Callbacks
|
||||
[%header,cols="4"]
|
||||
|===
|
||||
| Callback
|
||||
| Method
|
||||
| Description
|
||||
| Order
|
||||
|
||||
| Reactive/BeforeConvertCallback
|
||||
| `onBeforeConvert(T entity, String collection)`
|
||||
| Invoked before a domain object is converted to `org.bson.Document`.
|
||||
| `Ordered.LOWEST_PRECEDENCE`
|
||||
|
||||
| Reactive/AfterConvertCallback
|
||||
| `onAfterConvert(T entity, org.bson.Document target, String collection)`
|
||||
| Invoked after a domain object is loaded. +
|
||||
Can modify the domain object after reading it from a `org.bson.Document`.
|
||||
| `Ordered.LOWEST_PRECEDENCE`
|
||||
|
||||
| Reactive/AuditingEntityCallback
|
||||
| `onBeforeConvert(Object entity, String collection)`
|
||||
| Marks an auditable entity _created_ or _modified_
|
||||
| 100
|
||||
|
||||
| Reactive/BeforeSaveCallback
|
||||
| `onBeforeSave(T entity, org.bson.Document target, String collection)`
|
||||
| Invoked before a domain object is saved. +
|
||||
Can modify the target, to be persisted, `Document` containing all mapped entity information.
|
||||
| `Ordered.LOWEST_PRECEDENCE`
|
||||
|
||||
| Reactive/AfterSaveCallback
|
||||
| `onAfterSave(T entity, org.bson.Document target, String collection)`
|
||||
| Invoked before a domain object is saved. +
|
||||
Can modify the domain object, to be returned after save, `Document` containing all mapped entity information.
|
||||
| `Ordered.LOWEST_PRECEDENCE`
|
||||
|
||||
|===
|
||||
|
||||
@@ -9,21 +9,26 @@ The `MappingMongoConverter` also lets you map objects to documents without provi
|
||||
|
||||
This section describes the features of the `MappingMongoConverter`, including fundamentals, how to use conventions for mapping objects to documents and how to override those conventions with annotation-based mapping metadata.
|
||||
|
||||
include::{commons}@data-commons::page$object-mapping.adoc[leveloffset=+1]
|
||||
|
||||
[[mapping-conventions]]
|
||||
== Convention-based Mapping
|
||||
|
||||
`MappingMongoConverter` has a few conventions for mapping objects to documents when no additional mapping metadata is provided. The conventions are:
|
||||
`MappingMongoConverter` has a few conventions for mapping objects to documents when no additional mapping metadata is provided.
|
||||
The conventions are:
|
||||
|
||||
* The short Java class name is mapped to the collection name in the following manner. The class `com.bigbank.SavingsAccount` maps to the `savingsAccount` collection name.
|
||||
* The short Java class name is mapped to the collection name in the following manner.
|
||||
The class `com.bigbank.SavingsAccount` maps to the `savingsAccount` collection name.
|
||||
* All nested objects are stored as nested objects in the document and *not* as DBRefs.
|
||||
* The converter uses any Spring Converters registered with it to override the default mapping of object properties to document fields and values.
|
||||
* The fields of an object are used to convert to and from fields in the document. Public `JavaBean` properties are not used.
|
||||
* If you have a single non-zero-argument constructor whose constructor argument names match top-level field names of document, that constructor is used. Otherwise, the zero-argument constructor is used. If there is more than one non-zero-argument constructor, an exception will be thrown.
|
||||
* The fields of an object are used to convert to and from fields in the document.
|
||||
Public `JavaBean` properties are not used.
|
||||
* If you have a single non-zero-argument constructor whose constructor argument names match top-level field names of document, that constructor is used.Otherwise, the zero-argument constructor is used.If there is more than one non-zero-argument constructor, an exception will be thrown.
|
||||
|
||||
[[mapping.conventions.id-field]]
|
||||
=== How the `_id` field is handled in the mapping layer.
|
||||
|
||||
MongoDB requires that you have an `_id` field for all documents. If you don't provide one the driver will assign a ObjectId with a generated value. The "_id" field can be of any type the, other than arrays, so long as it is unique. The driver naturally supports all primitive types and Dates. When using the `MappingMongoConverter` there are certain rules that govern how properties from the Java class is mapped to this `_id` field.
|
||||
MongoDB requires that you have an `_id` field for all documents.If you don't provide one the driver will assign a ObjectId with a generated value.The "_id" field can be of any type the, other than arrays, so long as it is unique.The driver naturally supports all primitive types and Dates.When using the `MappingMongoConverter` there are certain rules that govern how properties from the Java class is mapped to this `_id` field.
|
||||
|
||||
The following outlines what field will be mapped to the `_id` document field:
|
||||
|
||||
@@ -569,6 +574,7 @@ Additional examples for using the `@PersistenceConstructor` annotation can be fo
|
||||
[[mapping-usage-events]]
|
||||
=== Mapping Framework Events
|
||||
|
||||
Events are fired throughout the lifecycle of the mapping process. This is described in the xref:mongodb/mapping/lifecycle-events.adoc[Lifecycle Events] section.
|
||||
Events are fired throughout the lifecycle of the mapping process.
|
||||
This is described in the xref:mongodb/lifecycle-events.adoc[Lifecycle Events] section.
|
||||
|
||||
Declaring these beans in your Spring ApplicationContext causes them to be invoked whenever the event is dispatched.
|
||||
|
||||
@@ -15,9 +15,9 @@ Specific data types require deterministic encryption to preserve equality compar
|
||||
== Automatic Encryption
|
||||
|
||||
MongoDB supports https://www.mongodb.com/docs/manual/core/csfle/[Client-Side Field Level Encryption] out of the box using the MongoDB driver with its Automatic Encryption feature.
|
||||
Automatic Encryption requires a xref:mongodb/template-collection-schema.adoc[JSON Schema] that allows to perform encrypted read and write operations without the need to provide an explicit en-/decryption step.
|
||||
Automatic Encryption requires a xref:mongodb/mapping/mapping-schema.adoc[JSON Schema] that allows to perform encrypted read and write operations without the need to provide an explicit en-/decryption step.
|
||||
|
||||
Please refer to the xref:mongodb/template-collection-schema.adoc#mongo.jsonSchema.encrypted-fields[JSON Schema] section for more information on defining a JSON Schema that holds encryption information.
|
||||
Please refer to the xref:mongodb/mapping/mapping-schema.adoc#mongo.jsonSchema.encrypted-fields[JSON Schema] section for more information on defining a JSON Schema that holds encryption information.
|
||||
|
||||
To make use of a the `MongoJsonSchema` it needs to be combined with `AutoEncryptionSettings` which can be done eg. via a `MongoClientSettingsBuilderCustomizer`.
|
||||
|
||||
@@ -50,7 +50,7 @@ MongoClientSettingsBuilderCustomizer customizer(MappingContext mappingContext) {
|
||||
== Explicit Encryption
|
||||
|
||||
Explicit encryption uses the MongoDB driver's encryption library (`org.mongodb:mongodb-crypt`) to perform encryption and decryption tasks.
|
||||
The `@ExplicitEncrypted` annotation is a combination of the `@Encrypted` annotation used for xref:mongodb/template-collection-schema.adoc#mongo.jsonSchema.encrypted-fields[JSON Schema creation] and a xref:mongodb/mapping/property-converters.adoc[Property Converter].
|
||||
The `@ExplicitEncrypted` annotation is a combination of the `@Encrypted` annotation used for xref:mongodb/mapping/mapping-schema.adoc#mongo.jsonSchema.encrypted-fields[JSON Schema creation] and a xref:mongodb/mapping/property-converters.adoc[Property Converter].
|
||||
In other words, `@ExplicitEncrypted` uses existing building blocks to combine them for simplified explicit encryption support.
|
||||
|
||||
[NOTE]
|
||||
|
||||
@@ -0,0 +1,875 @@
|
||||
[[mongodb.repositories.queries]]
|
||||
= MongoDB-specific Query Methods
|
||||
|
||||
Most of the data access operations you usually trigger on a repository result in a query being executed against the MongoDB databases.
|
||||
Defining such a query is a matter of declaring a method on the repository interface, as the following example shows:
|
||||
|
||||
.PersonRepository with query methods
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends PagingAndSortingRepository<Person, String> {
|
||||
|
||||
List<Person> findByLastname(String lastname); <1>
|
||||
|
||||
Page<Person> findByFirstname(String firstname, Pageable pageable); <2>
|
||||
|
||||
Person findByShippingAddresses(Address address); <3>
|
||||
|
||||
Person findFirstByLastname(String lastname); <4>
|
||||
|
||||
Stream<Person> findAllBy(); <5>
|
||||
}
|
||||
----
|
||||
<1> The `findByLastname` method shows a query for all people with the given last name.
|
||||
The query is derived by parsing the method name for constraints that can be concatenated with `And` and `Or`.
|
||||
Thus, the method name results in a query expression of `{"lastname" : lastname}`.
|
||||
<2> Applies pagination to a query.
|
||||
You can equip your method signature with a `Pageable` parameter and let the method return a `Page` instance and Spring Data automatically pages the query accordingly.
|
||||
<3> Shows that you can query based on properties that are not primitive types.
|
||||
Throws `IncorrectResultSizeDataAccessException` if more than one match is found.
|
||||
<4> Uses the `First` keyword to restrict the query to only the first result.
|
||||
Unlike <3>, this method does not throw an exception if more than one match is found.
|
||||
<5> Uses a Java 8 `Stream` that reads and converts individual elements while iterating the stream.
|
||||
|
||||
Reactive::
|
||||
+
|
||||
====
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface ReactivePersonRepository extends ReactiveSortingRepository<Person, String> {
|
||||
|
||||
Flux<Person> findByFirstname(String firstname); <1>
|
||||
|
||||
Flux<Person> findByFirstname(Publisher<String> firstname); <2>
|
||||
|
||||
Flux<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable); <3>
|
||||
|
||||
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname); <4>
|
||||
|
||||
Mono<Person> findFirstByLastname(String lastname); <5>
|
||||
}
|
||||
----
|
||||
<1> The method shows a query for all people with the given `lastname`. The query is derived by parsing the method name for constraints that can be concatenated with `And` and `Or`. Thus, the method name results in a query expression of `{"lastname" : lastname}`.
|
||||
<2> The method shows a query for all people with the given `firstname` once the `firstname` is emitted by the given `Publisher`.
|
||||
<3> Use `Pageable` to pass offset and sorting parameters to the database.
|
||||
<4> Find a single entity for the given criteria. It completes with `IncorrectResultSizeDataAccessException` on non-unique results.
|
||||
<5> Unless <4>, the first entity is always emitted even if the query yields more result documents.
|
||||
|
||||
WARNING: The `Page` return type (as in `Mono<Page>`) is not supported by reactive repositories.
|
||||
|
||||
It is possible to use `Pageable` in derived finder methods, to pass on `sort`, `limit` and `offset` parameters to the query to reduce load and network traffic.
|
||||
The returned `Flux` will only emit data within the declared range.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
Pageable page = PageRequest.of(1, 10, Sort.by("lastname"));
|
||||
Flux<Person> persons = repository.findByFirstnameOrderByLastname("luke", page);
|
||||
----
|
||||
====
|
||||
======
|
||||
|
||||
NOTE: We do not support referring to parameters that are mapped as `DBRef` in the domain class.
|
||||
|
||||
The following table shows the keywords that are supported for query methods:
|
||||
|
||||
[cols="1,2,3",options="header"]
|
||||
.Supported keywords for query methods
|
||||
|===
|
||||
| Keyword
|
||||
| Sample
|
||||
| Logical result
|
||||
|
||||
| `After`
|
||||
| `findByBirthdateAfter(Date date)`
|
||||
| `{"birthdate" : {"$gt" : date}}`
|
||||
|
||||
| `GreaterThan`
|
||||
| `findByAgeGreaterThan(int age)`
|
||||
| `{"age" : {"$gt" : age}}`
|
||||
|
||||
| `GreaterThanEqual`
|
||||
| `findByAgeGreaterThanEqual(int age)`
|
||||
| `{"age" : {"$gte" : age}}`
|
||||
|
||||
| `Before`
|
||||
| `findByBirthdateBefore(Date date)`
|
||||
| `{"birthdate" : {"$lt" : date}}`
|
||||
|
||||
| `LessThan`
|
||||
| `findByAgeLessThan(int age)`
|
||||
| `{"age" : {"$lt" : age}}`
|
||||
|
||||
| `LessThanEqual`
|
||||
| `findByAgeLessThanEqual(int age)`
|
||||
| `{"age" : {"$lte" : age}}`
|
||||
|
||||
| `Between`
|
||||
| `findByAgeBetween(int from, int to)` +
|
||||
`findByAgeBetween(Range<Integer> range)`
|
||||
| `{"age" : {"$gt" : from, "$lt" : to}}` +
|
||||
lower / upper bounds (`$gt` / `$gte` & `$lt` / `$lte`) according to `Range`
|
||||
|
||||
| `In`
|
||||
| `findByAgeIn(Collection ages)`
|
||||
| `{"age" : {"$in" : [ages...]}}`
|
||||
|
||||
| `NotIn`
|
||||
| `findByAgeNotIn(Collection ages)`
|
||||
| `{"age" : {"$nin" : [ages...]}}`
|
||||
|
||||
| `IsNotNull`, `NotNull`
|
||||
| `findByFirstnameNotNull()`
|
||||
| `{"firstname" : {"$ne" : null}}`
|
||||
|
||||
| `IsNull`, `Null`
|
||||
| `findByFirstnameNull()`
|
||||
| `{"firstname" : null}`
|
||||
|
||||
| `Like`, `StartingWith`, `EndingWith`
|
||||
| `findByFirstnameLike(String name)`
|
||||
| `{"firstname" : name} (name as regex)`
|
||||
|
||||
| `NotLike`, `IsNotLike`
|
||||
| `findByFirstnameNotLike(String name)`
|
||||
| `{"firstname" : { "$not" : name }} (name as regex)`
|
||||
|
||||
| `Containing` on String
|
||||
| `findByFirstnameContaining(String name)`
|
||||
| `{"firstname" : name} (name as regex)`
|
||||
|
||||
| `NotContaining` on String
|
||||
| `findByFirstnameNotContaining(String name)`
|
||||
| `{"firstname" : { "$not" : name}} (name as regex)`
|
||||
|
||||
| `Containing` on Collection
|
||||
| `findByAddressesContaining(Address address)`
|
||||
| `{"addresses" : { "$in" : address}}`
|
||||
|
||||
| `NotContaining` on Collection
|
||||
| `findByAddressesNotContaining(Address address)`
|
||||
| `{"addresses" : { "$not" : { "$in" : address}}}`
|
||||
|
||||
| `Regex`
|
||||
| `findByFirstnameRegex(String firstname)`
|
||||
| `{"firstname" : {"$regex" : firstname }}`
|
||||
|
||||
| `(No keyword)`
|
||||
| `findByFirstname(String name)`
|
||||
| `{"firstname" : name}`
|
||||
|
||||
| `Not`
|
||||
| `findByFirstnameNot(String name)`
|
||||
| `{"firstname" : {"$ne" : name}}`
|
||||
|
||||
| `Near`
|
||||
| `findByLocationNear(Point point)`
|
||||
| `{"location" : {"$near" : [x,y]}}`
|
||||
|
||||
| `Near`
|
||||
| `findByLocationNear(Point point, Distance max)`
|
||||
| `{"location" : {"$near" : [x,y], "$maxDistance" : max}}`
|
||||
|
||||
| `Near`
|
||||
| `findByLocationNear(Point point, Distance min, Distance max)`
|
||||
| `{"location" : {"$near" : [x,y], "$minDistance" : min, "$maxDistance" : max}}`
|
||||
|
||||
| `Within`
|
||||
| `findByLocationWithin(Circle circle)`
|
||||
| `{"location" : {"$geoWithin" : {"$center" : [ [x, y], distance]}}}`
|
||||
|
||||
| `Within`
|
||||
| `findByLocationWithin(Box box)`
|
||||
| `{"location" : {"$geoWithin" : {"$box" : [ [x1, y1], x2, y2]}}}`
|
||||
|
||||
| `IsTrue`, `True`
|
||||
| `findByActiveIsTrue()`
|
||||
| `{"active" : true}`
|
||||
|
||||
| `IsFalse`, `False`
|
||||
| `findByActiveIsFalse()`
|
||||
| `{"active" : false}`
|
||||
|
||||
| `Exists`
|
||||
| `findByLocationExists(boolean exists)`
|
||||
| `{"location" : {"$exists" : exists }}`
|
||||
|
||||
| `IgnoreCase`
|
||||
| `findByUsernameIgnoreCase(String username)`
|
||||
| `{"username" : {"$regex" : "^username$", "$options" : "i" }}`
|
||||
|===
|
||||
|
||||
NOTE: If the property criterion compares a document, the order of the fields and exact equality in the document matters.
|
||||
|
||||
[[mongodb.repositories.queries.geo-spatial]]
|
||||
== Geo-spatial Queries
|
||||
|
||||
As you saw in the preceding table of keywords, a few keywords trigger geo-spatial operations within a MongoDB query.
|
||||
The `Near` keyword allows some further modification, as the next few examples show.
|
||||
|
||||
The following example shows how to define a `near` query that finds all persons with a given distance of a given point:
|
||||
|
||||
.Advanced `Near` queries
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
// { 'location' : { '$near' : [point.x, point.y], '$maxDistance' : distance}}
|
||||
List<Person> findByLocationNear(Point location, Distance distance);
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
// { 'location' : { '$near' : [point.x, point.y], '$maxDistance' : distance}}
|
||||
Flux<Person> findByLocationNear(Point location, Distance distance);
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
Adding a `Distance` parameter to the query method allows restricting results to those within the given distance.
|
||||
If the `Distance` was set up containing a `Metric`, we transparently use `$nearSphere` instead of `$code`, as the following example shows:
|
||||
|
||||
.Using `Distance` with `Metrics`
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Point point = new Point(43.7, 48.8);
|
||||
Distance distance = new Distance(200, Metrics.KILOMETERS);
|
||||
… = repository.findByLocationNear(point, distance);
|
||||
// {'location' : {'$nearSphere' : [43.7, 48.8], '$maxDistance' : 0.03135711885774796}}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Reactive Geo-spatial repository queries support the domain type and `GeoResult<T>` results within a reactive wrapper type. `GeoPage` and `GeoResults` are not supported as they contradict the deferred result approach with pre-calculating the average distance. However, you can still pass in a `Pageable` argument to page results yourself.
|
||||
|
||||
Using a `Distance` with a `Metric` causes a `$nearSphere` (instead of a plain `$near`) clause to be added.
|
||||
Beyond that, the actual distance gets calculated according to the `Metrics` used.
|
||||
|
||||
(Note that `Metric` does not refer to metric units of measure.
|
||||
It could be miles rather than kilometers.
|
||||
Rather, `metric` refers to the concept of a system of measurement, regardless of which system you use.)
|
||||
|
||||
NOTE: Using `@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)` on the target property forces usage of the `$nearSphere` operator.
|
||||
|
||||
[[geo-near-queries]]
|
||||
=== Geo-near Queries
|
||||
|
||||
Spring Data MongoDb supports geo-near queries, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
// {'geoNear' : 'location', 'near' : [x, y] }
|
||||
GeoResults<Person> findByLocationNear(Point location);
|
||||
|
||||
// No metric: {'geoNear' : 'person', 'near' : [x, y], maxDistance : distance }
|
||||
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'maxDistance' : distance,
|
||||
// 'distanceMultiplier' : metric.multiplier, 'spherical' : true }
|
||||
GeoResults<Person> findByLocationNear(Point location, Distance distance);
|
||||
|
||||
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'minDistance' : min,
|
||||
// 'maxDistance' : max, 'distanceMultiplier' : metric.multiplier,
|
||||
// 'spherical' : true }
|
||||
GeoResults<Person> findByLocationNear(Point location, Distance min, Distance max);
|
||||
|
||||
// {'geoNear' : 'location', 'near' : [x, y] }
|
||||
GeoResults<Person> findByLocationNear(Point location);
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
// {'geoNear' : 'location', 'near' : [x, y] }
|
||||
Flux<GeoResult<Person>> findByLocationNear(Point location);
|
||||
|
||||
// No metric: {'geoNear' : 'person', 'near' : [x, y], maxDistance : distance }
|
||||
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'maxDistance' : distance,
|
||||
// 'distanceMultiplier' : metric.multiplier, 'spherical' : true }
|
||||
Flux<GeoResult<Person>> findByLocationNear(Point location, Distance distance);
|
||||
|
||||
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'minDistance' : min,
|
||||
// 'maxDistance' : max, 'distanceMultiplier' : metric.multiplier,
|
||||
// 'spherical' : true }
|
||||
Flux<GeoResult<Person>> findByLocationNear(Point location, Distance min, Distance max);
|
||||
|
||||
// {'geoNear' : 'location', 'near' : [x, y] }
|
||||
Flux<GeoResult<Person>> findByLocationNear(Point location);
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
[[mongodb.repositories.queries.json-based]]
|
||||
== JSON-based Query Methods and Field Restriction
|
||||
|
||||
By adding the `org.springframework.data.mongodb.repository.Query` annotation to your repository query methods, you can specify a MongoDB JSON query string to use instead of having the query be derived from the method name, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
@Query("{ 'firstname' : ?0 }")
|
||||
List<Person> findByThePersonsFirstname(String firstname);
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
@Query("{ 'firstname' : ?0 }")
|
||||
Flux<Person> findByThePersonsFirstname(String firstname);
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
The `?0` placeholder lets you substitute the value from the method arguments into the JSON query string.
|
||||
|
||||
NOTE: `String` parameter values are escaped during the binding process, which means that it is not possible to add MongoDB specific operators through the argument.
|
||||
|
||||
You can also use the filter property to restrict the set of properties that is mapped into the Java object, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
@Query(value="{ 'firstname' : ?0 }", fields="{ 'firstname' : 1, 'lastname' : 1}")
|
||||
List<Person> findByThePersonsFirstname(String firstname);
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
@Query(value="{ 'firstname' : ?0 }", fields="{ 'firstname' : 1, 'lastname' : 1}")
|
||||
Flux<Person> findByThePersonsFirstname(String firstname);
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
The query in the preceding example returns only the `firstname`, `lastname` and `Id` properties of the `Person` objects.
|
||||
The `age` property, a `java.lang.Integer`, is not set and its value is therefore null.
|
||||
|
||||
[[mongodb.repositories.queries.sort]]
|
||||
== Sorting Results
|
||||
|
||||
MongoDB repositories allow various approaches to define sorting order.
|
||||
Let's take a look at the following example:
|
||||
|
||||
.Sorting Query Results
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
List<Person> findByFirstnameSortByAgeDesc(String firstname); <1>
|
||||
|
||||
List<Person> findByFirstname(String firstname, Sort sort); <2>
|
||||
|
||||
@Query(sort = "{ age : -1 }")
|
||||
List<Person> findByFirstname(String firstname); <3>
|
||||
|
||||
@Query(sort = "{ age : -1 }")
|
||||
List<Person> findByLastname(String lastname, Sort sort); <4>
|
||||
}
|
||||
----
|
||||
<1> Static sorting derived from method name. `SortByAgeDesc` results in `{ age : -1 }` for the sort parameter.
|
||||
<2> Dynamic sorting using a method argument.
|
||||
`Sort.by(DESC, "age")` creates `{ age : -1 }` for the sort parameter.
|
||||
<3> Static sorting via `Query` annotation.
|
||||
Sort parameter applied as stated in the `sort` attribute.
|
||||
<4> Default sorting via `Query` annotation combined with dynamic one via a method argument. `Sort.unsorted()`
|
||||
results in `{ age : -1 }`.
|
||||
Using `Sort.by(ASC, "age")` overrides the defaults and creates `{ age : 1 }`.
|
||||
`Sort.by
|
||||
(ASC, "firstname")` alters the default and results in `{ age : -1, firstname : 1 }`.
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
Flux<Person> findByFirstnameSortByAgeDesc(String firstname);
|
||||
|
||||
Flux<Person> findByFirstname(String firstname, Sort sort);
|
||||
|
||||
@Query(sort = "{ age : -1 }")
|
||||
Flux<Person> findByFirstname(String firstname);
|
||||
|
||||
@Query(sort = "{ age : -1 }")
|
||||
Flux<Person> findByLastname(String lastname, Sort sort);
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
[[mongodb.repositories.queries.json-spel]]
|
||||
== JSON-based Queries with SpEL Expressions
|
||||
|
||||
Query strings and field definitions can be used together with SpEL expressions to create dynamic queries at runtime.
|
||||
SpEL expressions can provide predicate values and can be used to extend predicates with subdocuments.
|
||||
|
||||
Expressions expose method arguments through an array that contains all the arguments.
|
||||
The following query uses `[0]`
|
||||
to declare the predicate value for `lastname` (which is equivalent to the `?0` parameter binding):
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
@Query("{'lastname': ?#{[0]} }")
|
||||
List<Person> findByQueryWithExpression(String param0);
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
@Query("{'lastname': ?#{[0]} }")
|
||||
Flux<Person> findByQueryWithExpression(String param0);
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
Expressions can be used to invoke functions, evaluate conditionals, and construct values.
|
||||
SpEL expressions used in conjunction with JSON reveal a side-effect, because Map-like declarations inside of SpEL read like JSON, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
@Query("{'id': ?#{ [0] ? {$exists :true} : [1] }}")
|
||||
List<Person> findByQueryWithExpressionAndNestedObject(boolean param0, String param1);
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
@Query("{'id': ?#{ [0] ? {$exists :true} : [1] }}")
|
||||
Flux<Person> findByQueryWithExpressionAndNestedObject(boolean param0, String param1);
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
WARNING: SpEL in query strings can be a powerful way to enhance queries.
|
||||
However, they can also accept a broad range of unwanted arguments.
|
||||
Make sure to sanitize strings before passing them to the query to avoid creation of vulnerabilities or unwanted changes to your query.
|
||||
|
||||
Expression support is extensible through the Query SPI: `EvaluationContextExtension` & `ReactiveEvaluationContextExtension`
|
||||
The Query SPI can contribute properties and functions and can customize the root object.
|
||||
Extensions are retrieved from the application context at the time of SpEL evaluation when the query is built.
|
||||
The following example shows how to use an evaluation context extension:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public class SampleEvaluationContextExtension extends EvaluationContextExtensionSupport {
|
||||
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return "security";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getProperties() {
|
||||
return Collections.singletonMap("principal", SecurityContextHolder.getCurrent().getPrincipal());
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public class SampleEvaluationContextExtension implements ReactiveEvaluationContextExtension {
|
||||
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return "security";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<? extends EvaluationContextExtension> getExtension() {
|
||||
return Mono.just(new EvaluationContextExtensionSupport() { ... });
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
NOTE: Bootstrapping `MongoRepositoryFactory` yourself is not application context-aware and requires further configuration to pick up Query SPI extensions.
|
||||
|
||||
NOTE: Reactive query methods can make use of `org.springframework.data.spel.spi.ReactiveEvaluationContextExtension`.
|
||||
|
||||
[[mongodb.repositories.queries.update]]
|
||||
== Update Methods
|
||||
|
||||
You can also use the keywords in the preceding table to create queries that identify matching documents for running updates on them.
|
||||
The actual update action is defined by the `@Update` annotation on the method itself, as the following listing shows.
|
||||
Note that the naming schema for derived queries starts with `find`.
|
||||
Using `update` (as in `updateAllByLastname(...)`) is allowed only in combination with `@Query`.
|
||||
|
||||
The update is applied to *all* matching documents and it is *not* possible to limit the scope by passing in a `Page` or by using any of the <<repositories.limit-query-result,limiting keywords>>.
|
||||
The return type can be either `void` or a _numeric_ type, such as `long`, to hold the number of modified documents.
|
||||
|
||||
.Update Methods
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface PersonRepository extends CrudRepository<Person, String> {
|
||||
|
||||
@Update("{ '$inc' : { 'visits' : 1 } }")
|
||||
long findAndIncrementVisitsByLastname(String lastname); <1>
|
||||
|
||||
@Update("{ '$inc' : { 'visits' : ?1 } }")
|
||||
void findAndIncrementVisitsByLastname(String lastname, int increment); <2>
|
||||
|
||||
@Update("{ '$inc' : { 'visits' : ?#{[1]} } }")
|
||||
long findAndIncrementVisitsUsingSpELByLastname(String lastname, int increment); <3>
|
||||
|
||||
@Update(pipeline = {"{ '$set' : { 'visits' : { '$add' : [ '$visits', ?1 ] } } }"})
|
||||
void findAndIncrementVisitsViaPipelineByLastname(String lastname, int increment); <4>
|
||||
|
||||
@Update("{ '$push' : { 'shippingAddresses' : ?1 } }")
|
||||
long findAndPushShippingAddressByEmail(String email, Address address); <5>
|
||||
|
||||
@Query("{ 'lastname' : ?0 }")
|
||||
@Update("{ '$inc' : { 'visits' : ?1 } }")
|
||||
void updateAllByLastname(String lastname, int increment); <6>
|
||||
}
|
||||
----
|
||||
|
||||
<1> The filter query for the update is derived from the method name.
|
||||
The update is "`as is`" and does not bind any parameters.
|
||||
<2> The actual increment value is defined by the `increment` method argument that is bound to the `?1` placeholder.
|
||||
<3> Use the Spring Expression Language (SpEL) for parameter binding.
|
||||
<4> Use the `pipeline` attribute to issue xref:mongodb/template-crud-operations.adoc#mongo-template.aggregation-update[aggregation pipeline updates].
|
||||
<5> The update may contain complex objects.
|
||||
<6> Combine a xref:mongodb/repositories/repositories.adoc#mongodb.repositories.queries.json-based[string based query] with an update.
|
||||
====
|
||||
|
||||
WARNING: Repository updates do not emit persistence nor mapping lifecycle events.
|
||||
|
||||
[[mongodb.repositories.queries.delete]]
|
||||
== Delete Methods
|
||||
|
||||
The keywords in the preceding table can be used in conjunction with `delete…By` or `remove…By` to create queries that delete matching documents.
|
||||
|
||||
.`Delete…By` Query
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
List <Person> deleteByLastname(String lastname); <1>
|
||||
|
||||
Long deletePersonByLastname(String lastname); <2>
|
||||
|
||||
@Nullable
|
||||
Person deleteSingleByLastname(String lastname); <3>
|
||||
|
||||
Optional<Person> deleteByBirthdate(Date birthdate); <4>
|
||||
}
|
||||
----
|
||||
<1> Using a return type of `List` retrieves and returns all matching documents before actually deleting them.
|
||||
<2> A numeric return type directly removes the matching documents, returning the total number of documents removed.
|
||||
<3> A single domain type result retrieves and removes the first matching document.
|
||||
<4> Same as in 3 but wrapped in an `Optional` type.
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
Flux<Person> deleteByLastname(String lastname); <1>
|
||||
|
||||
Mono<Long> deletePersonByLastname(String lastname); <2>
|
||||
|
||||
Mono<Person> deleteSingleByLastname(String lastname); <3>
|
||||
}
|
||||
----
|
||||
<1> Using a return type of `Flux` retrieves and returns all matching documents before actually deleting them.
|
||||
<2> A numeric return type directly removes the matching documents, returning the total number of documents removed.
|
||||
<3> A single domain type result retrieves and removes the first matching document.
|
||||
======
|
||||
|
||||
[[mongodb.repositories.queries.aggregation]]
|
||||
== Aggregation Methods
|
||||
|
||||
The repository layer offers means to interact with xref:mongodb/aggregation-framework.adoc[the aggregation framework] via annotated repository query methods.
|
||||
Similar to the xref:mongodb/repositories/repositories.adoc#mongodb.repositories.queries.json-based[JSON based queries], you can define a pipeline using the `org.springframework.data.mongodb.repository.Aggregation` annotation.
|
||||
The definition may contain simple placeholders like `?0` as well as link:{springDocsUrl}/core.html#expressions[SpEL expressions] `?#{ … }`.
|
||||
|
||||
.Aggregating Repository Method
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface PersonRepository extends CrudRepository<Person, String> {
|
||||
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
|
||||
List<PersonAggregate> groupByLastnameAndFirstnames(); <1>
|
||||
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
|
||||
List<PersonAggregate> groupByLastnameAndFirstnames(Sort sort); <2>
|
||||
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : ?0 } } }")
|
||||
List<PersonAggregate> groupByLastnameAnd(String property); <3>
|
||||
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : ?0 } } }")
|
||||
Slice<PersonAggregate> groupByLastnameAnd(String property, Pageable page); <4>
|
||||
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
|
||||
Stream<PersonAggregate> groupByLastnameAndFirstnamesAsStream(); <5>
|
||||
|
||||
@Aggregation("{ $group : { _id : null, total : { $sum : $age } } }")
|
||||
SumValue sumAgeUsingValueWrapper(); <6>
|
||||
|
||||
@Aggregation("{ $group : { _id : null, total : { $sum : $age } } }")
|
||||
Long sumAge(); <7>
|
||||
|
||||
@Aggregation("{ $group : { _id : null, total : { $sum : $age } } }")
|
||||
AggregationResults<SumValue> sumAgeRaw(); <8>
|
||||
|
||||
@Aggregation("{ '$project': { '_id' : '$lastname' } }")
|
||||
List<String> findAllLastnames(); <9>
|
||||
|
||||
@Aggregation(pipeline = {
|
||||
"{ $group : { _id : '$author', books: { $push: '$title' } } }",
|
||||
"{ $out : 'authors' }"
|
||||
})
|
||||
void groupAndOutSkippingOutput(); <10>
|
||||
}
|
||||
----
|
||||
[source,java]
|
||||
----
|
||||
public class PersonAggregate {
|
||||
|
||||
private @Id String lastname; <2>
|
||||
private List<String> names;
|
||||
|
||||
public PersonAggregate(String lastname, List<String> names) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Getter / Setter omitted
|
||||
}
|
||||
|
||||
public class SumValue {
|
||||
|
||||
private final Long total; <6> <8>
|
||||
|
||||
public SumValue(Long total) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Getter omitted
|
||||
}
|
||||
----
|
||||
<1> Aggregation pipeline to group first names by `lastname` in the `Person` collection returning these as `PersonAggregate`.
|
||||
<2> If `Sort` argument is present, `$sort` is appended after the declared pipeline stages so that it only affects the order of the final results after having passed all other aggregation stages.
|
||||
Therefore, the `Sort` properties are mapped against the methods return type `PersonAggregate` which turns `Sort.by("lastname")` into `{ $sort : { '_id', 1 } }` because `PersonAggregate.lastname` is annotated with `@Id`.
|
||||
<3> Replaces `?0` with the given value for `property` for a dynamic aggregation pipeline.
|
||||
<4> `$skip`, `$limit` and `$sort` can be passed on via a `Pageable` argument. Same as in <2>, the operators are appended to the pipeline definition. Methods accepting `Pageable` can return `Slice` for easier pagination.
|
||||
<5> Aggregation methods can return `Stream` to consume results directly from an underlying cursor. Make sure to close the stream after consuming it to release the server-side cursor by either calling `close()` or through `try-with-resources`.
|
||||
<6> Map the result of an aggregation returning a single `Document` to an instance of a desired `SumValue` target type.
|
||||
<7> Aggregations resulting in single document holding just an accumulation result like e.g. `$sum` can be extracted directly from the result `Document`.
|
||||
To gain more control, you might consider `AggregationResult` as method return type as shown in <7>.
|
||||
<8> Obtain the raw `AggregationResults` mapped to the generic target wrapper type `SumValue` or `org.bson.Document`.
|
||||
<9> Like in <6>, a single value can be directly obtained from multiple result ``Document``s.
|
||||
<10> Skips the output of the `$out` stage when return type is `void`.
|
||||
====
|
||||
|
||||
In some scenarios, aggregations might require additional options, such as a maximum run time, additional log comments, or the permission to temporarily write data to disk.
|
||||
Use the `@Meta` annotation to set those options via `maxExecutionTimeMs`, `comment` or `allowDiskUse`.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
interface PersonRepository extends CrudRepository<Person, String> {
|
||||
|
||||
@Meta(allowDiskUse = true)
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
|
||||
List<PersonAggregate> groupByLastnameAndFirstnames();
|
||||
}
|
||||
----
|
||||
|
||||
Or use `@Meta` to create your own annotation as shown in the sample below.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD })
|
||||
@Meta(allowDiskUse = true)
|
||||
@interface AllowDiskUse { }
|
||||
|
||||
interface PersonRepository extends CrudRepository<Person, String> {
|
||||
|
||||
@AllowDiskUse
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
|
||||
List<PersonAggregate> groupByLastnameAndFirstnames();
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Simple-type single-result inspects the returned `Document` and checks for the following:
|
||||
|
||||
. Only one entry in the document, return it.
|
||||
. Two entries, one is the `_id` value. Return the other.
|
||||
. Return for the first value assignable to the return type.
|
||||
. Throw an exception if none of the above is applicable.
|
||||
====
|
||||
|
||||
WARNING: The `Page` return type is not supported for repository methods using `@Aggregation`. However, you can use a
|
||||
`Pageable` argument to add `$skip`, `$limit` and `$sort` to the pipeline and let the method return `Slice`.
|
||||
|
||||
[[mongodb.repositories.index-hint]]
|
||||
== Index Hints
|
||||
|
||||
The `@Hint` annotation allows to override MongoDB's default index selection and forces the database to use the specified index instead.
|
||||
|
||||
.Example of index hints
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Hint("lastname-idx") <1>
|
||||
List<Person> findByLastname(String lastname);
|
||||
|
||||
@Query(value = "{ 'firstname' : ?0 }", hint = "firstname-idx") <2>
|
||||
List<Person> findByFirstname(String firstname);
|
||||
----
|
||||
<1> Use the index with name `lastname-idx`.
|
||||
<2> The `@Query` annotation defines the `hint` alias which is equivalent to adding the `@Hint` annotation.
|
||||
====
|
||||
|
||||
For more information about index creation please refer to the xref:mongodb/template-collection-management.adoc[Collection Management] section.
|
||||
|
||||
[[mongo.repositories.collation]]
|
||||
== Repository Collation Support
|
||||
|
||||
Next to the xref:mongodb/collation.adoc[general Collation Support] repositories allow to define the collation for various operations.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
@Query(collation = "en_US") <1>
|
||||
List<Person> findByFirstname(String firstname);
|
||||
|
||||
@Query(collation = "{ 'locale' : 'en_US' }") <2>
|
||||
List<Person> findPersonByFirstname(String firstname);
|
||||
|
||||
@Query(collation = "?1") <3>
|
||||
List<Person> findByFirstname(String firstname, Object collation);
|
||||
|
||||
@Query(collation = "{ 'locale' : '?1' }") <4>
|
||||
List<Person> findByFirstname(String firstname, String collation);
|
||||
|
||||
List<Person> findByFirstname(String firstname, Collation collation); <5>
|
||||
|
||||
@Query(collation = "{ 'locale' : 'en_US' }")
|
||||
List<Person> findByFirstname(String firstname, @Nullable Collation collation); <6>
|
||||
}
|
||||
----
|
||||
<1> Static collation definition resulting in `{ 'locale' : 'en_US' }`.
|
||||
<2> Static collation definition resulting in `{ 'locale' : 'en_US' }`.
|
||||
<3> Dynamic collation depending on 2nd method argument. Allowed types include `String` (eg. 'en_US'), `Locacle` (eg. Locacle.US)
|
||||
and `Document` (eg. new Document("locale", "en_US"))
|
||||
<4> Dynamic collation depending on 2nd method argument.
|
||||
<5> Apply the `Collation` method parameter to the query.
|
||||
<6> The `Collation` method parameter overrides the default `collation` from `@Query` if not null.
|
||||
|
||||
NOTE: In case you enabled the automatic index creation for repository finder methods a potential static collation definition,
|
||||
as shown in (1) and (2), will be included when creating the index.
|
||||
|
||||
TIP: The most specifc `Collation` outrules potentially defined others. Which means Method argument over query method annotation over domain type annotation.
|
||||
====
|
||||
|
||||
To streamline usage of collation attributes throughout the codebase it is also possible to use the `@Collation` annotation, which serves as a meta annotation for the ones mentioned above.
|
||||
The same rules and locations apply, plus, direct usage of `@Collation` supersedes any collation values defined on `@Query` and other annotations.
|
||||
Which means, if a collation is declared via `@Query` and additionally via `@Collation`, then the one from `@Collation` is picked.
|
||||
|
||||
.Using `@Collation`
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Collation("en_US") <1>
|
||||
class Game {
|
||||
// ...
|
||||
}
|
||||
|
||||
interface GameRepository extends Repository<Game, String> {
|
||||
|
||||
@Collation("en_GB") <2>
|
||||
List<Game> findByTitle(String title);
|
||||
|
||||
@Collation("de_AT") <3>
|
||||
@Query(collation="en_GB")
|
||||
List<Game> findByDescriptionContaining(String keyword);
|
||||
}
|
||||
----
|
||||
<1> Instead of `@Document(collation=...)`.
|
||||
<2> Instead of `@Query(collation=...)`.
|
||||
<3> Favors `@Collation` over meta usage.
|
||||
====
|
||||
@@ -1,69 +0,0 @@
|
||||
[[mongo.repositories.collation]]
|
||||
= Repository Collation Support
|
||||
|
||||
Next to the xref:mongodb/collation.adoc[general Collation Support] repositories allow to define the collation for various operations.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
@Query(collation = "en_US") <1>
|
||||
List<Person> findByFirstname(String firstname);
|
||||
|
||||
@Query(collation = "{ 'locale' : 'en_US' }") <2>
|
||||
List<Person> findPersonByFirstname(String firstname);
|
||||
|
||||
@Query(collation = "?1") <3>
|
||||
List<Person> findByFirstname(String firstname, Object collation);
|
||||
|
||||
@Query(collation = "{ 'locale' : '?1' }") <4>
|
||||
List<Person> findByFirstname(String firstname, String collation);
|
||||
|
||||
List<Person> findByFirstname(String firstname, Collation collation); <5>
|
||||
|
||||
@Query(collation = "{ 'locale' : 'en_US' }")
|
||||
List<Person> findByFirstname(String firstname, @Nullable Collation collation); <6>
|
||||
}
|
||||
----
|
||||
<1> Static collation definition resulting in `{ 'locale' : 'en_US' }`.
|
||||
<2> Static collation definition resulting in `{ 'locale' : 'en_US' }`.
|
||||
<3> Dynamic collation depending on 2nd method argument. Allowed types include `String` (eg. 'en_US'), `Locacle` (eg. Locacle.US)
|
||||
and `Document` (eg. new Document("locale", "en_US"))
|
||||
<4> Dynamic collation depending on 2nd method argument.
|
||||
<5> Apply the `Collation` method parameter to the query.
|
||||
<6> The `Collation` method parameter overrides the default `collation` from `@Query` if not null.
|
||||
|
||||
NOTE: In case you enabled the automatic index creation for repository finder methods a potential static collation definition,
|
||||
as shown in (1) and (2), will be included when creating the index.
|
||||
|
||||
TIP: The most specifc `Collation` outrules potentially defined others. Which means Method argument over query method annotation over domain type annotation.
|
||||
====
|
||||
|
||||
To streamline usage of collation attributes throughout the codebase it is also possible to use the `@Collation` annotation, which serves as a meta annotation for the ones mentioned above.
|
||||
The same rules and locations apply, plus, direct usage of `@Collation` supersedes any collation values defined on `@Query` and other annotations.
|
||||
Which means, if a collation is declared via `@Query` and additionally via `@Collation`, then the one from `@Collation` is picked.
|
||||
|
||||
.Using `@Collation`
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Collation("en_US") <1>
|
||||
class Game {
|
||||
// ...
|
||||
}
|
||||
|
||||
interface GameRepository extends Repository<Game, String> {
|
||||
|
||||
@Collation("en_GB") <2>
|
||||
List<Game> findByTitle(String title);
|
||||
|
||||
@Collation("de_AT") <3>
|
||||
@Query(collation="en_GB")
|
||||
List<Game> findByDescriptionContaining(String keyword);
|
||||
}
|
||||
----
|
||||
<1> Instead of `@Document(collation=...)`.
|
||||
<2> Instead of `@Query(collation=...)`.
|
||||
<3> Favors `@Collation` over meta usage.
|
||||
====
|
||||
@@ -1,20 +0,0 @@
|
||||
[[mongodb.repositories.index-hint]]
|
||||
= Index Hints
|
||||
|
||||
The `@Hint` annotation allows to override MongoDB's default index selection and forces the database to use the specified index instead.
|
||||
|
||||
.Example of index hints
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Hint("lastname-idx") <1>
|
||||
List<Person> findByLastname(String lastname);
|
||||
|
||||
@Query(value = "{ 'firstname' : ?0 }", hint = "firstname-idx") <2>
|
||||
List<Person> findByFirstname(String firstname);
|
||||
----
|
||||
<1> Use the index with name `lastname-idx`.
|
||||
<2> The `@Query` annotation defines the `hint` alias which is equivalent to adding the `@Hint` annotation.
|
||||
====
|
||||
|
||||
For more information about index creation please refer to the xref:mongodb/template-collection-management.adoc[Collection Management] section.
|
||||
@@ -31,7 +31,7 @@ public class Person {
|
||||
|
||||
Note that the domain type shown in the preceding example has a property named `id` of type `String`.The default serialization mechanism used in `MongoTemplate` (which backs the repository support) regards properties named `id` as the document ID.
|
||||
Currently, we support `String`, `ObjectId`, and `BigInteger` as ID types.
|
||||
Please see xref:mongodb/template-id-handling.adoc#mongo-template.id-handling[ID mapping] for more information about on how the `id` field is handled in the mapping layer.
|
||||
Please see xref:mongodb/template-crud-operations.adoc#mongo-template.id-handling[ID mapping] for more information about on how the `id` field is handled in the mapping layer.
|
||||
|
||||
Now that we have a domain object, we can define an interface that uses it, as follows:
|
||||
|
||||
@@ -212,568 +212,8 @@ The preceding example creates an application context with Spring's unit test sup
|
||||
Inside the test method, we use the repository to query the datastore.
|
||||
We hand the repository a `PageRequest` instance that requests the first page of `Person` objects at a page size of 10.
|
||||
|
||||
[[mongodb.repositories.queries]]
|
||||
== Query Methods
|
||||
|
||||
Most of the data access operations you usually trigger on a repository result in a query being executed against the MongoDB databases.
|
||||
Defining such a query is a matter of declaring a method on the repository interface, as the following example shows:
|
||||
|
||||
.PersonRepository with query methods
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends PagingAndSortingRepository<Person, String> {
|
||||
|
||||
List<Person> findByLastname(String lastname); <1>
|
||||
|
||||
Page<Person> findByFirstname(String firstname, Pageable pageable); <2>
|
||||
|
||||
Person findByShippingAddresses(Address address); <3>
|
||||
|
||||
Person findFirstByLastname(String lastname); <4>
|
||||
|
||||
Stream<Person> findAllBy(); <5>
|
||||
}
|
||||
----
|
||||
<1> The `findByLastname` method shows a query for all people with the given last name.
|
||||
The query is derived by parsing the method name for constraints that can be concatenated with `And` and `Or`.
|
||||
Thus, the method name results in a query expression of `{"lastname" : lastname}`.
|
||||
<2> Applies pagination to a query.
|
||||
You can equip your method signature with a `Pageable` parameter and let the method return a `Page` instance and Spring Data automatically pages the query accordingly.
|
||||
<3> Shows that you can query based on properties that are not primitive types.
|
||||
Throws `IncorrectResultSizeDataAccessException` if more than one match is found.
|
||||
<4> Uses the `First` keyword to restrict the query to only the first result.
|
||||
Unlike <3>, this method does not throw an exception if more than one match is found.
|
||||
<5> Uses a Java 8 `Stream` that reads and converts individual elements while iterating the stream.
|
||||
|
||||
Reactive::
|
||||
+
|
||||
====
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface ReactivePersonRepository extends ReactiveSortingRepository<Person, String> {
|
||||
|
||||
Flux<Person> findByFirstname(String firstname); <1>
|
||||
|
||||
Flux<Person> findByFirstname(Publisher<String> firstname); <2>
|
||||
|
||||
Flux<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable); <3>
|
||||
|
||||
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname); <4>
|
||||
|
||||
Mono<Person> findFirstByLastname(String lastname); <5>
|
||||
}
|
||||
----
|
||||
<1> The method shows a query for all people with the given `lastname`. The query is derived by parsing the method name for constraints that can be concatenated with `And` and `Or`. Thus, the method name results in a query expression of `{"lastname" : lastname}`.
|
||||
<2> The method shows a query for all people with the given `firstname` once the `firstname` is emitted by the given `Publisher`.
|
||||
<3> Use `Pageable` to pass offset and sorting parameters to the database.
|
||||
<4> Find a single entity for the given criteria. It completes with `IncorrectResultSizeDataAccessException` on non-unique results.
|
||||
<5> Unless <4>, the first entity is always emitted even if the query yields more result documents.
|
||||
|
||||
WARNING: The `Page` return type (as in `Mono<Page>`) is not supported by reactive repositories.
|
||||
|
||||
It is possible to use `Pageable` in derived finder methods, to pass on `sort`, `limit` and `offset` parameters to the query to reduce load and network traffic.
|
||||
The returned `Flux` will only emit data within the declared range.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
Pageable page = PageRequest.of(1, 10, Sort.by("lastname"));
|
||||
Flux<Person> persons = repository.findByFirstnameOrderByLastname("luke", page);
|
||||
----
|
||||
====
|
||||
======
|
||||
|
||||
NOTE: We do not support referring to parameters that are mapped as `DBRef` in the domain class.
|
||||
|
||||
The following table shows the keywords that are supported for query methods:
|
||||
|
||||
[cols="1,2,3",options="header"]
|
||||
.Supported keywords for query methods
|
||||
|===
|
||||
| Keyword
|
||||
| Sample
|
||||
| Logical result
|
||||
|
||||
| `After`
|
||||
| `findByBirthdateAfter(Date date)`
|
||||
| `{"birthdate" : {"$gt" : date}}`
|
||||
|
||||
| `GreaterThan`
|
||||
| `findByAgeGreaterThan(int age)`
|
||||
| `{"age" : {"$gt" : age}}`
|
||||
|
||||
| `GreaterThanEqual`
|
||||
| `findByAgeGreaterThanEqual(int age)`
|
||||
| `{"age" : {"$gte" : age}}`
|
||||
|
||||
| `Before`
|
||||
| `findByBirthdateBefore(Date date)`
|
||||
| `{"birthdate" : {"$lt" : date}}`
|
||||
|
||||
| `LessThan`
|
||||
| `findByAgeLessThan(int age)`
|
||||
| `{"age" : {"$lt" : age}}`
|
||||
|
||||
| `LessThanEqual`
|
||||
| `findByAgeLessThanEqual(int age)`
|
||||
| `{"age" : {"$lte" : age}}`
|
||||
|
||||
| `Between`
|
||||
| `findByAgeBetween(int from, int to)` +
|
||||
`findByAgeBetween(Range<Integer> range)`
|
||||
| `{"age" : {"$gt" : from, "$lt" : to}}` +
|
||||
lower / upper bounds (`$gt` / `$gte` & `$lt` / `$lte`) according to `Range`
|
||||
|
||||
| `In`
|
||||
| `findByAgeIn(Collection ages)`
|
||||
| `{"age" : {"$in" : [ages...]}}`
|
||||
|
||||
| `NotIn`
|
||||
| `findByAgeNotIn(Collection ages)`
|
||||
| `{"age" : {"$nin" : [ages...]}}`
|
||||
|
||||
| `IsNotNull`, `NotNull`
|
||||
| `findByFirstnameNotNull()`
|
||||
| `{"firstname" : {"$ne" : null}}`
|
||||
|
||||
| `IsNull`, `Null`
|
||||
| `findByFirstnameNull()`
|
||||
| `{"firstname" : null}`
|
||||
|
||||
| `Like`, `StartingWith`, `EndingWith`
|
||||
| `findByFirstnameLike(String name)`
|
||||
| `{"firstname" : name} (name as regex)`
|
||||
|
||||
| `NotLike`, `IsNotLike`
|
||||
| `findByFirstnameNotLike(String name)`
|
||||
| `{"firstname" : { "$not" : name }} (name as regex)`
|
||||
|
||||
| `Containing` on String
|
||||
| `findByFirstnameContaining(String name)`
|
||||
| `{"firstname" : name} (name as regex)`
|
||||
|
||||
| `NotContaining` on String
|
||||
| `findByFirstnameNotContaining(String name)`
|
||||
| `{"firstname" : { "$not" : name}} (name as regex)`
|
||||
|
||||
| `Containing` on Collection
|
||||
| `findByAddressesContaining(Address address)`
|
||||
| `{"addresses" : { "$in" : address}}`
|
||||
|
||||
| `NotContaining` on Collection
|
||||
| `findByAddressesNotContaining(Address address)`
|
||||
| `{"addresses" : { "$not" : { "$in" : address}}}`
|
||||
|
||||
| `Regex`
|
||||
| `findByFirstnameRegex(String firstname)`
|
||||
| `{"firstname" : {"$regex" : firstname }}`
|
||||
|
||||
| `(No keyword)`
|
||||
| `findByFirstname(String name)`
|
||||
| `{"firstname" : name}`
|
||||
|
||||
| `Not`
|
||||
| `findByFirstnameNot(String name)`
|
||||
| `{"firstname" : {"$ne" : name}}`
|
||||
|
||||
| `Near`
|
||||
| `findByLocationNear(Point point)`
|
||||
| `{"location" : {"$near" : [x,y]}}`
|
||||
|
||||
| `Near`
|
||||
| `findByLocationNear(Point point, Distance max)`
|
||||
| `{"location" : {"$near" : [x,y], "$maxDistance" : max}}`
|
||||
|
||||
| `Near`
|
||||
| `findByLocationNear(Point point, Distance min, Distance max)`
|
||||
| `{"location" : {"$near" : [x,y], "$minDistance" : min, "$maxDistance" : max}}`
|
||||
|
||||
| `Within`
|
||||
| `findByLocationWithin(Circle circle)`
|
||||
| `{"location" : {"$geoWithin" : {"$center" : [ [x, y], distance]}}}`
|
||||
|
||||
| `Within`
|
||||
| `findByLocationWithin(Box box)`
|
||||
| `{"location" : {"$geoWithin" : {"$box" : [ [x1, y1], x2, y2]}}}`
|
||||
|
||||
| `IsTrue`, `True`
|
||||
| `findByActiveIsTrue()`
|
||||
| `{"active" : true}`
|
||||
|
||||
| `IsFalse`, `False`
|
||||
| `findByActiveIsFalse()`
|
||||
| `{"active" : false}`
|
||||
|
||||
| `Exists`
|
||||
| `findByLocationExists(boolean exists)`
|
||||
| `{"location" : {"$exists" : exists }}`
|
||||
|
||||
| `IgnoreCase`
|
||||
| `findByUsernameIgnoreCase(String username)`
|
||||
| `{"username" : {"$regex" : "^username$", "$options" : "i" }}`
|
||||
|===
|
||||
|
||||
NOTE: If the property criterion compares a document, the order of the fields and exact equality in the document matters.
|
||||
|
||||
[[mongodb.repositories.queries.geo-spatial]]
|
||||
=== Geo-spatial Repository Queries
|
||||
|
||||
As you saw in the preceding table of keywords, a few keywords trigger geo-spatial operations within a MongoDB query.
|
||||
The `Near` keyword allows some further modification, as the next few examples show.
|
||||
|
||||
The following example shows how to define a `near` query that finds all persons with a given distance of a given point:
|
||||
|
||||
.Advanced `Near` queries
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
// { 'location' : { '$near' : [point.x, point.y], '$maxDistance' : distance}}
|
||||
List<Person> findByLocationNear(Point location, Distance distance);
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
// { 'location' : { '$near' : [point.x, point.y], '$maxDistance' : distance}}
|
||||
Flux<Person> findByLocationNear(Point location, Distance distance);
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
Adding a `Distance` parameter to the query method allows restricting results to those within the given distance.
|
||||
If the `Distance` was set up containing a `Metric`, we transparently use `$nearSphere` instead of `$code`, as the following example shows:
|
||||
|
||||
.Using `Distance` with `Metrics`
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Point point = new Point(43.7, 48.8);
|
||||
Distance distance = new Distance(200, Metrics.KILOMETERS);
|
||||
… = repository.findByLocationNear(point, distance);
|
||||
// {'location' : {'$nearSphere' : [43.7, 48.8], '$maxDistance' : 0.03135711885774796}}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Reactive Geo-spatial repository queries support the domain type and `GeoResult<T>` results within a reactive wrapper type. `GeoPage` and `GeoResults` are not supported as they contradict the deferred result approach with pre-calculating the average distance. However, you can still pass in a `Pageable` argument to page results yourself.
|
||||
|
||||
Using a `Distance` with a `Metric` causes a `$nearSphere` (instead of a plain `$near`) clause to be added.
|
||||
Beyond that, the actual distance gets calculated according to the `Metrics` used.
|
||||
|
||||
(Note that `Metric` does not refer to metric units of measure.
|
||||
It could be miles rather than kilometers.
|
||||
Rather, `metric` refers to the concept of a system of measurement, regardless of which system you use.)
|
||||
|
||||
NOTE: Using `@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)` on the target property forces usage of the `$nearSphere` operator.
|
||||
|
||||
[[geo-near-queries]]
|
||||
==== Geo-near Queries
|
||||
|
||||
Spring Data MongoDb supports geo-near queries, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
// {'geoNear' : 'location', 'near' : [x, y] }
|
||||
GeoResults<Person> findByLocationNear(Point location);
|
||||
|
||||
// No metric: {'geoNear' : 'person', 'near' : [x, y], maxDistance : distance }
|
||||
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'maxDistance' : distance,
|
||||
// 'distanceMultiplier' : metric.multiplier, 'spherical' : true }
|
||||
GeoResults<Person> findByLocationNear(Point location, Distance distance);
|
||||
|
||||
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'minDistance' : min,
|
||||
// 'maxDistance' : max, 'distanceMultiplier' : metric.multiplier,
|
||||
// 'spherical' : true }
|
||||
GeoResults<Person> findByLocationNear(Point location, Distance min, Distance max);
|
||||
|
||||
// {'geoNear' : 'location', 'near' : [x, y] }
|
||||
GeoResults<Person> findByLocationNear(Point location);
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
// {'geoNear' : 'location', 'near' : [x, y] }
|
||||
Flux<GeoResult<Person>> findByLocationNear(Point location);
|
||||
|
||||
// No metric: {'geoNear' : 'person', 'near' : [x, y], maxDistance : distance }
|
||||
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'maxDistance' : distance,
|
||||
// 'distanceMultiplier' : metric.multiplier, 'spherical' : true }
|
||||
Flux<GeoResult<Person>> findByLocationNear(Point location, Distance distance);
|
||||
|
||||
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'minDistance' : min,
|
||||
// 'maxDistance' : max, 'distanceMultiplier' : metric.multiplier,
|
||||
// 'spherical' : true }
|
||||
Flux<GeoResult<Person>> findByLocationNear(Point location, Distance min, Distance max);
|
||||
|
||||
// {'geoNear' : 'location', 'near' : [x, y] }
|
||||
Flux<GeoResult<Person>> findByLocationNear(Point location);
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
[[mongodb.repositories.queries.json-based]]
|
||||
=== MongoDB JSON-based Query Methods and Field Restriction
|
||||
|
||||
By adding the `org.springframework.data.mongodb.repository.Query` annotation to your repository query methods, you can specify a MongoDB JSON query string to use instead of having the query be derived from the method name, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
@Query("{ 'firstname' : ?0 }")
|
||||
List<Person> findByThePersonsFirstname(String firstname);
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
@Query("{ 'firstname' : ?0 }")
|
||||
Flux<Person> findByThePersonsFirstname(String firstname);
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
The `?0` placeholder lets you substitute the value from the method arguments into the JSON query string.
|
||||
|
||||
NOTE: `String` parameter values are escaped during the binding process, which means that it is not possible to add MongoDB specific operators through the argument.
|
||||
|
||||
You can also use the filter property to restrict the set of properties that is mapped into the Java object, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
@Query(value="{ 'firstname' : ?0 }", fields="{ 'firstname' : 1, 'lastname' : 1}")
|
||||
List<Person> findByThePersonsFirstname(String firstname);
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
@Query(value="{ 'firstname' : ?0 }", fields="{ 'firstname' : 1, 'lastname' : 1}")
|
||||
Flux<Person> findByThePersonsFirstname(String firstname);
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
The query in the preceding example returns only the `firstname`, `lastname` and `Id` properties of the `Person` objects.
|
||||
The `age` property, a `java.lang.Integer`, is not set and its value is therefore null.
|
||||
|
||||
[[mongodb.repositories.queries.sort]]
|
||||
=== Sorting Query Method results
|
||||
|
||||
MongoDB repositories allow various approaches to define sorting order.
|
||||
Let's take a look at the following example:
|
||||
|
||||
.Sorting Query Results
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
List<Person> findByFirstnameSortByAgeDesc(String firstname); <1>
|
||||
|
||||
List<Person> findByFirstname(String firstname, Sort sort); <2>
|
||||
|
||||
@Query(sort = "{ age : -1 }")
|
||||
List<Person> findByFirstname(String firstname); <3>
|
||||
|
||||
@Query(sort = "{ age : -1 }")
|
||||
List<Person> findByLastname(String lastname, Sort sort); <4>
|
||||
}
|
||||
----
|
||||
<1> Static sorting derived from method name. `SortByAgeDesc` results in `{ age : -1 }` for the sort parameter.
|
||||
<2> Dynamic sorting using a method argument.
|
||||
`Sort.by(DESC, "age")` creates `{ age : -1 }` for the sort parameter.
|
||||
<3> Static sorting via `Query` annotation.
|
||||
Sort parameter applied as stated in the `sort` attribute.
|
||||
<4> Default sorting via `Query` annotation combined with dynamic one via a method argument. `Sort.unsorted()`
|
||||
results in `{ age : -1 }`.
|
||||
Using `Sort.by(ASC, "age")` overrides the defaults and creates `{ age : 1 }`.
|
||||
`Sort.by
|
||||
(ASC, "firstname")` alters the default and results in `{ age : -1, firstname : 1 }`.
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
Flux<Person> findByFirstnameSortByAgeDesc(String firstname);
|
||||
|
||||
Flux<Person> findByFirstname(String firstname, Sort sort);
|
||||
|
||||
@Query(sort = "{ age : -1 }")
|
||||
Flux<Person> findByFirstname(String firstname);
|
||||
|
||||
@Query(sort = "{ age : -1 }")
|
||||
Flux<Person> findByLastname(String lastname, Sort sort);
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
[[mongodb.repositories.queries.json-spel]]
|
||||
=== JSON-based Queries with SpEL Expressions
|
||||
|
||||
Query strings and field definitions can be used together with SpEL expressions to create dynamic queries at runtime.
|
||||
SpEL expressions can provide predicate values and can be used to extend predicates with subdocuments.
|
||||
|
||||
Expressions expose method arguments through an array that contains all the arguments.
|
||||
The following query uses `[0]`
|
||||
to declare the predicate value for `lastname` (which is equivalent to the `?0` parameter binding):
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
@Query("{'lastname': ?#{[0]} }")
|
||||
List<Person> findByQueryWithExpression(String param0);
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
@Query("{'lastname': ?#{[0]} }")
|
||||
Flux<Person> findByQueryWithExpression(String param0);
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
Expressions can be used to invoke functions, evaluate conditionals, and construct values.
|
||||
SpEL expressions used in conjunction with JSON reveal a side-effect, because Map-like declarations inside of SpEL read like JSON, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
@Query("{'id': ?#{ [0] ? {$exists :true} : [1] }}")
|
||||
List<Person> findByQueryWithExpressionAndNestedObject(boolean param0, String param1);
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
@Query("{'id': ?#{ [0] ? {$exists :true} : [1] }}")
|
||||
Flux<Person> findByQueryWithExpressionAndNestedObject(boolean param0, String param1);
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
WARNING: SpEL in query strings can be a powerful way to enhance queries.
|
||||
However, they can also accept a broad range of unwanted arguments.
|
||||
Make sure to sanitize strings before passing them to the query to avoid creation of vulnerabilities or unwanted changes to your query.
|
||||
|
||||
Expression support is extensible through the Query SPI: `EvaluationContextExtension` & `ReactiveEvaluationContextExtension`
|
||||
The Query SPI can contribute properties and functions and can customize the root object.
|
||||
Extensions are retrieved from the application context at the time of SpEL evaluation when the query is built.
|
||||
The following example shows how to use an evaluation context extension:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public class SampleEvaluationContextExtension extends EvaluationContextExtensionSupport {
|
||||
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return "security";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getProperties() {
|
||||
return Collections.singletonMap("principal", SecurityContextHolder.getCurrent().getPrincipal());
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public class SampleEvaluationContextExtension implements ReactiveEvaluationContextExtension {
|
||||
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return "security";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<? extends EvaluationContextExtension> getExtension() {
|
||||
return Mono.just(new EvaluationContextExtensionSupport() { ... });
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
NOTE: Bootstrapping `MongoRepositoryFactory` yourself is not application context-aware and requires further configuration to pick up Query SPI extensions.
|
||||
|
||||
NOTE: Reactive query methods can make use of `org.springframework.data.spel.spi.ReactiveEvaluationContextExtension`.
|
||||
|
||||
[[mongodb.repositories.queries.type-safe]]
|
||||
=== Type-safe Query Methods
|
||||
== Type-safe Query Methods
|
||||
|
||||
MongoDB repository and its reactive counterpart integrates with the http://www.querydsl.com/[Querydsl] project, which provides a way to perform type-safe queries.
|
||||
|
||||
@@ -901,7 +341,7 @@ NOTE: Please note that joins (DBRef's) are not supported with Reactive MongoDB s
|
||||
======
|
||||
|
||||
[[mongodb.repositories.queries.full-text]]
|
||||
=== Full-text Search Queries
|
||||
== Full-text Search Queries
|
||||
|
||||
MongoDB's full-text search feature is store-specific and, therefore, can be found on `MongoRepository` rather than on the more general `CrudRepository`.
|
||||
We need a document with a full-text index (see "`xref:mongodb/mapping/mapping.adoc#mapping-usage-indexes.text-index[Text Indexes]`" to learn how to create a full-text index).
|
||||
@@ -944,230 +384,3 @@ criteria = TextCriteria.forDefaultLanguage().matching("film");
|
||||
Page<FullTextDocument> page = repository.findAllBy(criteria, PageRequest.of(1, 1, sort));
|
||||
List<FullTextDocument> result = repository.findByTitleOrderByScoreDesc("mongodb", criteria);
|
||||
----
|
||||
|
||||
|
||||
[[mongodb.repositories.queries.update]]
|
||||
=== Repository Update Methods
|
||||
|
||||
You can also use the keywords in the preceding table to create queries that identify matching documents for running updates on them.
|
||||
The actual update action is defined by the `@Update` annotation on the method itself, as the following listing shows.
|
||||
Note that the naming schema for derived queries starts with `find`.
|
||||
Using `update` (as in `updateAllByLastname(...)`) is allowed only in combination with `@Query`.
|
||||
|
||||
The update is applied to *all* matching documents and it is *not* possible to limit the scope by passing in a `Page` or by using any of the <<repositories.limit-query-result,limiting keywords>>.
|
||||
The return type can be either `void` or a _numeric_ type, such as `long`, to hold the number of modified documents.
|
||||
|
||||
.Update Methods
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface PersonRepository extends CrudRepository<Person, String> {
|
||||
|
||||
@Update("{ '$inc' : { 'visits' : 1 } }")
|
||||
long findAndIncrementVisitsByLastname(String lastname); <1>
|
||||
|
||||
@Update("{ '$inc' : { 'visits' : ?1 } }")
|
||||
void findAndIncrementVisitsByLastname(String lastname, int increment); <2>
|
||||
|
||||
@Update("{ '$inc' : { 'visits' : ?#{[1]} } }")
|
||||
long findAndIncrementVisitsUsingSpELByLastname(String lastname, int increment); <3>
|
||||
|
||||
@Update(pipeline = {"{ '$set' : { 'visits' : { '$add' : [ '$visits', ?1 ] } } }"})
|
||||
void findAndIncrementVisitsViaPipelineByLastname(String lastname, int increment); <4>
|
||||
|
||||
@Update("{ '$push' : { 'shippingAddresses' : ?1 } }")
|
||||
long findAndPushShippingAddressByEmail(String email, Address address); <5>
|
||||
|
||||
@Query("{ 'lastname' : ?0 }")
|
||||
@Update("{ '$inc' : { 'visits' : ?1 } }")
|
||||
void updateAllByLastname(String lastname, int increment); <6>
|
||||
}
|
||||
----
|
||||
|
||||
<1> The filter query for the update is derived from the method name.
|
||||
The update is "`as is`" and does not bind any parameters.
|
||||
<2> The actual increment value is defined by the `increment` method argument that is bound to the `?1` placeholder.
|
||||
<3> Use the Spring Expression Language (SpEL) for parameter binding.
|
||||
<4> Use the `pipeline` attribute to issue xref:mongodb/template-crud-operations.adoc#mongo-template.aggregation-update[aggregation pipeline updates].
|
||||
<5> The update may contain complex objects.
|
||||
<6> Combine a xref:mongodb/repositories/repositories.adoc#mongodb.repositories.queries.json-based[string based query] with an update.
|
||||
====
|
||||
|
||||
WARNING: Repository updates do not emit persistence nor mapping lifecycle events.
|
||||
|
||||
[[mongodb.repositories.queries.delete]]
|
||||
=== Repository Delete Queries
|
||||
|
||||
The keywords in the preceding table can be used in conjunction with `delete…By` or `remove…By` to create queries that delete matching documents.
|
||||
|
||||
.`Delete…By` Query
|
||||
[tabs]
|
||||
======
|
||||
Imperative::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public interface PersonRepository extends MongoRepository<Person, String> {
|
||||
|
||||
List <Person> deleteByLastname(String lastname); <1>
|
||||
|
||||
Long deletePersonByLastname(String lastname); <2>
|
||||
|
||||
@Nullable
|
||||
Person deleteSingleByLastname(String lastname); <3>
|
||||
|
||||
Optional<Person> deleteByBirthdate(Date birthdate); <4>
|
||||
}
|
||||
----
|
||||
<1> Using a return type of `List` retrieves and returns all matching documents before actually deleting them.
|
||||
<2> A numeric return type directly removes the matching documents, returning the total number of documents removed.
|
||||
<3> A single domain type result retrieves and removes the first matching document.
|
||||
<4> Same as in 3 but wrapped in an `Optional` type.
|
||||
|
||||
Reactive::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
|
||||
Flux<Person> deleteByLastname(String lastname); <1>
|
||||
|
||||
Mono<Long> deletePersonByLastname(String lastname); <2>
|
||||
|
||||
Mono<Person> deleteSingleByLastname(String lastname); <3>
|
||||
}
|
||||
----
|
||||
<1> Using a return type of `Flux` retrieves and returns all matching documents before actually deleting them.
|
||||
<2> A numeric return type directly removes the matching documents, returning the total number of documents removed.
|
||||
<3> A single domain type result retrieves and removes the first matching document.
|
||||
======
|
||||
|
||||
[[mongodb.repositories.queries.aggregation]]
|
||||
== Aggregation Repository Methods
|
||||
|
||||
The repository layer offers means to interact with xref:mongodb/aggregation-framework.adoc[the aggregation framework] via annotated repository query methods.
|
||||
Similar to the xref:mongodb/repositories/repositories.adoc#mongodb.repositories.queries.json-based[JSON based queries], you can define a pipeline using the `org.springframework.data.mongodb.repository.Aggregation` annotation.
|
||||
The definition may contain simple placeholders like `?0` as well as link:{springDocsUrl}/core.html#expressions[SpEL expressions] `?#{ … }`.
|
||||
|
||||
.Aggregating Repository Method
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface PersonRepository extends CrudRepository<Person, String> {
|
||||
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
|
||||
List<PersonAggregate> groupByLastnameAndFirstnames(); <1>
|
||||
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
|
||||
List<PersonAggregate> groupByLastnameAndFirstnames(Sort sort); <2>
|
||||
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : ?0 } } }")
|
||||
List<PersonAggregate> groupByLastnameAnd(String property); <3>
|
||||
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : ?0 } } }")
|
||||
Slice<PersonAggregate> groupByLastnameAnd(String property, Pageable page); <4>
|
||||
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
|
||||
Stream<PersonAggregate> groupByLastnameAndFirstnamesAsStream(); <5>
|
||||
|
||||
@Aggregation("{ $group : { _id : null, total : { $sum : $age } } }")
|
||||
SumValue sumAgeUsingValueWrapper(); <6>
|
||||
|
||||
@Aggregation("{ $group : { _id : null, total : { $sum : $age } } }")
|
||||
Long sumAge(); <7>
|
||||
|
||||
@Aggregation("{ $group : { _id : null, total : { $sum : $age } } }")
|
||||
AggregationResults<SumValue> sumAgeRaw(); <8>
|
||||
|
||||
@Aggregation("{ '$project': { '_id' : '$lastname' } }")
|
||||
List<String> findAllLastnames(); <9>
|
||||
|
||||
@Aggregation(pipeline = {
|
||||
"{ $group : { _id : '$author', books: { $push: '$title' } } }",
|
||||
"{ $out : 'authors' }"
|
||||
})
|
||||
void groupAndOutSkippingOutput(); <10>
|
||||
}
|
||||
----
|
||||
[source,java]
|
||||
----
|
||||
public class PersonAggregate {
|
||||
|
||||
private @Id String lastname; <2>
|
||||
private List<String> names;
|
||||
|
||||
public PersonAggregate(String lastname, List<String> names) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Getter / Setter omitted
|
||||
}
|
||||
|
||||
public class SumValue {
|
||||
|
||||
private final Long total; <6> <8>
|
||||
|
||||
public SumValue(Long total) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Getter omitted
|
||||
}
|
||||
----
|
||||
<1> Aggregation pipeline to group first names by `lastname` in the `Person` collection returning these as `PersonAggregate`.
|
||||
<2> If `Sort` argument is present, `$sort` is appended after the declared pipeline stages so that it only affects the order of the final results after having passed all other aggregation stages.
|
||||
Therefore, the `Sort` properties are mapped against the methods return type `PersonAggregate` which turns `Sort.by("lastname")` into `{ $sort : { '_id', 1 } }` because `PersonAggregate.lastname` is annotated with `@Id`.
|
||||
<3> Replaces `?0` with the given value for `property` for a dynamic aggregation pipeline.
|
||||
<4> `$skip`, `$limit` and `$sort` can be passed on via a `Pageable` argument. Same as in <2>, the operators are appended to the pipeline definition. Methods accepting `Pageable` can return `Slice` for easier pagination.
|
||||
<5> Aggregation methods can return `Stream` to consume results directly from an underlying cursor. Make sure to close the stream after consuming it to release the server-side cursor by either calling `close()` or through `try-with-resources`.
|
||||
<6> Map the result of an aggregation returning a single `Document` to an instance of a desired `SumValue` target type.
|
||||
<7> Aggregations resulting in single document holding just an accumulation result like e.g. `$sum` can be extracted directly from the result `Document`.
|
||||
To gain more control, you might consider `AggregationResult` as method return type as shown in <7>.
|
||||
<8> Obtain the raw `AggregationResults` mapped to the generic target wrapper type `SumValue` or `org.bson.Document`.
|
||||
<9> Like in <6>, a single value can be directly obtained from multiple result ``Document``s.
|
||||
<10> Skips the output of the `$out` stage when return type is `void`.
|
||||
====
|
||||
|
||||
In some scenarios, aggregations might require additional options, such as a maximum run time, additional log comments, or the permission to temporarily write data to disk.
|
||||
Use the `@Meta` annotation to set those options via `maxExecutionTimeMs`, `comment` or `allowDiskUse`.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
interface PersonRepository extends CrudRepository<Person, String> {
|
||||
|
||||
@Meta(allowDiskUse = true)
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
|
||||
List<PersonAggregate> groupByLastnameAndFirstnames();
|
||||
}
|
||||
----
|
||||
|
||||
Or use `@Meta` to create your own annotation as shown in the sample below.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD })
|
||||
@Meta(allowDiskUse = true)
|
||||
@interface AllowDiskUse { }
|
||||
|
||||
interface PersonRepository extends CrudRepository<Person, String> {
|
||||
|
||||
@AllowDiskUse
|
||||
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
|
||||
List<PersonAggregate> groupByLastnameAndFirstnames();
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Simple-type single-result inspects the returned `Document` and checks for the following:
|
||||
|
||||
. Only one entry in the document, return it.
|
||||
. Two entries, one is the `_id` value. Return the other.
|
||||
. Return for the first value assignable to the return type.
|
||||
. Throw an exception if none of the above is applicable.
|
||||
====
|
||||
|
||||
WARNING: The `Page` return type is not supported for repository methods using `@Aggregation`. However, you can use a
|
||||
`Pageable` argument to add `$skip`, `$limit` and `$sort` to the pipeline and let the method return `Slice`.
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ template.indexOps(Venue.class)
|
||||
.ensureIndex(new GeospatialIndex("location"));
|
||||
----
|
||||
|
||||
NOTE: `Index` and `GeospatialIndex` support configuration of xref:mongodb/template-query-options.adoc#mongo.query.collation[collations].
|
||||
NOTE: `Index` and `GeospatialIndex` support configuration of xref:mongodb/template-query-operations.adoc#mongo.query.collation[collations].
|
||||
|
||||
[[mongo-template.index-and-collections.access]]
|
||||
== Accessing Index Information
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[[mongo-template.instantiating]]
|
||||
= MongoTemplate Configuration
|
||||
= Configuration
|
||||
|
||||
You can use the following configuration to create and register an instance of `MongoTemplate`, as the following example shows:
|
||||
|
||||
@@ -68,7 +68,7 @@ Other optional properties that you might like to set when creating a `MongoTempl
|
||||
[[mongo-template.read-preference]]
|
||||
== Default Read Preference
|
||||
|
||||
The default read preference applied to read operations if no other preference was defined via the xref:mongodb/template-query-options.adoc#mongo.query.read-preference[Query].
|
||||
The default read preference applied to read operations if no other preference was defined via the xref:mongodb/template-query-operations.adoc#mongo.query.read-preference[Query].
|
||||
|
||||
[[mongo-template.writeresultchecking]]
|
||||
== WriteResultChecking Policy
|
||||
@@ -115,7 +115,7 @@ public class MyAppWriteConcernResolver implements WriteConcernResolver {
|
||||
[[mongo-template.entity-lifecycle-events]]
|
||||
== Publish entity lifecycle events
|
||||
|
||||
The template publishes xref:mongodb/mapping/lifecycle-events.adoc#mongodb.mapping-usage.events[lifecycle events].
|
||||
The template publishes xref:mongodb/lifecycle-events.adoc#mongodb.mapping-usage.events[lifecycle events].
|
||||
In case there are no listeners present, this feature can be disabled.
|
||||
|
||||
[source,java]
|
||||
@@ -131,7 +131,7 @@ MongoOperations mongoTemplate(MongoClient mongoClient) {
|
||||
[[mongo-template.entity-callbacks-config]]
|
||||
== Configure EntityCallbacks
|
||||
|
||||
Nest to lifecycle events the template invokes xref:mongodb/mapping/entity-callbacks.adoc[EntityCallbacks] which can be (if not auto configured) set via the template API.
|
||||
Nest to lifecycle events the template invokes xref:mongodb/lifecycle-events.adoc#mongo.entity-callbacks[EntityCallbacks] which can be (if not auto configured) set via the template API.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
|
||||
@@ -9,7 +9,7 @@ Consider the following class:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::example$Person.java[tags=class]
|
||||
include::example$example/Person.java[tags=class]
|
||||
----
|
||||
|
||||
Given the `Person` class in the preceding example, you can save, update and delete the object, as the following example shows:
|
||||
@@ -112,7 +112,7 @@ public class ReactiveMongoApplication {
|
||||
The preceding example is meant to show the use of save, update, and remove operations on `MongoTemplate` / `ReactiveMongoTemplate` and not to show complex mapping functionality.
|
||||
The query syntax used in the preceding example is explained in more detail in the section "`xref:mongodb/template-query-operations.adoc[Querying Documents]`".
|
||||
|
||||
IMPORTANT: MongoDB requires that you have an `_id` field for all documents. Please refer to the xref:mongodb/template-id-handling.adoc[id handling] section for details on the special treatment of this field.
|
||||
IMPORTANT: MongoDB requires that you have an `_id` field for all documents. Please refer to the xref:mongodb/template-crud-operations.adoc[ID handling] section for details on the special treatment of this field.
|
||||
|
||||
IMPORTANT: MongoDB collections can contain documents that represent instances of a variety of types. Please refer to the xref:mongodb/converters-type-mapping.adoc[type mapping] for details.
|
||||
|
||||
@@ -178,6 +178,50 @@ A similar set of insert operations is also available:
|
||||
* `void` *insert* `(Object objectToSave)`: Insert the object to the default collection.
|
||||
* `void` *insert* `(Object objectToSave, String collectionName)`: Insert the object to the specified collection.
|
||||
|
||||
[[mongo-template.id-handling]]
|
||||
=== How the `_id` Field is Handled in the Mapping Layer
|
||||
|
||||
MongoDB requires that you have an `_id` field for all documents.
|
||||
If you do not provide one, the driver assigns an `ObjectId` with a generated value. When you use the `MappingMongoConverter`, certain rules govern how properties from the Java class are mapped to this `_id` field:
|
||||
|
||||
. A property or field annotated with `@Id` (`org.springframework.data.annotation.Id`) maps to the `_id` field.
|
||||
. A property or field without an annotation but named `id` maps to the `_id` field.
|
||||
|
||||
The following outlines what type conversion, if any, is done on the property mapped to the `_id` document field when using the `MappingMongoConverter` (the default for `MongoTemplate`).
|
||||
|
||||
. If possible, an `id` property or field declared as a `String` in the Java class is converted to and stored as an `ObjectId` by using a Spring `Converter<String, ObjectId>`. Valid conversion rules are delegated to the MongoDB Java driver. If it cannot be converted to an `ObjectId`, then the value is stored as a string in the database.
|
||||
. An `id` property or field declared as `BigInteger` in the Java class is converted to and stored as an `ObjectId` by using a Spring `Converter<BigInteger, ObjectId>`.
|
||||
|
||||
If no field or property specified in the previous sets of rules is present in the Java class, an implicit `_id` file is generated by the driver but not mapped to a property or field of the Java class.
|
||||
|
||||
When querying and updating, `MongoTemplate` uses the converter that corresponds to the preceding rules for saving documents so that field names and types used in your queries can match what is in your domain classes.
|
||||
|
||||
Some environments require a customized approach to map `Id` values such as data stored in MongoDB that did not run through the Spring Data mapping layer. Documents can contain `_id` values that can be represented either as `ObjectId` or as `String`.
|
||||
Reading documents from the store back to the domain type works just fine. Querying for documents via their `id` can be cumbersome due to the implicit `ObjectId` conversion. Therefore documents cannot be retrieved that way.
|
||||
For those cases `@MongoId` provides more control over the actual id mapping attempts.
|
||||
|
||||
.`@MongoId` mapping
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class PlainStringId {
|
||||
@MongoId String id; <1>
|
||||
}
|
||||
|
||||
public class PlainObjectId {
|
||||
@MongoId ObjectId id; <2>
|
||||
}
|
||||
|
||||
public class StringToObjectId {
|
||||
@MongoId(FieldType.OBJECT_ID) String id; <3>
|
||||
}
|
||||
----
|
||||
<1> The id is treated as `String` without further conversion.
|
||||
<2> The id is treated as `ObjectId`.
|
||||
<3> The id is treated as `ObjectId` if the given `String` is a valid `ObjectId` hex, otherwise as `String`. Corresponds to `@Id` usage.
|
||||
====
|
||||
|
||||
|
||||
[[mongo-template.save-insert.collection]]
|
||||
=== Into Which Collection Are My Documents Saved?
|
||||
|
||||
@@ -246,7 +290,7 @@ Mono<BulkWriteResult> result = template.bulkOps(BulkMode.ORDERED, Person.class)
|
||||
[NOTE]
|
||||
====
|
||||
Server performance of batch and bulk is identical.
|
||||
However bulk operations do not publish xref:mongodb/mapping/lifecycle-events.adoc[lifecycle events].
|
||||
However bulk operations do not publish xref:mongodb/lifecycle-events.adoc[lifecycle events].
|
||||
====
|
||||
|
||||
[[mongodb-template-update]]
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
[[mongo-template.id-handling]]
|
||||
== How the `_id` Field is Handled in the Mapping Layer
|
||||
|
||||
MongoDB requires that you have an `_id` field for all documents.
|
||||
If you do not provide one, the driver assigns an `ObjectId` with a generated value. When you use the `MappingMongoConverter`, certain rules govern how properties from the Java class are mapped to this `_id` field:
|
||||
|
||||
. A property or field annotated with `@Id` (`org.springframework.data.annotation.Id`) maps to the `_id` field.
|
||||
. A property or field without an annotation but named `id` maps to the `_id` field.
|
||||
|
||||
The following outlines what type conversion, if any, is done on the property mapped to the `_id` document field when using the `MappingMongoConverter` (the default for `MongoTemplate`).
|
||||
|
||||
. If possible, an `id` property or field declared as a `String` in the Java class is converted to and stored as an `ObjectId` by using a Spring `Converter<String, ObjectId>`. Valid conversion rules are delegated to the MongoDB Java driver. If it cannot be converted to an `ObjectId`, then the value is stored as a string in the database.
|
||||
. An `id` property or field declared as `BigInteger` in the Java class is converted to and stored as an `ObjectId` by using a Spring `Converter<BigInteger, ObjectId>`.
|
||||
|
||||
If no field or property specified in the previous sets of rules is present in the Java class, an implicit `_id` file is generated by the driver but not mapped to a property or field of the Java class.
|
||||
|
||||
When querying and updating, `MongoTemplate` uses the converter that corresponds to the preceding rules for saving documents so that field names and types used in your queries can match what is in your domain classes.
|
||||
|
||||
Some environments require a customized approach to map `Id` values such as data stored in MongoDB that did not run through the Spring Data mapping layer. Documents can contain `_id` values that can be represented either as `ObjectId` or as `String`.
|
||||
Reading documents from the store back to the domain type works just fine. Querying for documents via their `id` can be cumbersome due to the implicit `ObjectId` conversion. Therefore documents cannot be retrieved that way.
|
||||
For those cases `@MongoId` provides more control over the actual id mapping attempts.
|
||||
|
||||
.`@MongoId` mapping
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class PlainStringId {
|
||||
@MongoId String id; <1>
|
||||
}
|
||||
|
||||
public class PlainObjectId {
|
||||
@MongoId ObjectId id; <2>
|
||||
}
|
||||
|
||||
public class StringToObjectId {
|
||||
@MongoId(FieldType.OBJECT_ID) String id; <3>
|
||||
}
|
||||
----
|
||||
<1> The id is treated as `String` without further conversion.
|
||||
<2> The id is treated as `ObjectId`.
|
||||
<3> The id is treated as `ObjectId` if the given `String` is a valid `ObjectId` hex, otherwise as `String`. Corresponds to `@Id` usage.
|
||||
====
|
||||
@@ -97,7 +97,7 @@ The `Criteria` class provides the following methods, all of which correspond to
|
||||
* `Criteria` *sampleRate* `(double sampleRate)` Creates a criterion using the `$sampleRate` operator
|
||||
* `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 xref:mongodb/template-collection-schema.adoc[JSON schema criteria]. `$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` *matchingDocumentStructure* `(MongoJsonSchema schema)` Creates a criterion using the `$jsonSchema` operator for xref:mongodb/mapping/mapping-schema.adoc[JSON schema criteria]. `$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`.
|
||||
|
||||
The Criteria class also provides the following methods for geospatial queries.
|
||||
@@ -200,6 +200,87 @@ query.fields()
|
||||
|
||||
`@Query(fields="…")` allows usage of expression field projections at `Repository` level as described in xref:mongodb/repositories/repositories.adoc#mongodb.repositories.queries.json-based[MongoDB JSON-based Query Methods and Field Restriction].
|
||||
|
||||
[[mongo.query.additional-query-options]]
|
||||
== Additional Query Options
|
||||
|
||||
MongoDB offers various ways of applying meta information, like a comment or a batch size, to a query.Using the `Query` API
|
||||
directly there are several methods for those options.
|
||||
|
||||
[[mongo.query.hints]]
|
||||
=== Hints
|
||||
|
||||
Index hints can be applied in two ways, using the index name or its field definition.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
template.query(Person.class)
|
||||
.matching(query("...").withHint("index-to-use"));
|
||||
|
||||
template.query(Person.class)
|
||||
.matching(query("...").withHint("{ firstname : 1 }"));
|
||||
----
|
||||
====
|
||||
|
||||
[[mongo.query.cursor-size]]
|
||||
=== Cursor Batch Size
|
||||
|
||||
The cursor batch size defines the number of documents to return in each response batch.
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Query query = query(where("firstname").is("luke"))
|
||||
.cursorBatchSize(100)
|
||||
----
|
||||
====
|
||||
|
||||
[[mongo.query.collation]]
|
||||
=== Collations
|
||||
|
||||
Using collations with collection operations is a matter of specifying a `Collation` instance in your query or operation options, as the following two examples show:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Collation collation = Collation.of("de");
|
||||
|
||||
Query query = new Query(Criteria.where("firstName").is("Amél"))
|
||||
.collation(collation);
|
||||
|
||||
List<Person> results = template.find(query, Person.class);
|
||||
----
|
||||
====
|
||||
|
||||
[[mongo.query.read-preference]]
|
||||
=== Read Preference
|
||||
|
||||
The `ReadPreference` to use can be set directly on the `Query` object to be run as outlined below.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
template.find(Person.class)
|
||||
.matching(query(where(...)).withReadPreference(ReadPreference.secondary()))
|
||||
.all();
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: The preference set on the `Query` instance will supersede the default `ReadPreference` of `MongoTemplate`.
|
||||
|
||||
[[mongo.query.comment]]
|
||||
=== Comments
|
||||
|
||||
Queries can be equipped with comments which makes them easier to look up in server logs.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
template.find(Person.class)
|
||||
.matching(query(where(...)).comment("Use the force luke!"))
|
||||
.all();
|
||||
----
|
||||
====
|
||||
|
||||
[[mongo-template.query.distinct]]
|
||||
== Query Distinct Values
|
||||
|
||||
@@ -741,7 +822,7 @@ Note that these two optional flags have been introduced in MongoDB 3.2 and are n
|
||||
[[mongo.query-by-example]]
|
||||
== Query by Example
|
||||
|
||||
Some general information about Query By Example support in Spring Data can be found in the commons documentation.
|
||||
xref:repositories/query-by-example.adoc[Query by Example] can be used on the Template API level run example queries.
|
||||
|
||||
The following snipped shows how to query by example:
|
||||
|
||||
@@ -858,7 +939,7 @@ template.find(query(matchingDocumentStructure(schema)), Person.class);
|
||||
----
|
||||
====
|
||||
|
||||
Please refer to the xref:mongodb/template-collection-schema.adoc[JSON Schema] section to learn more about the schema support in Spring Data MongoDB.
|
||||
Please refer to the xref:mongodb/mapping/mapping-schema.adoc[JSON Schema] section to learn more about the schema support in Spring Data MongoDB.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
[[mongo.query.additional-query-options]]
|
||||
= Additional Query Options
|
||||
|
||||
MongoDB offers various ways of applying meta information, like a comment or a batch size, to a query.Using the `Query` API
|
||||
directly there are several methods for those options.
|
||||
|
||||
[[mongo.query.hints]]
|
||||
== Hints
|
||||
|
||||
Index hints can be applied in two ways, using the index name or its field definition.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
template.query(Person.class)
|
||||
.matching(query("...").withHint("index-to-use"));
|
||||
|
||||
template.query(Person.class)
|
||||
.matching(query("...").withHint("{ firstname : 1 }"));
|
||||
----
|
||||
====
|
||||
|
||||
[[mongo.query.cursor-size]]
|
||||
== Cursor Batch Size
|
||||
|
||||
The cursor batch size defines the number of documents to return in each response batch.
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Query query = query(where("firstname").is("luke"))
|
||||
.cursorBatchSize(100)
|
||||
----
|
||||
====
|
||||
|
||||
[[mongo.query.collation]]
|
||||
== Collations
|
||||
|
||||
Using collations with collection operations is a matter of specifying a `Collation` instance in your query or operation options, as the following two examples show:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Collation collation = Collation.of("de");
|
||||
|
||||
Query query = new Query(Criteria.where("firstName").is("Amél"))
|
||||
.collation(collation);
|
||||
|
||||
List<Person> results = template.find(query, Person.class);
|
||||
----
|
||||
====
|
||||
|
||||
[[mongo.query.read-preference]]
|
||||
== Read Preference
|
||||
|
||||
The `ReadPreference` to use can be set directly on the `Query` object to be run as outlined below.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
template.find(Person.class)
|
||||
.matching(query(where(...)).withReadPreference(ReadPreference.secondary()))
|
||||
.all();
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: The preference set on the `Query` instance will supersede the default `ReadPreference` of `MongoTemplate`.
|
||||
|
||||
[[mongo.query.comment]]
|
||||
== Comments
|
||||
|
||||
Queries can be equipped with comments which makes them easier to look up in server logs.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
template.find(Person.class)
|
||||
.matching(query(where(...)).comment("Use the force luke!"))
|
||||
.all();
|
||||
----
|
||||
====
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
include::{commons}@data-commons::page$repositories/core-concepts.adoc[]
|
||||
|
||||
[[cassandra.entity-persistence.state-detection-strategies]]
|
||||
[[mongodb.entity-persistence.state-detection-strategies]]
|
||||
include::{commons}@data-commons::page$is-new-state-detection.adoc[leveloffset=+1]
|
||||
|
||||
[NOTE]
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
[[cassandra.projections]]
|
||||
= Projections
|
||||
|
||||
include::{commons}@data-commons::page$repositories/projections.adoc[leveloffset=+1]
|
||||
[[mongodb.projections]]
|
||||
include::{commons}@data-commons::page$repositories/projections.adoc[]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[[query-by-example.running]]
|
||||
= Running an Example
|
||||
include::{commons}@data-commons::query-by-example.adoc[]
|
||||
|
||||
TODO: move this section to the repositories documentation
|
||||
[[query-by-example.running]]
|
||||
== Running an Example
|
||||
|
||||
The following example shows how to query by example when using a repository (of `Person` objects, in this case):
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 64 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 8.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
Reference in New Issue
Block a user