Querying Documents

You can use the Query and Criteria classes to express your queries. They have method names that mirror the native MongoDB operator names, such as lt, lte, is, and others. The Query and Criteria classes follow a fluent API style so that you can chain together multiple method criteria and queries while having easy-to-understand code. To improve readability, static imports let you avoid using the 'new' keyword for creating Query and Criteria instances. You can also use BasicQuery to create Query instances from plain JSON Strings, as shown in the following example:

Example 1. Creating a Query instance from a plain JSON String
BasicQuery query = new BasicQuery("{ age : { $lt : 50 }, accounts.balance : { $gt : 1000.00 }}");
List<Person> result = mongoTemplate.find(query, Person.class);

Spring MongoDB also supports GeoSpatial queries (see the GeoSpatial Queries section) and Map-Reduce operations (see the Map-Reduce section.).

Querying Documents in a Collection

Earlier, we saw how to retrieve a single document by using the findOne and findById methods on MongoTemplate. These methods return a single domain object. We can also query for a collection of documents to be returned as a list of domain objects. Assuming that we have a number of Person objects with name and age stored as documents in a collection and that each person has an embedded account document with a balance, we can now run a query using the following code:

Example 2. Querying for documents using the MongoTemplate
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;

…

List<Person> result = mongoTemplate.find(query(where("age").lt(50)
  .and("accounts.balance").gt(1000.00d)), Person.class);

All find methods take a Query object as a parameter. This object defines the criteria and options used to perform the query. The criteria are specified by using a Criteria object that has a static factory method named where to instantiate a new Criteria object. We recommend using static imports for org.springframework.data.mongodb.core.query.Criteria.where and Query.query to make the query more readable.

The query should return a list of Person objects that meet the specified criteria. The rest of this section lists the methods of the Criteria and Query classes that correspond to the operators provided in MongoDB. Most methods return the Criteria object, to provide a fluent style for the API.

Methods for the Criteria Class

The Criteria class provides the following methods, all of which correspond to operators in MongoDB:

  • Criteria all (Object o) Creates a criterion using the $all operator

  • Criteria and (String key) Adds a chained Criteria with the specified key to the current Criteria and returns the newly created one

  • Criteria andOperator (Criteria…​ criteria) Creates an and query using the $and operator for all of the provided criteria (requires MongoDB 2.0 or later)

  • Criteria elemMatch (Criteria c) Creates a criterion using the $elemMatch operator

  • Criteria exists (boolean b) Creates a criterion using the $exists operator

  • Criteria gt (Object o) Creates a criterion using the $gt operator

  • Criteria gte (Object o) Creates a criterion using the $gte operator

  • Criteria in (Object…​ o) Creates a criterion using the $in operator for a varargs argument.

  • Criteria in (Collection<?> collection) Creates a criterion using the $in operator using a collection

  • Criteria is (Object o) Creates a criterion using field matching ({ key:value }). If the specified value is a document, the order of the fields and exact equality in the document matters.

  • Criteria lt (Object o) Creates a criterion using the $lt operator

  • Criteria lte (Object o) Creates a criterion using the $lte operator

  • Criteria mod (Number value, Number remainder) Creates a criterion using the $mod operator

  • Criteria ne (Object o) Creates a criterion using the $ne operator

  • Criteria nin (Object…​ o) Creates a criterion using the $nin operator

  • Criteria norOperator (Criteria…​ criteria) Creates an nor query using the $nor operator for all of the provided criteria

  • Criteria not () Creates a criterion using the $not meta operator which affects the clause directly following

  • Criteria orOperator (Criteria…​ criteria) Creates an or query using the $or operator for all of the provided criteria

  • Criteria regex (String re) Creates a criterion using a $regex

  • 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 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 MongoDB bitwise query operators like $bitsAllClear.

The Criteria class also provides the following methods for geospatial queries (see the GeoSpatial Queries section to see them in action):

  • Criteria within (Circle circle) Creates a geospatial criterion using $geoWithin $center operators.

  • Criteria within (Box box) Creates a geospatial criterion using a $geoWithin $box operation.

  • Criteria withinSphere (Circle circle) Creates a geospatial criterion using $geoWithin $center operators.

  • Criteria near (Point point) Creates a geospatial criterion using a $near operation

  • Criteria nearSphere (Point point) Creates a geospatial criterion using $nearSphere$center operations. This is only available for MongoDB 1.7 and higher.

  • Criteria minDistance (double minDistance) Creates a geospatial criterion using the $minDistance operation, for use with $near.

  • Criteria maxDistance (double maxDistance) Creates a geospatial criterion using the $maxDistance operation, for use with $near.

