MongoDB supports https://geojson.org/[GeoJSON] and simple (legacy) coordinate pairs for geospatial data. Those formats can both be used for storing as well as querying data. See the https://docs.mongodb.org/manual/core/2dsphere/#geospatial-indexes-store-geojson/[MongoDB manual on GeoJSON support] to learn about requirements and restrictions.
[[mongo.geo-json.domain.classes]]
== GeoJSON Types in Domain Classes
Usage of https://geojson.org/[GeoJSON] types in domain classes is straightforward. The `org.springframework.data.mongodb.core.geo` package contains types such as `GeoJsonPoint`, `GeoJsonPolygon`, and others. These types are extend the existing `org.springframework.data.geo` types. The following example uses a `GeoJsonPoint`:
====
[source,java]
----
public class Store {
String id;
/**
* location is stored in GeoJSON format.
* {
* "type" : "Point",
* "coordinates" : [ x, y ]
* }
*/
GeoJsonPoint location;
}
----
====
[TIP]
====
If the `coordinates` of a GeoJSON object represent _latitude_ and _longitude_ pairs, the _longitude_ goes first followed by _latitude_. +
`GeoJsonPoint` therefore treats `getX()` as _longitude_ and `getY()` as _latitude_.
====
[[mongo.geo-json.query-methods]]
== GeoJSON Types in Repository Query Methods
Using GeoJSON types as repository query parameters forces usage of the `$geometry` operator when creating the query, as the following example shows:
====
[source,java]
----
public interface StoreRepository extends CrudRepository<Store, String> {
MongoDB supports GeoSpatial queries through the use of operators such as `$near`, `$within`, `geoWithin`, and `$nearSphere`. Methods specific to geospatial queries are available on the `Criteria` class. There are also a few shape classes (`Box`, `Circle`, and `Point`) that are used in conjunction with geospatial related `Criteria` methods.
NOTE: Using GeoSpatial queries requires attention when used within MongoDB transactions, see xref:reference/client-session-transactions.adoc#mongo.transactions.behavior[Special behavior inside transactions].
To understand how to perform GeoSpatial queries, consider the following `Venue` class (taken from the integration tests and relying on the rich `MappingMongoConverter`):
[source,java]
----
@Document(collection="newyork")
public class Venue {
@Id
private String id;
private String name;
private double[] location;
@PersistenceConstructor
Venue(String name, double[] location) {
super();
this.name = name;
this.location = location;
}
public Venue(String name, double x, double y) {
super();
this.name = name;
this.location = new double[] { x, y };
}
public String getName() {
return name;
}
public double[] getLocation() {
return location;
}
@Override
public String toString() {
return "Venue [id=" + id + ", name=" + name + ", location="
+ Arrays.toString(location) + "]";
}
}
----
To find locations within a `Circle`, you can use the following query:
[source,java]
----
Circle circle = new Circle(-73.99171, 40.738868, 0.01);
https://docs.mongodb.com/master/release-notes/4.2-compatibility/[MongoDB 4.2] removed support for the
`geoNear` command which had been previously used to run the `NearQuery`.
Spring Data MongoDB 2.2 `MongoOperations#geoNear` uses the `$geoNear` https://docs.mongodb.com/manual/reference/operator/aggregation/geoNear/[aggregation]
instead of the `geoNear` command to run a `NearQuery`.
The calculated distance (the `dis` when using a geoNear command) previously returned within a wrapper type now is embedded
into the resulting document.
If the given domain type already contains a property with that name, the calculated distance
is named `calculated-distance` with a potentially random postfix.
Target types may contain a property named after the returned distance to (additionally) read it back directly into the domain type as shown below.
<1> Domain type used to identify the target collection and potential query mapping.
<2> Target type containing a `dis` field of type `Number`.
====
MongoDB supports querying the database for geo locations and calculating the distance from a given origin at the same time. With geo-near queries, you can express queries such as "find all restaurants in the surrounding 10 miles". To let you do so, `MongoOperations` provides `geoNear(…)` methods that take a `NearQuery` as an argument (as well as the already familiar entity type and collection), as shown in the following example:
We use the `NearQuery` builder API to set up a query to return all `Restaurant` instances surrounding the given `Point` out to 10 miles. The `Metrics` enum used here actually implements an interface so that other metrics could be plugged into a distance as well. A `Metric` is backed by a multiplier to transform the distance value of the given metric into native distances. The sample shown here would consider the 10 to be miles. Using one of the built-in metrics (miles and kilometers) automatically triggers the spherical flag to be set on the query. If you want to avoid that, pass plain `double` values into `maxDistance(…)`. For more information, see the https://docs.spring.io/spring-data/mongodb/docs/{version}/api/index.html[JavaDoc] of `NearQuery` and `Distance`.
The geo-near operations return a `GeoResults` wrapper object that encapsulates `GeoResult` instances. Wrapping `GeoResults` allows accessing the average distance of all results. A single `GeoResult` object carries the entity found plus its distance from the origin.
MongoDB provides an operation to obtain distinct values for a single field by using a query from the resulting documents.
Resulting values are not required to have the same data type, nor is the feature limited to simple types.
For retrieval, the actual result type does matter for the sake of conversion and typing. The following example shows how to query for distinct values:
.Retrieving distinct values
====
[source,java]
----
template.query(Person.class) <1>
.distinct("lastname") <2>
.all(); <3>
----
<1> Query the `Person` collection.
<2> Select distinct values of the `lastname` field. The field name is mapped according to the domain types property declaration, taking potential `@Field` annotations into account.
<3> Retrieve all distinct values as a `List` of `Object` (due to no explicit result type being specified).
====
Retrieving distinct values into a `Collection` of `Object` is the most flexible way, as it tries to determine the property value of the domain type and convert results to the desired type or mapping `Document` structures.
Sometimes, when all values of the desired field are fixed to a certain type, it is more convenient to directly obtain a correctly typed `Collection`, as shown in the following example:
.Retrieving strongly typed distinct values
====
[source,java]
----
template.query(Person.class) <1>
.distinct("lastname") <2>
.as(String.class) <3>
.all(); <4>
----
<1> Query the collection of `Person`.
<2> Select distinct values of the `lastname` field. The fieldname is mapped according to the domain types property declaration, taking potential `@Field` annotations into account.
<3> Retrieved values are converted into the desired target type -- in this case, `String`. It is also possible to map the values to a more complex type if the stored field contains a document.
<4> Retrieve all distinct values as a `List` of `String`. If the type cannot be converted into the desired target type, this method throws a `DataAccessException`.
The query methods need to specify the target type `T` that is returned, and they are overloaded with an explicit collection name for queries that should operate on a collection other than the one indicated by the return type. The following query methods let you find one or more documents:
* *findAll*: Query for a list of objects of type `T` from the collection.
* *findOne*: Map the results of an ad-hoc query on the collection to a single instance of an object of the specified type.
* *findById*: Return an object of the given ID and target class.
* *find*: Map the results of an ad-hoc query on the collection to a `List` of the specified type.
* *findAndRemove*: Map the results of an ad-hoc query on the collection to a single instance of an object of the specified type. The first document that matches the query is returned and removed from the collection in the database.
Since version 2.6 of MongoDB, you can run full-text queries by using the `$text` operator. Methods and operations specific to full-text queries are available in `TextQuery` and `TextCriteria`. When doing full text search, see the https://docs.mongodb.org/manual/reference/operator/query/text/#behavior[MongoDB reference] for its behavior and limitations.
[[full-text-search]]
== Full-text Search
Before you can actually use full-text search, you must set up the search index correctly. See xref:reference/mapping.adoc#mapping-usage-indexes.text-index[Text Index] for more detail on how to create index structures. The following example shows how to set up a full-text search:
[source,javascript]
----
db.foo.createIndex(
{
title : "text",
content : "text"
},
{
weights : {
title : 3
}
}
)
----
A query searching for `coffee cake` can be defined and run as follows:
<1> Use the score property for sorting results by relevance which triggers `.sort({'score': {'$meta': 'textScore'}})`.
<2> Use `TextQuery.includeScore()` to include the calculated relevance in the resulting `Document`.
====
You can exclude search terms by prefixing the term with `-` or by using `notMatching`, as shown in the following example (note that the two lines have the same effect and are thus redundant):
`TextCriteria.matching` takes the provided term as is. Therefore, you can define phrases by putting them between double quotation marks (for example, `\"coffee cake\")` or using by `TextCriteria.phrase.` The following example shows both ways of defining a phrase:
You can set flags for `$caseSensitive` and `$diacriticSensitive` by using the corresponding methods on `TextCriteria`. Note that these two optional flags have been introduced in MongoDB 3.2 and are not included in the query unless explicitly set.
<4> Retrieve all distinct values as a `List` of `String`. If the type cannot be converted into the desired target type, this method throws a `DataAccessException`.
====
[[mongo.geospatial]]
= GeoSpatial Queries
MongoDB supports GeoSpatial queries through the use of operators such as `$near`, `$within`, `geoWithin`, and `$nearSphere`. Methods specific to geospatial queries are available on the `Criteria` class. There are also a few shape classes (`Box`, `Circle`, and `Point`) that are used in conjunction with geospatial related `Criteria` methods.
NOTE: Using GeoSpatial queries requires attention when used within MongoDB transactions, see xref:reference/client-session-transactions.adoc#mongo.transactions.behavior[Special behavior inside transactions].
To understand how to perform GeoSpatial queries, consider the following `Venue` class (taken from the integration tests and relying on the rich `MappingMongoConverter`):
[source,java]
----
@Document(collection="newyork")
public class Venue {
@Id
private String id;
private String name;
private double[] location;
@PersistenceConstructor
Venue(String name, double[] location) {
super();
this.name = name;
this.location = location;
}
public Venue(String name, double x, double y) {
super();
this.name = name;
this.location = new double[] { x, y };
}
public String getName() {
return name;
}
public double[] getLocation() {
return location;
}
@Override
public String toString() {
return "Venue [id=" + id + ", name=" + name + ", location="
+ Arrays.toString(location) + "]";
}
}
----
To find locations within a `Circle`, you can use the following query:
[source,java]
----
Circle circle = new Circle(-73.99171, 40.738868, 0.01);
https://docs.mongodb.com/master/release-notes/4.2-compatibility/[MongoDB 4.2] removed support for the
`geoNear` command which had been previously used to run the `NearQuery`.
Spring Data MongoDB 2.2 `MongoOperations#geoNear` uses the `$geoNear` https://docs.mongodb.com/manual/reference/operator/aggregation/geoNear/[aggregation]
instead of the `geoNear` command to run a `NearQuery`.
The calculated distance (the `dis` when using a geoNear command) previously returned within a wrapper type now is embedded
into the resulting document.
If the given domain type already contains a property with that name, the calculated distance
is named `calculated-distance` with a potentially random postfix.
Target types may contain a property named after the returned distance to (additionally) read it back directly into the domain type as shown below.
<1> Domain type used to identify the target collection and potential query mapping.
<2> Target type containing a `dis` field of type `Number`.
====
MongoDB supports querying the database for geo locations and calculating the distance from a given origin at the same time. With geo-near queries, you can express queries such as "find all restaurants in the surrounding 10 miles". To let you do so, `MongoOperations` provides `geoNear(…)` methods that take a `NearQuery` as an argument (as well as the already familiar entity type and collection), as shown in the following example:
We use the `NearQuery` builder API to set up a query to return all `Restaurant` instances surrounding the given `Point` out to 10 miles.
The `Metrics` enum used here actually implements an interface so that other metrics could be plugged into a distance as well.
A `Metric` is backed by a multiplier to transform the distance value of the given metric into native distances.
The sample shown here would consider the 10 to be miles. Using one of the built-in metrics (miles and kilometers) automatically triggers the spherical flag to be set on the query.
If you want to avoid that, pass plain `double` values into `maxDistance(…)`.
For more information, see the https://docs.spring.io/spring-data/mongodb/docs/{version}/api/index.html[JavaDoc] of `NearQuery` and `Distance`.
The geo-near operations return a `GeoResults` wrapper object that encapsulates `GeoResult` instances.
Wrapping `GeoResults` allows accessing the average distance of all results.
A single `GeoResult` object carries the entity found plus its distance from the origin.
[[mongo.geo-json]]
== GeoJSON Support
MongoDB supports https://geojson.org/[GeoJSON] and simple (legacy) coordinate pairs for geospatial data. Those formats can both be used for storing as well as querying data. See the https://docs.mongodb.org/manual/core/2dsphere/#geospatial-indexes-store-geojson/[MongoDB manual on GeoJSON support] to learn about requirements and restrictions.
[[mongo.geo-json.domain.classes]]
== GeoJSON Types in Domain Classes
Usage of https://geojson.org/[GeoJSON] types in domain classes is straightforward. The `org.springframework.data.mongodb.core.geo` package contains types such as `GeoJsonPoint`, `GeoJsonPolygon`, and others. These types are extend the existing `org.springframework.data.geo` types. The following example uses a `GeoJsonPoint`:
====
[source,java]
----
public class Store {
String id;
/**
* location is stored in GeoJSON format.
* {
* "type" : "Point",
* "coordinates" : [ x, y ]
* }
*/
GeoJsonPoint location;
}
----
====
[TIP]
====
If the `coordinates` of a GeoJSON object represent _latitude_ and _longitude_ pairs, the _longitude_ goes first followed by _latitude_. +
`GeoJsonPoint` therefore treats `getX()` as _longitude_ and `getY()` as _latitude_.
====
[[mongo.geo-json.query-methods]]
== GeoJSON Types in Repository Query Methods
Using GeoJSON types as repository query parameters forces usage of the `$geometry` operator when creating the query, as the following example shows:
====
[source,java]
----
public interface StoreRepository extends CrudRepository<Store, String> {
<1> Maximum distance from center point in _Radians_.
<2> The distance multiplier so we get _Kilometers_ as resulting distance.
<3> Make sure we operate on a 2d_sphere index.
<4> Distance from center point in _Kilometers_ - take it times 1000 to match _Meters_ of the GeoJSON variant.
====
[[mongo.textsearch]]
== Full-text Search
Since version 2.6 of MongoDB, you can run full-text queries by using the `$text` operator. Methods and operations specific to full-text queries are available in `TextQuery` and `TextCriteria`. When doing full text search, see the https://docs.mongodb.org/manual/reference/operator/query/text/#behavior[MongoDB reference] for its behavior and limitations.
Before you can actually use full-text search, you must set up the search index correctly.
See xref:mongodb/mapping.adoc#mapping-usage-indexes.text-index[Text Index] for more detail on how to create index structures.
The following example shows how to set up a full-text search:
[source,javascript]
----
db.foo.createIndex(
{
title : "text",
content : "text"
},
{
weights : {
title : 3
}
}
)
----
A query searching for `coffee cake` can be defined and run as follows:
<1> Use the score property for sorting results by relevance which triggers `.sort({'score': {'$meta': 'textScore'}})`.
<2> Use `TextQuery.includeScore()` to include the calculated relevance in the resulting `Document`.
====
You can exclude search terms by prefixing the term with `-` or by using `notMatching`, as shown in the following example (note that the two lines have the same effect and are thus redundant):