Methods for the Query class

The Query class has some additional methods that provide options for the query:

  • Query addCriteria (Criteria criteria) used to add additional criteria to the query

  • Field fields () used to define fields to be included in the query results

  • Query limit (int limit) used to limit the size of the returned results to the provided limit (used for paging)

  • Query skip (int skip) used to skip the provided number of documents in the results (used for paging)

  • Query with (Sort sort) used to provide sort definition for the results

Methods for Querying for Documents

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.

Query Distinct Values

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:

Example 3. Retrieving distinct values
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:

Example 4. Retrieving strongly typed distinct values
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.

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.

Using GeoSpatial queries requires attention when used within MongoDB transactions, see mongo.transactions.behavior.

To understand how to perform GeoSpatial queries, consider the following Venue class (taken from the integration tests and relying on the rich MappingMongoConverter):

@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:

Circle circle = new Circle(-73.99171, 40.738868, 0.01);
List<Venue> venues =
    template.find(new Query(Criteria.where("location").within(circle)), Venue.class);

To find venues within a Circle using spherical coordinates, you can use the following query:

Circle circle = new Circle(-73.99171, 40.738868, 0.003712240453784);
List<Venue> venues =
    template.find(new Query(Criteria.where("location").withinSphere(circle)), Venue.class);

To find venues within a Box, you can use the following query:

//lower-left then upper-right
Box box = new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404));
List<Venue> venues =
    template.find(new Query(Criteria.where("location").within(box)), Venue.class);

To find venues near a Point, you can use the following queries:

Point point = new Point(-73.99171, 40.738868);
List<Venue> venues =
    template.find(new Query(Criteria.where("location").near(point).maxDistance(0.01)), Venue.class);
Point point = new Point(-73.99171, 40.738868);
List<Venue> venues =
    template.find(new Query(Criteria.where("location").near(point).minDistance(0.01).maxDistance(100)), Venue.class);

To find venues near a Point using spherical coordinates, you can use the following query:

Point point = new Point(-73.99171, 40.738868);
List<Venue> venues =
    template.find(new Query(
        Criteria.where("location").nearSphere(point).maxDistance(0.003712240453784)),
        Venue.class);

Geo-near Queries

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:

Point location = new Point(-73.99171, 40.738868);
NearQuery query = NearQuery.near(location).maxDistance(new Distance(10, Metrics.MILES));

GeoResults<Restaurant> = operations.geoNear(query, Restaurant.class);

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 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.

GeoJSON Support

MongoDB supports GeoJSON and simple (legacy) coordinate pairs for geospatial data. Those formats can both be used for storing as well as querying data. See the MongoDB manual on GeoJSON support to learn about requirements and restrictions.

GeoJSON Types in Domain Classes

Usage of 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:

public class Store {

	String id;

	/**
	 * location is stored in GeoJSON format.
	 * {
	 *   "type" : "Point",
	 *   "coordinates" : [ x, y ]
	 * }
	 */
	GeoJsonPoint location;
}

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:

public interface StoreRepository extends CrudRepository<Store, String> {

	List<Store> findByLocationWithin(Polygon polygon);  (1)

}

/*
 * {
 *   "location": {
 *     "$geoWithin": {
 *       "$geometry": {
 *         "type": "Polygon",
 *         "coordinates": [
 *           [
 *             [-73.992514,40.758934],
 *             [-73.961138,40.760348],
 *             [-73.991658,40.730006],
 *             [-73.992514,40.758934]
 *           ]
 *         ]
 *       }
 *     }
 *   }
 * }
 */
repo.findByLocationWithin(                              (2)
  new GeoJsonPolygon(
    new Point(-73.992514, 40.758934),
    new Point(-73.961138, 40.760348),
    new Point(-73.991658, 40.730006),
    new Point(-73.992514, 40.758934)));                 (3)

/*
 * {
 *   "location" : {
 *     "$geoWithin" : {
 *        "$polygon" : [ [-73.992514,40.758934] , [-73.961138,40.760348] , [-73.991658,40.730006] ]
 *     }
 *   }
 * }
 */
repo.findByLocationWithin(                              (4)
  new Polygon(
    new Point(-73.992514, 40.758934),
    new Point(-73.961138, 40.760348),
    new Point(-73.991658, 40.730006));
1 Repository method definition using the commons type allows calling it with both the GeoJSON and the legacy format.
2 Use GeoJSON type to make use of $geometry operator.
3 Note that GeoJSON polygons need to define a closed ring.
4 Use the legacy format $polygon operator.

Full-text Queries

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 MongoDB reference for its behavior and limitations.

Before you can actually use full-text search, you must set up the search index correctly. See Text Index for more detail on how to create index structures. The following example shows how to set up a full-text search:

db.foo.createIndex(
{
  title : "text",
  content : "text"
},
{
  weights : {
              title : 3
            }
}
)

A query searching for coffee cake, sorted by relevance according to the weights, can be defined and executed as follows:

Query query = TextQuery.searching(new TextCriteria().matchingAny("coffee", "cake")).sortByScore();
List<Document> page = template.find(query, Document.class);

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):

// search for 'coffee' and not 'cake'
TextQuery.searching(new TextCriteria().matching("coffee").matching("-cake"));
TextQuery.searching(new TextCriteria().matching("coffee").notMatching("cake"));

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:

// search for phrase 'coffee cake'
TextQuery.searching(new TextCriteria().matching("\"coffee cake\""));
TextQuery.searching(new TextCriteria().phrase("coffee cake"));

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.

Collations

Since version 3.4, MongoDB supports collations for collection and index creation and various query operations. Collations define string comparison rules based on the ICU collations. A collation document consists of various properties that are encapsulated in Collation, as the following listing shows:

Collation collation = Collation.of("fr")         (1)

  .strength(ComparisonLevel.secondary()          (2)
    .includeCase())

  .numericOrderingEnabled()                      (3)

  .alternate(Alternate.shifted().punct())        (4)

  .forwardDiacriticSort()                        (5)

  .normalizationEnabled();                       (6)
1 Collation requires a locale for creation. This can be either a string representation of the locale, a Locale (considering language, country, and variant) or a CollationLocale. The locale is mandatory for creation.
2 Collation strength defines comparison levels that denote differences between characters. You can configure various options (case-sensitivity, case-ordering, and others), depending on the selected strength.
3 Specify whether to compare numeric strings as numbers or as strings.
4 Specify whether the collation should consider whitespace and punctuation as base characters for purposes of comparison.
5 Specify whether strings with diacritics sort from back of the string, such as with some French dictionary ordering.
6 Specify whether to check whether text requires normalization and whether to perform normalization.

Collations can be used to create collections and indexes. If you create a collection that specifies a collation, the collation is applied to index creation and queries unless you specify a different collation. A collation is valid for a whole operation and cannot be specified on a per-field basis, as the following example shows:

Collation french = Collation.of("fr");
Collation german = Collation.of("de");

template.createCollection(Person.class, CollectionOptions.just(collation));

template.indexOps(Person.class).ensureIndex(new Index("name", Direction.ASC).collation(german));
MongoDB uses simple binary comparison if no collation is specified (Collation.simple()).

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:

Example 5. Using collation with find
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);
Example 6. Using collation with aggregate
Collation collation = Collation.of("de");

AggregationOptions options = AggregationOptions.builder().collation(collation).build();

Aggregation aggregation = newAggregation(
  project("tags"),
  unwind("tags"),
  group("tags")
    .count().as("count")
).withOptions(options);

AggregationResults<TagCount> results = template.aggregate(aggregation, "tags", TagCount.class);
Indexes are only used if the collation used for the operation matches the index collation.

JSON Schema

As of version 3.6, MongoDB supports collections that validate documents against a provided JSON Schema. The schema itself and both validation action and level can be defined when creating the collection, as the following example shows:

Example 7. Sample JSON schema
{
  "type": "object",                                                        (1)

  "required": [ "firstname", "lastname" ],                                 (2)

  "properties": {                                                          (3)

    "firstname": {                                                         (4)
      "type": "string",
      "enum": [ "luke", "han" ]
    },
    "address": {                                                           (5)
      "type": "object",
      "properties": {
        "postCode": { "type": "string", "minLength": 4, "maxLength": 5 }
      }
    }
  }
}
1 JSON schema documents always describe a whole document from its root. A schema is a schema object itself that can contain embedded schema objects that describe properties and subdocuments.
2 required is a property that describes which properties are required in a document. It can be specified optionally, along with other schema constraints. See MongoDB’s documentation on available keywords.
3 properties is related to a schema object that describes an object type. It contains property-specific schema constraints.
4 firstname specifies constraints for the firsname field inside the document. Here, it is a string-based properties element declaring possible field values.
5 address is a subdocument defining a schema for values in its postCode field.

You can provide a schema either by specifying a schema document (that is, by using the Document API to parse or build a document object) or by building it with Spring Data’s JSON schema utilities in org.springframework.data.mongodb.core.schema. MongoJsonSchema is the entry point for all JSON schema-related operations. The following example shows how use MongoJsonSchema.builder() to create a JSON schema:

Example 8. Creating a JSON schema
MongoJsonSchema.builder()                                                  (1)
    .required("firstname", "lastname")                                     (2)

    .properties(
                string("firstname").possibleValues("luke", "han"),         (3)

                object("address")
                     .properties(string("postCode").minLength(4).maxLength(5)))

    .build();                                                              (4)
1 Obtain a schema builder to configure the schema with a fluent API.
2 Configure required properties.
3 Configure the String-typed firstname field, allowing only luke and han values. Properties can be typed or untyped. Use a static import of JsonSchemaProperty to make the syntax slightly more compact and to get entry points such as string(…).
4 Build the schema object. Use the schema to create either a collection or query documents.

There are already some predefined and strongly typed schema objects (JsonSchemaObject and JsonSchemaProperty) available through static methods on the gateway interfaces. However, you may need to build custom property validation rules, which can be created through the builder API, as the following example shows:

// "birthdate" : { "bsonType": "date" }
JsonSchemaProperty.named("birthdate").ofType(Type.dateType());

// "birthdate" : { "bsonType": "date", "description", "Must be a date" }
JsonSchemaProperty.named("birthdate").with(JsonSchemaObject.of(Type.dateType()).description("Must be a date"));

CollectionOptions provides the entry point to schema support for collections, as the following example shows:

Example 9. Create collection with $jsonSchema
MongoJsonSchema schema = MongoJsonSchema.builder().required("firstname", "lastname").build();

template.createCollection(Person.class, CollectionOptions.empty().schema(schema));

You can use a schema to query any collection for documents that match a given structure defined by a JSON schema, as the following example shows:

Example 10. Query for Documents matching a $jsonSchema
MongoJsonSchema schema = MongoJsonSchema.builder().required("firstname", "lastname").build();

template.find(query(matchingDocumentStructure(schema)), Person.class);

The following table shows the supported JSON schema types:

Table 1. Supported JSON schema types
Schema Type Java Type Schema Properties

untyped

-

description, generated description, enum, allOf, anyOf, oneOf, not

object

Object

required, additionalProperties, properties, minProperties, maxProperties, patternProperties

array

any array except byte[]

uniqueItems, additionalItems, items, minItems, maxItems

string

String

minLength, maxLentgth, pattern

int

int, Integer

multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximum

long

long, Long

multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximum

double

float, Float, double, Double

multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximum

decimal

BigDecimal

multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximum

number

Number

multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximum

binData

byte[]

(none)

boolean

boolean, Boolean

(none)

null

null

(none)

objectId

ObjectId

(none)

date

java.util.Date

(none)

timestamp

BsonTimestamp

(none)

regex

java.util.regex.Pattern

(none)

untyped is a generic type that is inherited by all typed schema types. It provides all untyped schema properties to typed schema types.

For more information, see $jsonSchema.

Fluent Template API

The MongoOperations interface is one of the central components when it comes to more low-level interaction with MongoDB. It offers a wide range of methods covering needs from collection creation, index creation, and CRUD operations to more advanced functionality, such as Map-Reduce and aggregations. You can find multiple overloads for each method. Most of them cover optional or nullable parts of the API.

FluentMongoOperations provides a more narrow interface for the common methods of MongoOperations and provides a more readable, fluent API. The entry points (insert(…), find(…), update(…), and others) follow a natural naming schema based on the operation to be run. Moving on from the entry point, the API is designed to offer only context-dependent methods that lead to a terminating method that invokes the actual MongoOperations counterpart — the all method in the case of the following example:

List<SWCharacter> all = ops.find(SWCharacter.class)
  .inCollection("star-wars")                        (1)
  .all();
1 Skip this step if SWCharacter defines the collection with @Document or if you use the class name as the collection name, which is fine.

Sometimes, a collection in MongoDB holds entities of different types, such as a Jedi within a collection of SWCharacters. To use different types for Query and return value mapping, you can use as(Class<?> targetType) to map results differently, as the following example shows:

List<Jedi> all = ops.find(SWCharacter.class)    (1)
  .as(Jedi.class)                               (2)
  .matching(query(where("jedi").is(true)))
  .all();
1 The query fields are mapped against the SWCharacter type.
2 Resulting documents are mapped into Jedi.
You can directly apply projections to result documents by providing the target type via as(Class<?>).
Using projections allows MongoTemplate to optimize result mapping by limiting the actual response to fields required by the projection target type. This applies as long as the Query itself does not contain any field restriction and the target type is a closed interface or DTO projection.

You can switch between retrieving a single entity and retrieving multiple entities as a List or a Stream through the terminating methods: first(), one(), all(), or stream().

When writing a geo-spatial query with near(NearQuery), the number of terminating methods is altered to include only the methods that are valid for executing a geoNear command in MongoDB (fetching entities as a GeoResult within GeoResults), as the following example shows:

GeoResults<Jedi> results = mongoOps.query(SWCharacter.class)
  .as(Jedi.class)
  .near(alderaan) // NearQuery.near(-73.9667, 40.78).maxDis…
  .all();

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.

Query query = query(where("firstname").is("luke"))
    .comment("find luke")         (1)
    .batchSize(100)                                 (2)
    .slaveOk();                                     (3)
1 The comment propagated to the MongoDB profile log.
2 The number of documents to return in each response batch.
3 Allows querying a replica slave.

On the repository level the @Meta annotation provides means to add query options in a declarative way.

@Meta(comment = "find luke", batchSize = 100, flags = { SLAVE_OK })
List<Person> findByFirstname(String firstname);

Running an Example

The following example shows how to query by example when using a repository (of Person objects, in this case):

Example 11. Query by Example using a repository
public interface PersonRepository extends QueryByExampleExecutor<Person> {

}

public class PersonService {

  @Autowired PersonRepository personRepository;

  public List<Person> findPeople(Person probe) {
    return personRepository.findAll(Example.of(probe));
  }
}

An Example containing an untyped ExampleSpec uses the Repository type and its collection name. Typed ExampleSpec instances use their type as the result type and the collection name from the Repository instance.

When including null values in the ExampleSpec, Spring Data Mongo uses embedded document matching instead of dot notation property matching. Doing so forces exact document matching for all property values and the property order in the embedded document.

Spring Data MongoDB provides support for the following matching options:

Table 2. StringMatcher options
Matching Logical result

DEFAULT (case-sensitive)

{"firstname" : firstname}

DEFAULT (case-insensitive)

{"firstname" : { $regex: firstname, $options: 'i'}}

EXACT (case-sensitive)

{"firstname" : { $regex: /^firstname$/}}

EXACT (case-insensitive)

{"firstname" : { $regex: /^firstname$/, $options: 'i'}}

STARTING (case-sensitive)

{"firstname" : { $regex: /^firstname/}}

STARTING (case-insensitive)

{"firstname" : { $regex: /^firstname/, $options: 'i'}}

ENDING (case-sensitive)

{"firstname" : { $regex: /firstname$/}}

ENDING (case-insensitive)

{"firstname" : { $regex: /firstname$/, $options: 'i'}}

CONTAINING (case-sensitive)

{"firstname" : { $regex: /.*firstname.*/}}

CONTAINING (case-insensitive)

{"firstname" : { $regex: /.*firstname.*/, $options: 'i'}}

REGEX (case-sensitive)

{"firstname" : { $regex: /firstname/}}

REGEX (case-insensitive)

{"firstname" : { $regex: /firstname/, $options: 'i'}}

Untyped Example

By default Example is strictly typed. This means that the mapped query has an included type match, restricting it to probe assignable types. For example, when sticking with the default type key (_class), the query has restrictions such as (_class : { $in : [ com.acme.Person] }).

By using the UntypedExampleMatcher, it is possible to bypass the default behavior and skip the type restriction. So, as long as field names match, nearly any domain type can be used as the probe for creating the reference, as the following example shows:

Example 12. Untyped Example Query
class JustAnArbitraryClassWithMatchingFieldName {
  @Field("lastname") String value;
}

JustAnArbitraryClassWithMatchingFieldNames probe = new JustAnArbitraryClassWithMatchingFieldNames();
probe.value = "stark";

Example example = Example.of(probe, UntypedExampleMatcher.matching());

Query query = new Query(new Criteria().alike(example));
List<Person> result = template.find(query, Person.class);