You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3484 lines
146 KiB
3484 lines
146 KiB
<?xml version="1.0" encoding="UTF-8"?> |
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" |
|
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"> |
|
<chapter id="mongo.core"> |
|
<title>MongoDB support</title> |
|
|
|
<para>The MongoDB support contains a wide range of features which are |
|
summarized below.</para> |
|
|
|
<itemizedlist> |
|
<listitem> |
|
<para>Spring configuration support using Java based @Configuration |
|
classes or an XML namespace for a Mongo driver instance and replica |
|
sets</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>MongoTemplate helper class that increases productivity performing |
|
common Mongo operations. Includes integrated object mapping between |
|
documents and POJOs.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Exception translation into Spring's portable Data Access Exception |
|
hierarchy</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Feature Rich Object Mapping integrated with Spring's Conversion |
|
Service</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Annotation based mapping metadata but extensible to support other |
|
metadata formats</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Persistence and mapping lifecycle events</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Java based Query, Criteria, and Update DSLs</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Automatic implementation of Repository interfaces including |
|
support for custom finder methods.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>QueryDSL integration to support type-safe queries.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Cross-store persistance - support for JPA Entities with fields |
|
transparently persisted/retrieved using MongoDB</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Log4j log appender</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>GeoSpatial integration</para> |
|
</listitem> |
|
</itemizedlist> |
|
|
|
<para>For most tasks you will find yourself using |
|
<classname>MongoTemplate</classname> or the Repository support that both |
|
leverage the rich mapping functionality. MongoTemplate is the place to look |
|
for accessing functionality such as incrementing counters or ad-hoc CRUD |
|
operations. MongoTemplate also provides callback methods so that it is easy |
|
for you to get a hold of the low level API artifacts such as |
|
<literal>org.mongo.DB</literal> to communicate directly with MongoDB. The |
|
goal with naming conventions on various API artifacts is to copy those in |
|
the base MongoDB Java driver so you can easily map your existing knowledge |
|
onto the Spring APIs.</para> |
|
|
|
<section id="mongodb-getting-started"> |
|
<title>Getting Started</title> |
|
|
|
<para>Spring MongoDB support requires MongoDB 1.4 or higher and Java SE 5 |
|
or higher. The latest production release (2.4.9 as of this writing) is |
|
recommended. An easy way to bootstrap setting up a working environment is |
|
to create a Spring based project in <ulink |
|
url="http://spring.io/tools/sts">STS</ulink>.</para> |
|
|
|
<para>First you need to set up a running Mongodb server. Refer to the |
|
<ulink url="http://docs.mongodb.org/manual/core/introduction/">Mongodb |
|
Quick Start guide</ulink> for an explanation on how to startup a MongoDB |
|
instance. Once installed starting MongoDB is typically a matter of |
|
executing the following command: |
|
<literal>MONGO_HOME/bin/mongod</literal></para> |
|
|
|
<para>To create a Spring project in STS go to File -> New -> Spring |
|
Template Project -> Simple Spring Utility Project -> press Yes when |
|
prompted. Then enter a project and a package name such as |
|
org.spring.mongodb.example.</para> |
|
|
|
<para>Then add the following to pom.xml dependencies section.</para> |
|
|
|
<programlisting lang="" language="xml"><dependencies> |
|
|
|
<!-- other dependency elements omitted --> |
|
|
|
<dependency> |
|
<groupId>org.springframework.data</groupId> |
|
<artifactId>spring-data-mongodb</artifactId> |
|
<version>1.4.2.RELEASE</version> |
|
</dependency> |
|
|
|
</dependencies></programlisting> |
|
|
|
<para>Also change the version of Spring in the pom.xml to be</para> |
|
|
|
<programlisting lang="" language="xml"><spring.framework.version>3.2.8.RELEASE</spring.framework.version></programlisting> |
|
|
|
<para>You will also need to add the location of the Spring Milestone |
|
repository for maven to your pom.xml which is at the same level of your |
|
<dependencies/> element</para> |
|
|
|
<programlisting language="xml"><repositories> |
|
<repository> |
|
<id>spring-milestone</id> |
|
<name>Spring Maven MILESTONE Repository</name> |
|
<url>http://repo.spring.io/libs-milestone</url> |
|
</repository> |
|
</repositories></programlisting> |
|
|
|
<para>The repository is also <ulink |
|
url="http://shrub.appspot.com/maven.springframework.org/milestone/org/springframework/data/">browseable |
|
here</ulink>.</para> |
|
|
|
<para>You may also want to set the logging level to <code>DEBUG</code> to |
|
see some additional information, edit the log4j.properties file to |
|
have</para> |
|
|
|
<programlisting>log4j.category.org.springframework.data.document.mongodb=DEBUG |
|
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %40.40c:%4L - %m%n</programlisting> |
|
|
|
<para>Create a simple Person class to persist</para> |
|
|
|
<programlisting language="java">package org.spring.mongodb.example; |
|
|
|
public class Person { |
|
|
|
private String id; |
|
private String name; |
|
private int age; |
|
|
|
public Person(String name, int age) { |
|
this.name = name; |
|
this.age = age; |
|
} |
|
|
|
public String getId() { |
|
return id; |
|
} |
|
public String getName() { |
|
return name; |
|
} |
|
public int getAge() { |
|
return age; |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]"; |
|
} |
|
|
|
}</programlisting> |
|
|
|
<para>And a main application to run</para> |
|
|
|
<programlisting language="java">package org.spring.mongodb.example; |
|
|
|
import static org.springframework.data.mongodb.core.query.Criteria.where; |
|
|
|
import org.apache.commons.logging.Log; |
|
import org.apache.commons.logging.LogFactory; |
|
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.Mongo; |
|
|
|
public class MongoApp { |
|
|
|
private static final Log log = LogFactory.getLog(MongoApp.class); |
|
|
|
public static void main(String[] args) throws Exception { |
|
|
|
MongoOperations mongoOps = new MongoTemplate(new Mongo(), "database"); |
|
|
|
mongoOps.insert(new Person("Joe", 34)); |
|
|
|
log.info(mongoOps.findOne(new Query(where("name").is("Joe")), Person.class)); |
|
|
|
mongoOps.dropCollection("person"); |
|
} |
|
}</programlisting> |
|
|
|
<para>This will produce the following output</para> |
|
|
|
<programlisting>10:01:32,062 DEBUG apping.MongoPersistentEntityIndexCreator: 80 - Analyzing class class org.spring.example.Person for index information. |
|
10:01:32,265 DEBUG ramework.data.mongodb.core.MongoTemplate: 631 - insert DBObject containing fields: [_class, age, name] in collection: Person |
|
10:01:32,765 DEBUG ramework.data.mongodb.core.MongoTemplate:1243 - findOne using query: { "name" : "Joe"} in db.collection: database.Person |
|
10:01:32,953 INFO org.spring.mongodb.example.MongoApp: 25 - Person [id=4ddbba3c0be56b7e1b210166, name=Joe, age=34] |
|
10:01:32,984 DEBUG ramework.data.mongodb.core.MongoTemplate: 375 - Dropped collection [database.person]</programlisting> |
|
|
|
<para>Even in this simple example, there are few things to take notice |
|
of</para> |
|
|
|
<itemizedlist> |
|
<listitem> |
|
<para>You can instantiate the central helper class of Spring Mongo, |
|
<link |
|
linkend="mongo-template"><classname>MongoTemplate</classname></link>, |
|
using the standard <classname>com.mongodb.Mongo</classname> object and |
|
the name of the database to use.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>The mapper works against standard POJO objects without the need |
|
for any additional metadata (though you can optionally provide that |
|
information. See <link linkend="mongo.mapping">here</link>.).</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Conventions are used for handling the id field, converting it to |
|
be a ObjectId when stored in the database.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Mapping conventions can use field access. Notice the Person |
|
class has only getters.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>If the constructor argument names match the field names of the |
|
stored document, they will be used to instantiate the object</para> |
|
</listitem> |
|
</itemizedlist> |
|
</section> |
|
|
|
<section id="mongo.examples-repo"> |
|
<title>Examples Repository</title> |
|
|
|
<para>There is an <ulink |
|
url="https://github.com/spring-projects/spring-data-document-examples">github |
|
repository with several examples</ulink> that you can download and play |
|
around with to get a feel for how the library works.</para> |
|
</section> |
|
|
|
<section id="mongodb-connectors"> |
|
<title>Connecting to MongoDB with Spring</title> |
|
|
|
<para>One of the first tasks when using MongoDB and Spring is to create a |
|
<classname>com.mongodb.Mongo</classname> object using the IoC container. |
|
There are two main ways to do this, either using Java based bean metadata |
|
or XML based bean metadata. These are discussed in the following sections. |
|
<note> |
|
<para>For those not familiar with how to configure the Spring |
|
container using Java based bean metadata instead of XML based metadata |
|
see the high level introduction in the reference docs <ulink |
|
url="http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/new-in-3.0.html#new-java-configuration" |
|
userlevel="">here </ulink> as well as the detailed documentation<ulink |
|
url="http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-java-instantiating-container"> |
|
here</ulink>.</para> |
|
</note></para> |
|
|
|
<section id="mongo.mongo-java-config"> |
|
<title>Registering a Mongo instance using Java based metadata</title> |
|
|
|
<para>An example of using Java based bean metadata to register an |
|
instance of a <classname>com.mongodb.Mongo</classname> is shown below |
|
<example> |
|
<title>Registering a com.mongodb.Mongo object using Java based bean |
|
metadata</title> |
|
|
|
<programlisting language="java">@Configuration |
|
public class AppConfig { |
|
|
|
/* |
|
* Use the standard Mongo driver API to create a com.mongodb.Mongo instance. |
|
*/ |
|
public @Bean Mongo mongo() throws UnknownHostException { |
|
return new Mongo("localhost"); |
|
} |
|
} </programlisting> |
|
</example></para> |
|
|
|
<para>This approach allows you to use the standard |
|
<classname>com.mongodb.Mongo</classname> API that you may already be |
|
used to using but also pollutes the code with the UnknownHostException |
|
checked exception. The use of the checked exception is not desirable as |
|
Java based bean metadata uses methods as a means to set object |
|
dependencies, making the calling code cluttered.</para> |
|
|
|
<para>An alternative is to register an instance of |
|
<classname>com.mongodb.Mongo</classname> instance with the container |
|
using Spring's<interfacename> MongoFactoryBean</interfacename>. As |
|
compared to instantiating a <classname>com.mongodb.Mongo</classname> |
|
instance directly, the FactoryBean approach does not throw a checked |
|
exception and has the added advantage of also providing the container |
|
with an ExceptionTranslator implementation that translates MongoDB |
|
exceptions to exceptions in Spring's portable |
|
<classname>DataAccessException</classname> hierarchy for data access |
|
classes annoated with the <literal>@Repository</literal> annotation. |
|
This hierarchy and use of <literal>@Repository</literal> is described in |
|
<ulink |
|
url="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/dao.html">Spring's |
|
DAO support features</ulink>.</para> |
|
|
|
<para>An example of a Java based bean metadata that supports exception |
|
translation on <classname>@Repository</classname> annotated classes is |
|
shown below:</para> |
|
|
|
<example> |
|
<title>Registering a com.mongodb.Mongo object using Spring's |
|
MongoFactoryBean and enabling Spring's exception translation |
|
support</title> |
|
|
|
<programlisting language="java">@Configuration |
|
public class AppConfig { |
|
|
|
/* |
|
* Factory bean that creates the com.mongodb.Mongo instance |
|
*/ |
|
public @Bean MongoFactoryBean mongo() { |
|
MongoFactoryBean mongo = new MongoFactoryBean(); |
|
mongo.setHost("localhost"); |
|
return mongo; |
|
} |
|
} |
|
</programlisting> |
|
|
|
<para>To access the <classname>com.mongodb.Mongo</classname> object |
|
created by the <classname>MongoFactoryBean</classname> in other |
|
<literal>@Configuration</literal> or your own classes, use a |
|
"<literal>private @Autowired Mongo mongo;</literal>" field.</para> |
|
</example> |
|
</section> |
|
|
|
<section id="mongo.mongo-xml-config"> |
|
<title>Registering a Mongo instance using XML based metadata</title> |
|
|
|
<para>While you can use Spring's traditional |
|
<literal><beans/></literal> XML namespace to register an instance |
|
of <classname>com.mongodb.Mongo</classname> with the container, the XML |
|
can be quite verbose as it is general purpose. XML namespaces are a |
|
better alternative to configuring commonly used objects such as the |
|
Mongo instance. The mongo namespace alows you to create a Mongo instance |
|
server location, replica-sets, and options.</para> |
|
|
|
<para>To use the Mongo namespace elements you will need to reference the |
|
Mongo schema:</para> |
|
|
|
<example> |
|
<title>XML schema to configure MongoDB</title> |
|
|
|
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xmlns:mongo="http://www.springframework.org/schema/data/mongo" |
|
xsi:schemaLocation= |
|
"http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context-3.0.xsd |
|
<emphasis role="bold">http://www.springframework.org/schema/data/mongo |
|
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd</emphasis> |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> |
|
|
|
<!-- Default bean name is 'mongo' --> |
|
<emphasis role="bold"><mongo:mongo host="localhost" port="27017"/></emphasis> |
|
|
|
</beans> |
|
</programlisting> |
|
</example> |
|
|
|
<para>A more advanced configuration with MongoOptions is shown below |
|
(note these are not recommended values)</para> |
|
|
|
<example> |
|
<title>XML schema to configure a com.mongodb.Mongo object with |
|
MongoOptions</title> |
|
|
|
<programlisting language="xml"><beans> |
|
|
|
<mongo:mongo host="localhost" port="27017"> |
|
<mongo:options connections-per-host="8" |
|
threads-allowed-to-block-for-connection-multiplier="4" |
|
connect-timeout="1000" |
|
max-wait-time="1500}" |
|
auto-connect-retry="true" |
|
socket-keep-alive="true" |
|
socket-timeout="1500" |
|
slave-ok="true" |
|
write-number="1" |
|
write-timeout="0" |
|
write-fsync="true"/> |
|
</mongo:mongo/> |
|
|
|
</beans> |
|
</programlisting> |
|
</example> |
|
|
|
<para>A configuration using replica sets is shown below. <example> |
|
<title>XML schema to configure com.mongodb.Mongo object with Replica |
|
Sets</title> |
|
|
|
<programlisting language="xml"><mongo:mongo id="replicaSetMongo" replica-set="127.0.0.1:27017,localhost:27018"/> </programlisting> |
|
</example></para> |
|
</section> |
|
|
|
<section id="mongo.mongo-db-factory"> |
|
<title>The MongoDbFactory interface</title> |
|
|
|
<para>While <classname>com.mongodb.Mongo</classname> is the entry point |
|
to the MongoDB driver API, connecting to a specific MongoDB database |
|
instance requires additional information such as the database name and |
|
an optional username and password. With that information you can obtain |
|
a com.mongodb.DB object and access all the functionality of a specific |
|
MongoDB database instance. Spring provides the |
|
<classname>org.springframework.data.mongodb.core.MongoDbFactory</classname> |
|
interface shown below to bootstrap connectivity to the database.</para> |
|
|
|
<programlisting language="java">public interface MongoDbFactory { |
|
|
|
DB getDb() throws DataAccessException; |
|
|
|
DB getDb(String dbName) throws DataAccessException; |
|
}</programlisting> |
|
|
|
<para>The following sections show how you can use the container with |
|
either Java or the XML based metadata to configure an instance of the |
|
<classname>MongoDbFactory</classname> interface. In turn, you can use |
|
the <classname>MongoDbFactory</classname> instance to configure |
|
MongoTemplate.</para> |
|
|
|
<para>The class |
|
<classname>org.springframework.data.mongodb.core.SimpleMongoDbFactory</classname> |
|
provides implements the MongoDbFactory interface and is created with a |
|
standard <classname>com.mongodb.Mongo</classname> instance, the database |
|
name and an optional |
|
<classname>org.springframework.data.authentication.UserCredentials</classname> |
|
constructor argument.</para> |
|
|
|
<para>Instead of using the IoC container to create an instance of |
|
MongoTemplate, you can just use them in standard Java code as shown |
|
below.</para> |
|
|
|
<programlisting language="java">public class MongoApp { |
|
|
|
private static final Log log = LogFactory.getLog(MongoApp.class); |
|
|
|
public static void main(String[] args) throws Exception { |
|
|
|
MongoOperations mongoOps = new MongoTemplate(<emphasis role="bold">new SimpleMongoDbFactory(new Mongo(), "database")</emphasis>); |
|
|
|
mongoOps.insert(new Person("Joe", 34)); |
|
|
|
log.info(mongoOps.findOne(new Query(where("name").is("Joe")), Person.class)); |
|
|
|
mongoOps.dropCollection("person"); |
|
} |
|
}</programlisting> |
|
|
|
<para>The code in bold highlights the use of SimpleMongoDbFactory and is |
|
the only difference between the listing shown in the <link lang="" |
|
linkend="mongodb-getting-started" os="">getting started |
|
section</link>.</para> |
|
</section> |
|
|
|
<section id="mongo.mongo-db-factory-java"> |
|
<title>Registering a MongoDbFactory instance using Java based |
|
metadata</title> |
|
|
|
<para>To register a MongoDbFactory instance with the container, you |
|
write code much like what was highlighted in the previous code listing. |
|
A simple example is shown below</para> |
|
|
|
<programlisting language="java">@Configuration |
|
public class MongoConfiguration { |
|
|
|
public @Bean MongoDbFactory mongoDbFactory() throws Exception { |
|
return new SimpleMongoDbFactory(new Mongo(), "database"); |
|
} |
|
}</programlisting> |
|
|
|
<para>To define the username and password create an instance of |
|
<classname>org.springframework.data.authentication.UserCredentials</classname> |
|
and pass it into the constructor as shown below. This listing also shows |
|
using <classname>MongoDbFactory</classname> register an instance of |
|
MongoTemplate with the container.</para> |
|
|
|
<programlisting language="java">@Configuration |
|
public class MongoConfiguration { |
|
|
|
public @Bean MongoDbFactory mongoDbFactory() throws Exception { |
|
UserCredentials userCredentials = new UserCredentials("joe", "secret"); |
|
return new SimpleMongoDbFactory(new Mongo(), "database", userCredentials); |
|
} |
|
|
|
public @Bean MongoTemplate mongoTemplate() throws Exception { |
|
return new MongoTemplate(mongoDbFactory()); |
|
} |
|
}</programlisting> |
|
</section> |
|
|
|
<section id="mongo.mongo-db-factory-xml"> |
|
<title>Registering a MongoDbFactory instance using XML based |
|
metadata</title> |
|
|
|
<para>The mongo namespace provides a convient way to create a |
|
<classname>SimpleMongoDbFactory</classname> as compared to using |
|
the<literal><beans/></literal> namespace. Simple usage is shown |
|
below</para> |
|
|
|
<programlisting language="xml"><mongo:db-factory dbname="database"></programlisting> |
|
|
|
<para>In the above example a <classname>com.mongodb.Mongo</classname> |
|
instance is created using the default host and port number. The |
|
<classname>SimpleMongoDbFactory</classname> registered with the |
|
container is identified by the id 'mongoDbFactory' unless a value for |
|
the id attribute is specified.</para> |
|
|
|
<para>You can also provide the host and port for the underlying |
|
<classname>com.mongodb.Mongo</classname> instance as shown below, in |
|
addition to username and password for the database.</para> |
|
|
|
<programlisting language="xml"><mongo:db-factory id="anotherMongoDbFactory" |
|
host="localhost" |
|
port="27017" |
|
dbname="database" |
|
username="joe" |
|
password="secret"/></programlisting> |
|
|
|
<para>If you need to configure additional options on the |
|
<classname>com.mongodb.Mongo</classname> instance that is used to create |
|
a <classname>SimpleMongoDbFactory</classname> you can refer to an |
|
existing bean using the <literal>mongo-ref</literal> attribute as shown |
|
below. To show another common usage pattern, this listing show the use |
|
of a property placeholder to parameterise the configuration and creating |
|
<classname>MongoTemplate</classname>.</para> |
|
|
|
<programlisting language="xml"><context:property-placeholder location="classpath:/com/myapp/mongodb/config/mongo.properties"/> |
|
|
|
<mongo:mongo host="${mongo.host}" port="${mongo.port}"> |
|
<mongo:options |
|
connections-per-host="${mongo.connectionsPerHost}" |
|
threads-allowed-to-block-for-connection-multiplier="${mongo.threadsAllowedToBlockForConnectionMultiplier}" |
|
connect-timeout="${mongo.connectTimeout}" |
|
max-wait-time="${mongo.maxWaitTime}" |
|
auto-connect-retry="${mongo.autoConnectRetry}" |
|
socket-keep-alive="${mongo.socketKeepAlive}" |
|
socket-timeout="${mongo.socketTimeout}" |
|
slave-ok="${mongo.slaveOk}" |
|
write-number="1" |
|
write-timeout="0" |
|
write-fsync="true"/> |
|
</mongo:mongo> |
|
|
|
<mongo:db-factory dbname="database" mongo-ref="mongo"/> |
|
|
|
<bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> |
|
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/> |
|
</bean></programlisting> |
|
</section> |
|
</section> |
|
|
|
<section id="mongo.auditing"> |
|
<title>General auditing configuration</title> |
|
|
|
<para>Activating auditing functionality is just a matter of adding the |
|
Spring Data Mongo <literal>auditing</literal> namespace element to your |
|
configuration:</para> |
|
|
|
<example> |
|
<title>Activating auditing using XML configuration</title> |
|
|
|
<programlisting language="xml"><mongo:auditing mapping-context-ref="customMappingContext" auditor-aware-ref="yourAuditorAwareImpl"/></programlisting> |
|
</example> |
|
|
|
<para>Since Spring Data MongoDB 1.4 auditing can be enabled by annotating |
|
a configuration class with the <classname>@EnableMongoAuditing</classname> |
|
annotation.</para> |
|
|
|
<example> |
|
<title>Activating auditing using JavaConfig</title> |
|
|
|
<programlisting language="java">@Configuration |
|
@EnableMongoAuditing |
|
class Config { |
|
|
|
@Bean |
|
public AuditorAware<AuditableUser> myAuditorProvider() { |
|
return new AuditorAwareImpl(); |
|
} |
|
}</programlisting> |
|
</example> |
|
|
|
<para>If you expose a bean of type |
|
<interfacename>AuditorAware</interfacename> to the |
|
<interfacename>ApplicationContext</interfacename>, the auditing |
|
infrastructure will pick it up automatically and use it to determine the |
|
current user to be set on domain types. If you have multiple |
|
implementations registered in the |
|
<interfacename>ApplicationContext</interfacename>, you can select the one |
|
to be used by explicitly setting the <code>auditorAwareRef</code> |
|
attribute of <interfacename>@EnableJpaAuditing</interfacename>.</para> |
|
</section> |
|
|
|
<section id="mongo-template"> |
|
<title>Introduction to MongoTemplate</title> |
|
|
|
<para>The class <classname>MongoTemplate</classname>, located in the |
|
package <literal>org.springframework.data.document.mongodb</literal>, is |
|
the central class of the Spring's MongoDB support providng a rich feature |
|
set to interact with the database. The template offers convenience |
|
operations to create, update, delete and query for MongoDB documents and |
|
provides a mapping between your domain objects and MongoDB |
|
documents.</para> |
|
|
|
<note> |
|
<para>Once configured, <classname>MongoTemplate</classname> is |
|
thread-safe and can be reused across multiple instances.</para> |
|
</note> |
|
|
|
<para>The mapping between MongoDB documents and domain classes is done by |
|
delegating to an implementation of the interface |
|
<interfacename>MongoConverter</interfacename>. Spring provides two |
|
implementations, <classname>SimpleMappingConverter</classname> and |
|
<classname>MongoMappingConverter</classname>, but you can also write your |
|
own converter. Please refer to the section on MongoCoverters for more |
|
detailed information.</para> |
|
|
|
<para>The <classname>MongoTemplate</classname> class implements the |
|
interface <interfacename>MongoOperations</interfacename>. In as much as |
|
possible, the methods on <interfacename>MongoOperations</interfacename> |
|
are named after methods available on the MongoDB driver |
|
<classname>Collection</classname> object as as to make the API familiar to |
|
existing MongoDB developers who are used to the driver API. For example, |
|
you will find methods such as "find", "findAndModify", "findOne", |
|
"insert", "remove", "save", "update" and "updateMulti". The design goal |
|
was to make it as easy as possible to transition between the use of the |
|
base MongoDB driver and <interfacename>MongoOperations</interfacename>. A |
|
major difference in between the two APIs is that MongOperations can be |
|
passed domain objects instead of <classname>DBObject</classname> and there |
|
are fluent APIs for <classname>Query</classname>, |
|
<classname>Criteria</classname>, and <classname>Update</classname> |
|
operations instead of populating a <classname>DBObject</classname> to |
|
specify the parameters for those operatiosn.</para> |
|
|
|
<note> |
|
<para>The preferred way to reference the operations on |
|
<classname>MongoTemplate</classname> instance is via its interface |
|
<interfacename>MongoOperations</interfacename>.</para> |
|
</note> |
|
|
|
<para>The default converter implementation used by |
|
<classname>MongoTemplate</classname> is MongoMappingConverter. While the |
|
<classname>MongoMappingConverter</classname> can make use of additional |
|
metadata to specify the mapping of objects to documents it is also capable |
|
of converting objects that contain no additonal metadata by using some |
|
conventions for the mapping of IDs and collection names. These conventions |
|
as well as the use of mapping annotations is explained in the <link |
|
linkend="mongo.mapping">Mapping chapter</link>.<note> |
|
<para>In the M2 release <classname>SimpleMappingConverter</classname>, |
|
was the default and this class is now deprecated as its functionality |
|
has been subsumed by the MongoMappingConverter.</para> |
|
</note></para> |
|
|
|
<para>Another central feature of MongoTemplate is exception translation of |
|
exceptions thrown in the MongoDB Java driver into Spring's portable Data |
|
Access Exception hierarchy. Refer to the section on <link |
|
linkend="mongo.exception">exception translation</link> for more |
|
information.</para> |
|
|
|
<para>While there are many convenience methods on |
|
<classname>MongoTemplate</classname> to help you easily perform common |
|
tasks if you should need to access the MongoDB driver API directly to |
|
access functionality not explicitly exposed by the MongoTemplate you can |
|
use one of several Execute callback methods to access underlying driver |
|
APIs. The execute callbacks will give you a reference to either a |
|
<classname>com.mongodb.Collection</classname> or a |
|
<classname>com.mongodb.DB</classname> object. Please see the section |
|
<ulink url="mongo.executioncallback">Execution Callbacks</ulink> for more |
|
information.</para> |
|
|
|
<para>Now let's look at a examples of how to work with the |
|
<classname>MongoTemplate</classname> in the context of the Spring |
|
container.</para> |
|
|
|
<section id="mongo-template.instantiating" label=" "> |
|
<title>Instantiating MongoTemplate</title> |
|
|
|
<para>You can use Java to create and register an instance of |
|
MongoTemplate as shown below.</para> |
|
|
|
<example> |
|
<title>Registering a com.mongodb.Mongo object and enabling Spring's |
|
exception translation support</title> |
|
|
|
<programlisting language="java">@Configuration |
|
public class AppConfig { |
|
|
|
public @Bean Mongo mongo() throws Exception { |
|
return new Mongo("localhost"); |
|
} |
|
|
|
public @Bean MongoTemplate mongoTemplate() throws Exception { |
|
return new MongoTemplate(mongo(), "mydatabase"); |
|
} |
|
} |
|
</programlisting> |
|
</example> |
|
|
|
<para>There are several overloaded constructors of MongoTemplate. These |
|
are</para> |
|
|
|
<itemizedlist> |
|
<listitem> |
|
<para><emphasis role="bold">MongoTemplate </emphasis> |
|
<literal>(Mongo mongo, String databaseName)</literal> - takes the |
|
<classname>com.mongodb.Mongo</classname> object and the default |
|
database name to operate against.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">MongoTemplate </emphasis> |
|
<literal>(Mongo mongo, String databaseName, UserCredentials |
|
userCredentials) </literal> - adds the username and password for |
|
authenticating with the database.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">MongoTemplate</emphasis> |
|
<literal>(MongoDbFactory mongoDbFactory)</literal> - takes a |
|
MongoDbFactory object that encapsulated the |
|
<classname>com.mongodb.Mongo</classname> object, database name, and |
|
username and password.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">MongoTemplate </emphasis> |
|
<literal>(MongoDbFactory mongoDbFactory, MongoConverter |
|
mongoConverter) </literal> - adds a MongoConverter to use for |
|
mapping.</para> |
|
</listitem> |
|
</itemizedlist> |
|
|
|
<para>You can also configure a MongoTemplate using Spring's XML |
|
<beans/> schema.</para> |
|
|
|
<programlisting language="java"> <mongo:mongo host="localhost" port="27017"/> |
|
|
|
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> |
|
<constructor-arg ref="mongo"/> |
|
<constructor-arg name="databaseName" value="geospatial"/> |
|
</bean></programlisting> |
|
|
|
<para>Other optional properties that you might like to set when creating |
|
a <classname>MongoTemplate</classname> are the default |
|
<classname>WriteResultCheckingPolicy</classname>, |
|
<classname>WriteConcern</classname>, and |
|
<classname>ReadPreference</classname>.</para> |
|
|
|
<note> |
|
<para>The preferred way to reference the operations on |
|
<classname>MongoTemplate</classname> instance is via its interface |
|
<interfacename>MongoOperations</interfacename>.</para> |
|
</note> |
|
|
|
<section id="mongo-template.writeresultchecking"> |
|
<title>WriteResultChecking Policy</title> |
|
|
|
<para>When in development it is very handy to either log or throw an |
|
exception if the <classname>com.mongodb.WriteResult</classname> |
|
returned from any MongoDB operation contains an error. It is quite |
|
common to forget to do this during development and then end up with an |
|
application that looks like it runs successfully but in fact the |
|
database was not modified according to your expectations. Set |
|
MongoTemplate's <property>WriteResultChecking</property> property to |
|
an enum with the following values, LOG, EXCEPTION, or NONE to either |
|
log the error, throw and exception or do nothing. The default is to |
|
use a <literal>WriteResultChecking</literal> value of NONE.</para> |
|
</section> |
|
|
|
<section id="mongo-template.writeconcern"> |
|
<title>WriteConcern</title> |
|
|
|
<para>You can set the <classname>com.mongodb.WriteConcern</classname> |
|
property that the <classname>MongoTemplate</classname> will use for |
|
write operations if it has not yet been specified via the driver at a |
|
higher level such as <classname>com.mongodb.Mongo</classname>. If |
|
MongoTemplate's <classname>WriteConcern</classname> property is not |
|
set it will default to the one set in the MongoDB driver's DB or |
|
Collection setting.</para> |
|
</section> |
|
|
|
<section id="mongo-template.writeconcernresolver"> |
|
<title>WriteConcernResolver</title> |
|
|
|
<para>For more advanced cases where you want to set different |
|
<classname>WriteConcern</classname> values on a per-operation basis |
|
(for remove, update, insert and save operations), a strategy interface |
|
called <interfacename>WriteConcernResolver</interfacename> can be |
|
configured on <classname>MongoTemplate</classname>. Since |
|
<classname>MongoTemplate</classname> is used to persist POJOs, the |
|
<interfacename>WriteConcernResolver</interfacename> lets you create a |
|
policy that can map a specific POJO class to a |
|
<classname>WriteConcern</classname> value. The |
|
<interfacename>WriteConcernResolver</interfacename> interface is shown |
|
below.</para> |
|
|
|
<programlisting language="java">public interface WriteConcernResolver { |
|
WriteConcern resolve(MongoAction action); |
|
}</programlisting> |
|
|
|
<para>The passed in argument, MongoAction, is what you use to |
|
determine the <classname>WriteConcern</classname> value to be used or |
|
to use the value of the Template itself as a default. |
|
<classname>MongoAction</classname> contains the collection name being |
|
written to, the <classname>java.lang.Class</classname> of the POJO, |
|
the converted <classname>DBObject</classname>, as well as the |
|
operation as an enumeration |
|
(<classname>MongoActionOperation</classname>: REMOVE, UPDATE, INSERT, |
|
INSERT_LIST, SAVE) and a few other pieces of contextual information. |
|
For example,</para> |
|
|
|
<programlisting>private class MyAppWriteConcernResolver implements WriteConcernResolver { |
|
|
|
public WriteConcern resolve(MongoAction action) { |
|
if (action.getEntityClass().getSimpleName().contains("Audit")) { |
|
return WriteConcern.NONE; |
|
} else if (action.getEntityClass().getSimpleName().contains("Metadata")) { |
|
return WriteConcern.JOURNAL_SAFE; |
|
} |
|
return action.getDefaultWriteConcern(); |
|
} |
|
}</programlisting> |
|
</section> |
|
</section> |
|
</section> |
|
|
|
<section id="mongo-template.save-update-remove"> |
|
<title>Saving, Updating, and Removing Documents</title> |
|
|
|
<para><classname>MongoTemplate</classname> provides a simple way for you |
|
to save, update, and delete your domain objects and map those objects to |
|
documents stored in MongoDB.</para> |
|
|
|
<para>Given a simple class such as Person</para> |
|
|
|
<programlisting language="java">public class Person { |
|
|
|
private String id; |
|
private String name; |
|
private int age; |
|
|
|
public Person(String name, int age) { |
|
this.name = name; |
|
this.age = age; |
|
} |
|
|
|
public String getId() { |
|
return id; |
|
} |
|
public String getName() { |
|
return name; |
|
} |
|
public int getAge() { |
|
return age; |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]"; |
|
} |
|
|
|
} |
|
</programlisting> |
|
|
|
<para>You can save, update and delete the object as shown below.</para> |
|
|
|
<note> |
|
<para><interfacename>MongoOperations</interfacename> is the interface |
|
that <classname>MongoTemplate</classname> implements.</para> |
|
</note> |
|
|
|
<programlisting language="java">package org.spring.example; |
|
|
|
import static org.springframework.data.mongodb.core.query.Criteria.where; |
|
import static org.springframework.data.mongodb.core.query.Update.update; |
|
import static org.springframework.data.mongodb.core.query.Query.query; |
|
|
|
import java.util.List; |
|
|
|
import org.apache.commons.logging.Log; |
|
import org.apache.commons.logging.LogFactory; |
|
import org.springframework.data.mongodb.core.MongoOperations; |
|
import org.springframework.data.mongodb.core.MongoTemplate; |
|
import org.springframework.data.mongodb.core.SimpleMongoDbFactory; |
|
|
|
import com.mongodb.Mongo; |
|
|
|
public class MongoApp { |
|
|
|
private static final Log log = LogFactory.getLog(MongoApp.class); |
|
|
|
public static void main(String[] args) throws Exception { |
|
|
|
MongoOperations mongoOps = new MongoTemplate(new SimpleMongoDbFactory(new Mongo(), "database")); |
|
|
|
Person p = new Person("Joe", 34); |
|
|
|
// Insert is used to initially store the object into the database. |
|
mongoOps.insert(p); |
|
log.info("Insert: " + p); |
|
|
|
// Find |
|
p = mongoOps.findById(p.getId(), Person.class); |
|
log.info("Found: " + p); |
|
|
|
// Update |
|
mongoOps.updateFirst(query(where("name").is("Joe")), update("age", 35), Person.class); |
|
p = mongoOps.findOne(query(where("name").is("Joe")), Person.class); |
|
log.info("Updated: " + p); |
|
|
|
// Delete |
|
mongoOps.remove(p); |
|
|
|
// Check that deletion worked |
|
List<Person> people = mongoOps.findAll(Person.class); |
|
log.info("Number of people = : " + people.size()); |
|
|
|
|
|
mongoOps.dropCollection(Person.class); |
|
} |
|
} |
|
</programlisting> |
|
|
|
<para>This would produce the following log output (including debug |
|
messages from <classname>MongoTemplate</classname> itself)</para> |
|
|
|
<programlisting>DEBUG apping.MongoPersistentEntityIndexCreator: 80 - Analyzing class class org.spring.example.Person for index information. |
|
DEBUG work.data.mongodb.core.MongoTemplate: 632 - insert DBObject containing fields: [_class, age, name] in collection: person |
|
INFO org.spring.example.MongoApp: 30 - Insert: Person [id=4ddc6e784ce5b1eba3ceaf5c, name=Joe, age=34] |
|
DEBUG work.data.mongodb.core.MongoTemplate:1246 - findOne using query: { "_id" : { "$oid" : "4ddc6e784ce5b1eba3ceaf5c"}} in db.collection: database.person |
|
INFO org.spring.example.MongoApp: 34 - Found: Person [id=4ddc6e784ce5b1eba3ceaf5c, name=Joe, age=34] |
|
DEBUG work.data.mongodb.core.MongoTemplate: 778 - calling update using query: { "name" : "Joe"} and update: { "$set" : { "age" : 35}} in collection: person |
|
DEBUG work.data.mongodb.core.MongoTemplate:1246 - findOne using query: { "name" : "Joe"} in db.collection: database.person |
|
INFO org.spring.example.MongoApp: 39 - Updated: Person [id=4ddc6e784ce5b1eba3ceaf5c, name=Joe, age=35] |
|
DEBUG work.data.mongodb.core.MongoTemplate: 823 - remove using query: { "id" : "4ddc6e784ce5b1eba3ceaf5c"} in collection: person |
|
INFO org.spring.example.MongoApp: 46 - Number of people = : 0 |
|
DEBUG work.data.mongodb.core.MongoTemplate: 376 - Dropped collection [database.person]</programlisting> |
|
|
|
<para>There was implicit conversion using the |
|
<interfacename>MongoConverter</interfacename> between a |
|
<classname>String</classname> and <classname>ObjectId</classname> as |
|
stored in the database and recognizing a convention of the property "Id" |
|
name.</para> |
|
|
|
<note> |
|
<para>This example is meant to show the use of save, update and remove |
|
operations on MongoTemplate and not to show complex mapping |
|
functionality</para> |
|
</note> |
|
|
|
<para>The query syntax used in the example is explained in more detail in |
|
the section <link linkend="mongo.query">Querying Documents</link>.</para> |
|
|
|
<section id="mongo-template.id-handling"> |
|
<title>How the '_id' field is handled in the mapping layer</title> |
|
|
|
<para>MongoDB requires that you have an '_id' field for all documents. |
|
If you don't provide one the driver will assign a |
|
<classname>ObjectId</classname> with a generated value. When using the |
|
<classname>MongoMappingConverter</classname> there are certain rules |
|
that govern how properties from the Java class is mapped to this '_id' |
|
field.</para> |
|
|
|
<para>The following outlines what property will be mapped to the '_id' |
|
document field:</para> |
|
|
|
<para><itemizedlist> |
|
<listitem> |
|
<para>A property or field annotated with |
|
<classname>@Id</classname> |
|
(<classname>org.springframework.data.annotation.Id</classname>) |
|
will be mapped to the '_id' field.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>A property or field without an annotation but named |
|
<classname>id</classname> will be mapped to the '_id' |
|
field.</para> |
|
</listitem> |
|
</itemizedlist></para> |
|
|
|
<para>The following outlines what type conversion, if any, will be done |
|
on the property mapped to the _id document field when using the |
|
<classname>MappingMongoConverter</classname>, the default for |
|
<classname>MongoTemplate</classname>.</para> |
|
|
|
<itemizedlist> |
|
<listitem> |
|
<para>An id property or field declared as a String in the Java class |
|
will be converted to and stored as an |
|
<classname>ObjectId</classname> if possible using a Spring |
|
<interfacename>Converter<String, ObjectId></interfacename>. |
|
Valid conversion rules are delegated to the MongoDB Java driver. If |
|
it cannot be converted to an ObjectId, then the value will be stored |
|
as a string in the database.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>An id property or field declared as |
|
<classname>BigInteger</classname> in the Java class will be |
|
converted to and stored as an <classname>ObjectId</classname> using |
|
a Spring <interfacename>Converter<BigInteger, |
|
ObjectId></interfacename>.</para> |
|
</listitem> |
|
</itemizedlist> |
|
|
|
<para>If no field or property specified above is present in the Java |
|
class then an implicit '_id' file will be generated by the driver but |
|
not mapped to a property or field of the Java class.</para> |
|
|
|
<para>When querying and updating <classname>MongoTemplate</classname> |
|
will use the converter to handle conversions of the |
|
<classname>Query</classname> and <classname>Update</classname> objects |
|
that correspond to the above rules for saving documents so field names |
|
and types used in your queries will be able to match what is in your |
|
domain classes.</para> |
|
</section> |
|
|
|
<section id="mongo-template.type-mapping"> |
|
<title>Type mapping</title> |
|
|
|
<para>As MongoDB collections can contain documents that represent |
|
instances of a variety of types. A great example here is if you store a |
|
hierarchy of classes or simply have a class with a property of type |
|
<classname>Object</classname>. In the latter case the values held inside |
|
that property have to be read in correctly when retrieving the object. |
|
Thus we need a mechanism to store type information alongside the actual |
|
document.</para> |
|
|
|
<para>To achieve that the <classname>MappingMongoConverter</classname> |
|
uses a <interfacename>MongoTypeMapper</interfacename> abstraction with |
|
<classname>DefaultMongoTypeMapper</classname> as it's main |
|
implementation. It's default behaviour is storing the fully qualified |
|
classname under <code>_class</code> inside the document for the |
|
top-level document as well as for every value if it's a complex type and |
|
a subtype of the property type declared.</para> |
|
|
|
<example> |
|
<title>Type mapping</title> |
|
|
|
<programlisting language="java">public class Sample { |
|
Contact value; |
|
} |
|
|
|
public abstract class Contact { … } |
|
|
|
public class Person extends Contact { … } |
|
|
|
Sample sample = new Sample(); |
|
sample.value = new Person(); |
|
|
|
mongoTemplate.save(sample); |
|
|
|
{ "_class" : "com.acme.Sample", |
|
"value" : { "_class" : "com.acme.Person" } |
|
}</programlisting> |
|
</example> |
|
|
|
<para>As you can see we store the type information for the actual root |
|
class persistent as well as for the nested type as it is complex and a |
|
subtype of <classname>Contact</classname>. So if you're now using |
|
<methodname>mongoTemplate.findAll(Object.class, "sample")</methodname> |
|
we are able to find out that the document stored shall be a |
|
<classname>Sample</classname> instance. We are also able to find out |
|
that the value property shall be a <classname>Person</classname> |
|
actually.</para> |
|
|
|
<simplesect> |
|
<title>Customizing type mapping</title> |
|
|
|
<para>In case you want to avoid writing the entire Java class name as |
|
type information but rather like to use some key you can use the |
|
<interfacename>@TypeAlias</interfacename> annotation at the entity |
|
class being persisted. If you need to customize the mapping even more |
|
have a look at the |
|
<interfacename>TypeInformationMapper</interfacename> interface. An |
|
instance of that interface can be configured at the |
|
<classname>DefaultMongoTypeMapper</classname> which can be configured |
|
in turn on <classname>MappingMongoConverter</classname>.</para> |
|
|
|
<example> |
|
<title>Defining a TypeAlias for an Entity</title> |
|
|
|
<programlisting language="java">@TypeAlias("pers") |
|
class Person { |
|
|
|
}</programlisting> |
|
|
|
<para>Note that the resulting document will contain |
|
<code>"pers"</code> as the value in the <code>_class</code> |
|
Field.</para> |
|
</example> |
|
</simplesect> |
|
|
|
<simplesect> |
|
<title>Configuring custom type mapping</title> |
|
|
|
<para>The following example demonstrates how to configure a custom |
|
<classname>MongoTypeMapper</classname> in |
|
<classname>MappingMongoConverter</classname>.</para> |
|
|
|
<example> |
|
<title>Configuring a custom MongoTypeMapper via Spring Java |
|
Config</title> |
|
|
|
<programlisting language="java">class CustomMongoTypeMapper extends DefaultMongoTypeMapper { |
|
//implement custom type mapping here |
|
}</programlisting> |
|
|
|
<programlisting language="java">@Configuration |
|
class SampleMongoConfiguration extends AbstractMongoConfiguration { |
|
|
|
@Override |
|
protected String getDatabaseName() { |
|
return "database"; |
|
} |
|
|
|
@Override |
|
public Mongo mongo() throws Exception { |
|
return new Mongo(); |
|
} |
|
|
|
@Bean |
|
@Override |
|
public MappingMongoConverter mappingMongoConverter() throws Exception { |
|
MappingMongoConverter mmc = super.mappingMongoConverter(); |
|
mmc.setTypeMapper(customTypeMapper()); |
|
return mmc; |
|
} |
|
|
|
@Bean |
|
public MongoTypeMapper customTypeMapper() { |
|
return new CustomMongoTypeMapper(); |
|
} |
|
}</programlisting> |
|
|
|
<para>Note that we are extending the |
|
<classname>AbstractMongoConfiguration</classname> class and override |
|
the bean definition of the |
|
<classname>MappingMongoConverter</classname> where we configure our |
|
custom <classname>MongoTypeMapper</classname>.</para> |
|
</example> |
|
|
|
<example> |
|
<title>Configuring a custom MongoTypeMapper via XML</title> |
|
|
|
<programlisting language="xml"><mongo:mapping-converter type-mapper-ref="customMongoTypeMapper"/> |
|
|
|
<bean name="customMongoTypeMapper" class="com.bubu.mongo.CustomMongoTypeMapper"/></programlisting> |
|
</example> |
|
</simplesect> |
|
</section> |
|
|
|
<section id="mongo-template.save-insert"> |
|
<title>Methods for saving and inserting documents</title> |
|
|
|
<para>There are several convenient methods on |
|
<classname>MongoTemplate</classname> for saving and inserting your |
|
objects. To have more fine grained control over the conversion process |
|
you can register Spring converters with the |
|
<classname>MappingMongoConverter</classname>, for example |
|
<interfacename>Converter<Person, DBObject></interfacename> and |
|
<interfacename>Converter<DBObject, Person></interfacename>.</para> |
|
|
|
<note> |
|
<para>The difference between insert and save operations is that a save |
|
operation will perform an insert if the object is not already |
|
present.</para> |
|
</note> |
|
|
|
<para>The simple case of using the save operation is to save a POJO. In |
|
this case the collection name will be determined by name (not fully |
|
qualfied) of the class. You may also call the save operation with a |
|
specific collection name. The collection to store the object can be |
|
overriden using mapping metadata.</para> |
|
|
|
<para>When inserting or saving, if the Id property is not set, the |
|
assumption is that its value will be auto-generated by the database. As |
|
such, for auto-generation of an ObjectId to succeed the type of the Id |
|
property/field in your class must be either a |
|
<classname>String</classname>, <classname>ObjectId</classname>, or |
|
<classname>BigInteger</classname>.</para> |
|
|
|
<para>Here is a basic example of using the save operation and retrieving |
|
its contents.</para> |
|
|
|
<example> |
|
<title>Inserting and retrieving documents using the |
|
MongoTemplate</title> |
|
|
|
<programlisting language="java">import static org.springframework.data.mongodb.core.query.Criteria.where; |
|
import static org.springframework.data.mongodb.core.query.Criteria.query; |
|
|
|
… |
|
|
|
Person p = new Person("Bob", 33); |
|
mongoTemplate.insert(p); |
|
|
|
Person qp = mongoTemplate.findOne(query(where("age").is(33)), Person.class); </programlisting> |
|
</example> |
|
|
|
<para>The insert/save operations available to you are listed |
|
below.</para> |
|
|
|
<para><itemizedlist> |
|
<listitem> |
|
<para><literal>void</literal> <emphasis role="bold">save |
|
</emphasis> <literal>(Object objectToSave) </literal> Save the |
|
object to the default collection.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>void</literal> <emphasis role="bold">save |
|
</emphasis> <literal>(Object objectToSave, String collectionName) |
|
</literal> Save the object to the specified collection.</para> |
|
</listitem> |
|
</itemizedlist></para> |
|
|
|
<para>A similar set of insert operations is listed below</para> |
|
|
|
<para><itemizedlist> |
|
<listitem> |
|
<para><literal>void</literal> <emphasis |
|
role="bold">insert</emphasis> <literal>(Object objectToSave) |
|
</literal> Insert the object to the default collection.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>void</literal> <emphasis role="bold">insert |
|
</emphasis> <literal>(Object objectToSave, String collectionName) |
|
</literal> Insert the object to the specified collection.</para> |
|
</listitem> |
|
</itemizedlist></para> |
|
|
|
<section id="mongo-template.save-insert.collection"> |
|
<title>Which collection will my documents be saved into?</title> |
|
|
|
<para>There are two ways to manage the collection name that is used |
|
for operating on the documents. The default collection name that is |
|
used is the class name changed to start with a lower-case letter. So a |
|
<classname>com.test.Person</classname> class would be stored in the |
|
"person" collection. You can customize this by providing a different |
|
collection name using the @Document annotation. You can also override |
|
the collection name by providing your own collection name as the last |
|
parameter for the selected MongoTemplate method calls.</para> |
|
</section> |
|
|
|
<section id="mongo-template.save-insert.individual"> |
|
<title>Inserting or saving individual objects</title> |
|
|
|
<para>The MongoDB driver supports inserting a collection of documents |
|
in one operation. The methods in the MongoOperations interface that |
|
support this functionality are listed below</para> |
|
|
|
<para><itemizedlist> |
|
<listitem> |
|
<para><emphasis role="bold">insert</emphasis><literal> |
|
</literal> Insert an object. If there is an existing document |
|
with the same id then an error is generated.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">insertAll</emphasis> Takes a |
|
<literal>Collection </literal>of objects as the first parameter. |
|
This method inspects each object and inserts it to the |
|
appropriate collection based on the rules specified |
|
above.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">save</emphasis> Save the object |
|
overwriting any object that might exist with the same id.</para> |
|
</listitem> |
|
</itemizedlist></para> |
|
</section> |
|
|
|
<section id="mongo-template.save-insert.batch"> |
|
<title>Inserting several objects in a batch</title> |
|
|
|
<para>The MongoDB driver supports inserting a collection of documents |
|
in one operation. The methods in the MongoOperations interface that |
|
support this functionality are listed below</para> |
|
|
|
<para><itemizedlist> |
|
<listitem> |
|
<para><emphasis role="bold">insert</emphasis><literal> methods |
|
that take a Collection</literal><literal> as the first |
|
argument.</literal>This inserts a list of objects in a single |
|
batch write to the database.</para> |
|
</listitem> |
|
</itemizedlist></para> |
|
</section> |
|
</section> |
|
|
|
<section id="mongodb-template-update"> |
|
<title>Updating documents in a collection</title> |
|
|
|
<para>For updates we can elect to update the first document found using |
|
<interfacename>MongoOperation</interfacename>'s method |
|
<literal>updateFirst</literal> or we can update all documents that were |
|
found to match the query using the method |
|
<literal>updateMulti</literal>. Here is an example of an update of all |
|
SAVINGS accounts where we are adding a one time $50.00 bonus to the |
|
balance using the <literal>$inc</literal> operator.</para> |
|
|
|
<example> |
|
<title>Updating documents using the MongoTemplate</title> |
|
|
|
<programlisting language="java">import static org.springframework.data.mongodb.core.query.Criteria.where; |
|
import static org.springframework.data.mongodb.core.query.Query; |
|
import static org.springframework.data.mongodb.core.query.Update; |
|
|
|
... |
|
|
|
WriteResult wr = mongoTemplate.updateMulti(new Query(where("accounts.accountType").is(Account.Type.SAVINGS)), |
|
new Update().inc("accounts.$.balance", 50.00), |
|
Account.class); |
|
</programlisting> |
|
</example> |
|
|
|
<para>In addition to the <classname>Query</classname> discussed above we |
|
provide the update definition using an <classname>Update</classname> |
|
object. The <classname>Update</classname> class has methods that match |
|
the update modifiers available for MongoDB.</para> |
|
|
|
<para>As you can see most methods return the |
|
<classname>Update</classname> object to provide a fluent style for the |
|
API.</para> |
|
|
|
<section id="mongodb-template-update.methods"> |
|
<title>Methods for executing updates for documents</title> |
|
|
|
<para><itemizedlist> |
|
<listitem> |
|
<para><emphasis role="bold">updateFirst </emphasis> Updates the |
|
first document that matches the query document criteria with the |
|
provided updated document.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">updateMulti </emphasis> Updates all |
|
objects that match the query document criteria with the provided |
|
updated document.</para> |
|
</listitem> |
|
</itemizedlist></para> |
|
</section> |
|
|
|
<section id="mongodb-template-update.update"> |
|
<title>Methods for the Update class</title> |
|
|
|
<para>The Update class can be used with a little 'syntax sugar' as its |
|
methods are meant to be chained together and you can kick-start the |
|
creation of a new Update instance via the static method |
|
<literal>public static Update update(String key, Object |
|
value)</literal> and using static imports.</para> |
|
|
|
<para>Here is a listing of methods on the Update class</para> |
|
|
|
<para><itemizedlist> |
|
<listitem> |
|
<para><literal>Update</literal> <emphasis role="bold">addToSet |
|
</emphasis> <literal>(String key, Object value) </literal> |
|
Update using the <literal>$addToSet</literal> update |
|
modifier</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Update</literal> <emphasis role="bold">inc |
|
</emphasis> <literal>(String key, Number inc) </literal> Update |
|
using the <literal>$inc</literal> update modifier</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Update</literal> <emphasis role="bold">pop |
|
</emphasis> <literal>(String key, Update.Position pos) |
|
</literal> Update using the <literal>$pop</literal> update |
|
modifier</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Update</literal> <emphasis role="bold">pull |
|
</emphasis> <literal>(String key, Object value) </literal> |
|
Update using the <literal>$pull</literal> update modifier</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Update</literal> <emphasis role="bold">pullAll |
|
</emphasis> <literal>(String key, Object[] values) </literal> |
|
Update using the <literal>$pullAll</literal> update |
|
modifier</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Update</literal> <emphasis role="bold">push |
|
</emphasis> <literal>(String key, Object value) </literal> |
|
Update using the <literal>$push</literal> update modifier</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Update</literal> <emphasis role="bold">pushAll |
|
</emphasis> <literal>(String key, Object[] values) </literal> |
|
Update using the <literal>$pushAll</literal> update |
|
modifier</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Update</literal> <emphasis role="bold">rename |
|
</emphasis> <literal>(String oldName, String newName) </literal> |
|
Update using the <literal>$rename</literal> update |
|
modifier</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Update</literal> <emphasis role="bold">set |
|
</emphasis> <literal>(String key, Object value) </literal> |
|
Update using the <literal>$set</literal> update modifier</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Update</literal> <emphasis role="bold">unset |
|
</emphasis> <literal>(String key)</literal> Update using the |
|
<literal>$unset</literal> update modifier</para> |
|
</listitem> |
|
</itemizedlist></para> |
|
</section> |
|
</section> |
|
|
|
<section id="mongo-template.upserts"> |
|
<title>Upserting documents in a collection</title> |
|
|
|
<para>Related to performing an <methodname>updateFirst</methodname> |
|
operations, you can also perform an upsert operation which will perform |
|
an insert if no document is found that matches the query. The document |
|
that is inserted is a combination of the query document and the update |
|
document. Here is an example</para> |
|
|
|
<programlisting>template.upsert(query(where("ssn").is(1111).and("firstName").is("Joe").and("Fraizer").is("Update")), update("address", addr), Person.class);</programlisting> |
|
</section> |
|
|
|
<section id="mongo-template.find-and-upsert"> |
|
<title>Finding and Upserting documents in a collection</title> |
|
|
|
<para>The <methodname>findAndModify(…)</methodname> method on |
|
DBCollection can update a document and return either the old or newly |
|
updated document in a single operation. |
|
<classname>MongoTemplate</classname> provides a findAndModify method |
|
that takes <classname>Query</classname> and |
|
<classname>Update</classname> classes and converts from |
|
<classname>DBObject</classname> to your POJOs. Here are the |
|
methods</para> |
|
|
|
<programlisting language="java"><T> T findAndModify(Query query, Update update, Class<T> entityClass); |
|
|
|
<T> T findAndModify(Query query, Update update, Class<T> entityClass, String collectionName); |
|
|
|
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass); |
|
|
|
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass, String collectionName);</programlisting> |
|
|
|
<para>As an example usage, we will insert of few |
|
<classname>Person</classname> objects into the container and perform a |
|
simple findAndUpdate operation</para> |
|
|
|
<programlisting language="java">mongoTemplate.insert(new Person("Tom", 21)); |
|
mongoTemplate.insert(new Person("Dick", 22)); |
|
mongoTemplate.insert(new Person("Harry", 23)); |
|
|
|
Query query = new Query(Criteria.where("firstName").is("Harry")); |
|
Update update = new Update().inc("age", 1); |
|
Person p = mongoTemplate.findAndModify(query, update, Person.class); // return's old person object |
|
|
|
assertThat(p.getFirstName(), is("Harry")); |
|
assertThat(p.getAge(), is(23)); |
|
p = mongoTemplate.findOne(query, Person.class); |
|
assertThat(p.getAge(), is(24)); |
|
|
|
|
|
// Now return the newly updated document when updating |
|
p = template.findAndModify(query, update, new FindAndModifyOptions().returnNew(true), Person.class); |
|
assertThat(p.getAge(), is(25));</programlisting> |
|
|
|
<para>The <classname>FindAndModifyOptions</classname> lets you set the |
|
options of returnNew, upsert, and remove. An example extending off the |
|
previous code snippit is shown below</para> |
|
|
|
<programlisting language="java">Query query2 = new Query(Criteria.where("firstName").is("Mary")); |
|
p = mongoTemplate.findAndModify(query2, update, new FindAndModifyOptions().returnNew(true).upsert(true), Person.class); |
|
assertThat(p.getFirstName(), is("Mary")); |
|
assertThat(p.getAge(), is(1));</programlisting> |
|
</section> |
|
|
|
<section id="mongo-template.delete"> |
|
<title>Methods for removing documents</title> |
|
|
|
<para>You can use several overloaded methods to remove an object from |
|
the database.</para> |
|
|
|
<para><itemizedlist> |
|
<listitem> |
|
<para><emphasis role="bold">remove</emphasis> Remove the given |
|
document based on one of the following: a specific object |
|
instance, a query document criteria combined with a class or a |
|
query document criteria combined with a specific collection |
|
name.</para> |
|
</listitem> |
|
</itemizedlist></para> |
|
</section> |
|
</section> |
|
|
|
<section id="mongo.query"> |
|
<title>Querying Documents</title> |
|
|
|
<para>You can express your queries using the <classname>Query</classname> |
|
and <classname>Criteria</classname> classes which have method names that |
|
mirror the native MongoDB operator names such as <literal>lt</literal>, |
|
<literal>lte</literal>, <literal>is</literal>, and others. The |
|
<classname>Query</classname> and <classname>Criteria</classname> classes |
|
follow a fluent API style so that you can easily chain together multiple |
|
method criteria and queries while having easy to understand code. Static |
|
imports in Java are used to help remove the need to see the 'new' keyword |
|
for creating <classname>Query</classname> and |
|
<classname>Criteria</classname> instances so as to improve readability. If |
|
you like to create <classname>Query</classname> instances from a plain |
|
JSON String use <classname>BasicQuery</classname>.</para> |
|
|
|
<example> |
|
<title>Creating a Query instance from a plain JSON String</title> |
|
|
|
<programlisting language="java">BasicQuery query = new BasicQuery("{ age : { $lt : 50 }, accounts.balance : { $gt : 1000.00 }}"); |
|
List<Person> result = mongoTemplate.find(query, Person.class); </programlisting> |
|
</example> |
|
|
|
<para>GeoSpatial queries are also supported and are described more in the |
|
section <link linkend="mongo.geospatial">GeoSpatial Queries</link>.</para> |
|
|
|
<para>Map-Reduce operations are also supported and are described more in |
|
the section <link linkend="mongo.mapreduce">Map-Reduce</link>.</para> |
|
|
|
<section id="mongodb-template-query"> |
|
<title>Querying documents in a collection</title> |
|
|
|
<para>We saw how to retrieve a single document using the findOne and |
|
findById methods on MongoTemplate in previous sections which 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.</para> |
|
|
|
<example> |
|
<title>Querying for documents using the MongoTemplate</title> |
|
|
|
<programlisting language="java">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); </programlisting> |
|
</example> |
|
|
|
<para>All find methods take a <classname>Query</classname> object as a |
|
parameter. This object defines the criteria and options used to perform |
|
the query. The criteria is specified using a |
|
<classname>Criteria</classname> object that has a static factory method |
|
named <classname>where</classname> used to instantiate a new |
|
<classname>Criteria</classname> object. We recommend using a static |
|
import for |
|
<classname>org.springframework.data.mongodb.core.query.Criteria.where</classname> |
|
and <literal>Query.query</literal> to make the query more |
|
readable.</para> |
|
|
|
<para>This query should return a list of <classname>Person</classname> |
|
objects that meet the specified criteria. The |
|
<classname>Criteria</classname> class has the following methods that |
|
correspond to the operators provided in MongoDB.</para> |
|
|
|
<para>As you can see most methods return the |
|
<classname>Criteria</classname> object to provide a fluent style for the |
|
API.</para> |
|
|
|
<section id="mongodb-template-query.criteria"> |
|
<title>Methods for the Criteria class</title> |
|
|
|
<para> |
|
<itemizedlist> |
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">all |
|
</emphasis> <literal>(Object o)</literal>Creates a criterion |
|
using the <literal>$all</literal> operator</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">and |
|
</emphasis> <literal>(String key) </literal>Adds a chained |
|
<classname>Criteria</classname> with the specified |
|
<literal>key</literal> to the current |
|
<classname>Criteria</classname> and returns the newly created |
|
one</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis |
|
role="bold">andOperator </emphasis> <literal>(Criteria... |
|
criteria)</literal>Creates an and query using the |
|
<literal>$and</literal> operator for all of the provided |
|
criteria (requires MongoDB 2.0 or later)</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis |
|
role="bold">elemMatch </emphasis> <literal>(Criteria c) |
|
</literal>Creates a criterion using the |
|
<literal>$elemMatch</literal> operator</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">exists |
|
</emphasis> <literal>(boolean b) </literal>Creates a criterion |
|
using the <literal>$exists</literal> operator</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">gt |
|
</emphasis> <literal>(Object o)</literal>Creates a criterion |
|
using the <literal>$gt</literal> operator</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">gte |
|
</emphasis> <literal>(Object o)</literal>Creates a criterion |
|
using the <literal>$gte</literal> operator</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">in |
|
</emphasis> <literal>(Object... o) </literal>Creates a criterion |
|
using the <literal>$in</literal> operator for a varargs |
|
argument.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">in |
|
</emphasis> <literal>(Collection<?> collection) |
|
</literal>Creates a criterion using the <literal>$in</literal> |
|
operator using a collection</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">is |
|
</emphasis> <literal>(Object o)</literal>Creates a criterion |
|
using the <literal>$is</literal> operator</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">lt |
|
</emphasis> <literal>(Object o)</literal>Creates a criterion |
|
using the <literal>$lt</literal> operator</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">lte |
|
</emphasis> <literal>(Object o)</literal>Creates a criterion |
|
using the <literal>$lte</literal> operator</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">mod |
|
</emphasis> <literal>(Number value, Number |
|
remainder)</literal>Creates a criterion using the |
|
<literal>$mod</literal> operator</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">ne |
|
</emphasis> <literal>(Object o)</literal>Creates a criterion |
|
using the <literal>$ne</literal> operator</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">nin |
|
</emphasis> <literal>(Object... o) </literal>Creates a criterion |
|
using the <literal>$nin</literal> operator</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis |
|
role="bold">norOperator </emphasis> <literal>(Criteria... |
|
criteria)</literal>Creates an nor query using the |
|
<literal>$nor</literal> operator for all of the provided |
|
criteria</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">not |
|
</emphasis> <literal>()</literal>Creates a criterion using the |
|
<literal>$not</literal> meta operator which affects the clause |
|
directly following</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis |
|
role="bold">orOperator </emphasis> <literal>(Criteria... |
|
criteria)</literal>Creates an or query using the |
|
<literal>$or</literal> operator for all of the provided |
|
criteria</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">regex |
|
</emphasis> <literal>(String re) </literal>Creates a criterion |
|
using a <literal>$regex</literal></para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">size |
|
</emphasis> <literal>(int s)</literal>Creates a criterion using |
|
the <literal>$size</literal> operator</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">type |
|
</emphasis> <literal>(int t)</literal>Creates a criterion using |
|
the <literal>$type</literal> operator</para> |
|
</listitem> |
|
</itemizedlist> |
|
</para> |
|
</section> |
|
|
|
<para>There are also methods on the Criteria class for geospatial |
|
queries. Here is a listing but look at the section on <link |
|
linkend="mongo.geospatial">GeoSpatial Queries</link> to see them in |
|
action.</para> |
|
|
|
<itemizedlist> |
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">withinCenter |
|
</emphasis> <literal>(Circle circle)</literal> Creates a geospatial |
|
criterion using <literal>$within $center</literal> operators</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis |
|
role="bold">withinCenterSphere </emphasis> <literal>(Circle circle) |
|
</literal>Creates a geospatial criterion using <literal>$within |
|
$center</literal> operators. This is only available for MongoDB 1.7 |
|
and higher.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">withinBox |
|
</emphasis> <literal>(Box box)</literal> Creates a geospatial |
|
criterion using a <literal>$within $box</literal> operation |
|
<literal/></para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">near |
|
</emphasis> <literal>(Point point)</literal> Creates a geospatial |
|
criterion using a <literal>$near </literal>operation</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">nearSphere |
|
</emphasis> <literal>(Point point) </literal>Creates a geospatial |
|
criterion using <literal>$nearSphere$center</literal> operations. |
|
This is only available for MongoDB 1.7 and higher.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Criteria</literal> <emphasis role="bold">maxDistance |
|
</emphasis> <literal>(double maxDistance) </literal>Creates a |
|
geospatial criterion using the <literal>$maxDistance</literal> |
|
operation, for use with $near.</para> |
|
</listitem> |
|
</itemizedlist> |
|
|
|
<para>The <classname>Query</classname> class has some additional methods |
|
used to provide options for the query.</para> |
|
|
|
<section id="mongodb-template-query.query"> |
|
<title>Methods for the Query class</title> |
|
|
|
<para> |
|
<itemizedlist> |
|
<listitem> |
|
<para><literal>Query</literal> <emphasis role="bold">addCriteria |
|
</emphasis> <literal>(Criteria criteria)</literal> used to add |
|
additional criteria to the query</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Field</literal> <emphasis role="bold">fields |
|
</emphasis> <literal>()</literal> used to define fields to be |
|
included in the query results</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Query</literal> <emphasis role="bold">limit |
|
</emphasis> <literal>(int limit)</literal> used to limit the |
|
size of the returned results to the provided limit (used for |
|
paging)</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Query</literal> <emphasis role="bold">skip |
|
</emphasis> <literal>(int skip)</literal> used to skip the |
|
provided number of documents in the results (used for |
|
paging)</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>Sort</literal> <emphasis role="bold">sort |
|
</emphasis> <literal>()</literal> used to provide sort |
|
definition for the results</para> |
|
</listitem> |
|
</itemizedlist> |
|
</para> |
|
</section> |
|
</section> |
|
|
|
<section id="mongo-template.querying"> |
|
<title>Methods for querying for documents</title> |
|
|
|
<para>The query methods need to specify the target type T that will be |
|
returned and they are also overloaded with an explicit collection name |
|
for queries that should operate on a collection other than the one |
|
indicated by the return type.</para> |
|
|
|
<para><itemizedlist> |
|
<listitem> |
|
<para><emphasis role="bold">findAll </emphasis> Query for a list |
|
of objects of type T from the collection.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">findOne</emphasis> Map the results of |
|
an ad-hoc query on the collection to a single instance of an |
|
object of the specified type.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">findById</emphasis> Return an object |
|
of the given id and target class.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">find</emphasis> Map the results of an |
|
ad-hoc query on the collection to a List of the specified |
|
type.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">findAndRemove</emphasis> 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 also removed from the collection |
|
in the database.</para> |
|
</listitem> |
|
</itemizedlist></para> |
|
</section> |
|
|
|
<section id="mongo.geospatial" lang=""> |
|
<title>GeoSpatial Queries</title> |
|
|
|
<para>MongoDB supports GeoSpatial queries through the use of operators |
|
such as <literal>$near</literal>, <literal>$within</literal>, and |
|
<literal>$nearSphere</literal>. Methods specific to geospatial queries |
|
are available on the <classname>Criteria</classname> class. There are |
|
also a few shape classes, <classname>Box</classname>, |
|
<classname>Circle</classname>, and <classname>Point</classname> that are |
|
used in conjunction with geospatial related |
|
<classname>Criteria</classname> methods.</para> |
|
|
|
<para>To understand how to perform GeoSpatial queries we will use the |
|
following Venue class taken from the integration tests.which relies on |
|
using the rich <classname>MappingMongoConverter</classname>.</para> |
|
|
|
<programlisting language="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) + "]"; |
|
} |
|
}</programlisting> |
|
|
|
<para>To find locations within a <classname>Circle</classname>, the |
|
following query can be used.</para> |
|
|
|
<programlisting lang="" language="java">Circle circle = new Circle(-73.99171, 40.738868, 0.01); |
|
List<Venue> venues = |
|
template.find(new Query(Criteria.where("location").withinCenter(circle)), Venue.class);</programlisting> |
|
|
|
<para>To find venues within a <classname>Circle</classname> using |
|
spherical coordinates the following query can be used</para> |
|
|
|
<programlisting lang="" language="java">Circle circle = new Circle(-73.99171, 40.738868, 0.003712240453784); |
|
List<Venue> venues = |
|
template.find(new Query(Criteria.where("location").withinCenterSphere(circle)), Venue.class);</programlisting> |
|
|
|
<para>To find venues within a <classname>Box</classname> the following |
|
query can be used</para> |
|
|
|
<programlisting language="java">//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").withinBox(box)), Venue.class);</programlisting> |
|
|
|
<para>To find venues near a <classname>Point</classname>, the following |
|
query can be used</para> |
|
|
|
<programlisting language="java">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);</programlisting> |
|
|
|
<para>To find venues near a <classname>Point</classname> using spherical |
|
coordines the following query can be used</para> |
|
|
|
<programlisting language="java">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); |
|
</programlisting> |
|
|
|
<section id="mongo.geo-near"> |
|
<title>Geo near queries</title> |
|
|
|
<para>MongoDB supports querying the database for geo locations and |
|
calculation the distance from a given origin at the very same time. |
|
With geo-near queries it's possible to express queries like: "find all |
|
restaurants in the surrounding 10 miles". To do so |
|
<interfacename>MongoOperations</interfacename> provides |
|
<methodname>geoNear(…)</methodname> methods taking a |
|
<classname>NearQuery</classname> as argument as well as the already |
|
familiar entity type and collection</para> |
|
|
|
<programlisting language="java">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);</programlisting> |
|
|
|
<para>As you can see we use the <classname>NearQuery</classname> |
|
builder API to set up a query to return all |
|
<classname>Restaurant</classname> instances surrounding the given |
|
<classname>Point</classname> by 10 miles maximum. The |
|
<classname>Metrics</classname> enum used here actually implements an |
|
interface so that other metrics could be plugged into a distance as |
|
well. A <interfacename>Metric</interfacename> 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 pre-built in metrics (miles and kilometers) |
|
will automatically trigger the spherical flag to be set on the query. |
|
If you want to avoid that, simply hand in plain |
|
<classname>double</classname> values into |
|
<methodname>maxDistance(…)</methodname>. For more information see the |
|
JavaDoc of <classname>NearQuery</classname> and |
|
<classname>Distance</classname>.</para> |
|
|
|
<para>The geo near operations return a |
|
<classname>GeoResults</classname> wrapper object that encapsulates |
|
<classname>GeoResult</classname> instances. The wrapping |
|
<classname>GeoResults</classname> allows to access the average |
|
distance of all results. A single <classname>GeoResult</classname> |
|
object simply carries the entity found plus its distance from the |
|
origin.</para> |
|
</section> |
|
</section> |
|
</section> |
|
|
|
<section id="mongo.mapreduce"> |
|
<title>Map-Reduce Operations</title> |
|
|
|
<para>You can query MongoDB using Map-Reduce which is useful for batch |
|
processing, data aggregation, and for when the query language doesn't |
|
fulfill your needs.</para> |
|
|
|
<para>Spring provides integration with MongoDB's map reduce by providing |
|
methods on MongoOperations to simplify the creation and execution of |
|
Map-Reduce operations. It can convert the results of a Map-Reduce |
|
operation to a POJO also integrates with Spring's <ulink |
|
url="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html">Resource |
|
abstraction</ulink> abstraction. This will let you place your JavaScript |
|
files on the file system, classpath, http server or any other Spring |
|
Resource implementation and then reference the JavaScript resources via an |
|
easy URI style syntax, e.g. 'classpath:reduce.js;. Externalizing |
|
JavaScript code in files is often preferable to embedding them as Java |
|
strings in your code. Note that you can still pass JavaScript code as Java |
|
strings if you prefer.</para> |
|
|
|
<section id="mongo.mapreduce.example" lang=""> |
|
<title>Example Usage</title> |
|
|
|
<para>To understand how to perform Map-Reduce operations an example from |
|
the book 'MongoDB - The definitive guide' is used. In this example we |
|
will create three documents that have the values [a,b], [b,c], and [c,d] |
|
respectfully. The values in each document are associated with the key |
|
'x' as shown below. For this example assume these documents are in the |
|
collection named "jmr1". <programlisting>{ "_id" : ObjectId("4e5ff893c0277826074ec533"), "x" : [ "a", "b" ] } |
|
{ "_id" : ObjectId("4e5ff893c0277826074ec534"), "x" : [ "b", "c" ] } |
|
{ "_id" : ObjectId("4e5ff893c0277826074ec535"), "x" : [ "c", "d" ] } |
|
</programlisting> A map function that will count the occurance of each letter |
|
in the array for each document is shown below <programlisting |
|
language="java">function () { |
|
for (var i = 0; i < this.x.length; i++) { |
|
emit(this.x[i], 1); |
|
} |
|
}</programlisting> The reduce function that will sum up the occurance of each |
|
letter across all the documents is shown below <programlisting |
|
language="java">function (key, values) { |
|
var sum = 0; |
|
for (var i = 0; i < values.length; i++) |
|
sum += values[i]; |
|
return sum; |
|
}</programlisting> Executing this will result in a collection as shown below. |
|
<programlisting>{ "_id" : "a", "value" : 1 } |
|
{ "_id" : "b", "value" : 2 } |
|
{ "_id" : "c", "value" : 2 } |
|
{ "_id" : "d", "value" : 1 }</programlisting> Assuming that the map and reduce |
|
functions are located in map.js and reduce.js and bundled in your jar so |
|
they are available on the classpath, you can execute a map-reduce |
|
operation and obtain the results as shown below <programlisting |
|
language="java">MapReduceResults<ValueObject> results = mongoOperations.mapReduce("jmr1", "classpath:map.js", "classpath:reduce.js", ValueObject.class); |
|
for (ValueObject valueObject : results) { |
|
System.out.println(valueObject); |
|
}</programlisting> The output of the above code is <programlisting>ValueObject [id=a, value=1.0] |
|
ValueObject [id=b, value=2.0] |
|
ValueObject [id=c, value=2.0] |
|
ValueObject [id=d, value=1.0]</programlisting> The MapReduceResults class |
|
implements <classname>Iterable</classname> and provides access to the |
|
raw output, as well as timing and count statistics. The |
|
<classname>ValueObject</classname> class is simply <programlisting |
|
language="java">public class ValueObject { |
|
|
|
private String id; |
|
private float value; |
|
|
|
public String getId() { |
|
return id; |
|
} |
|
|
|
public float getValue() { |
|
return value; |
|
} |
|
|
|
public void setValue(float value) { |
|
this.value = value; |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return "ValueObject [id=" + id + ", value=" + value + "]"; |
|
} |
|
}</programlisting> By default the output type of INLINE is used so you don't |
|
have to specify an output collection. To specify additional map-reduce |
|
options use an overloaded method that takes an additional |
|
<classname>MapReduceOptions</classname> argument. The class |
|
<classname>MapReduceOptions</classname> has a fluent API so adding |
|
additional options can be done in a very compact syntax. Here an example |
|
that sets the output collection to "jmr1_out". Note that setting only |
|
the output collection assumes a default output type of REPLACE. |
|
<programlisting language="java">MapReduceResults<ValueObject> results = mongoOperations.mapReduce("jmr1", "classpath:map.js", "classpath:reduce.js", |
|
new MapReduceOptions().outputCollection("jmr1_out"), ValueObject.class);</programlisting> |
|
There is also a static import <literal>import static |
|
org.springframework.data.mongodb.core.mapreduce.MapReduceOptions.options;</literal> |
|
that can be used to make the syntax slightly more compact |
|
<programlisting language="java">MapReduceResults<ValueObject> results = mongoOperations.mapReduce("jmr1", "classpath:map.js", "classpath:reduce.js", |
|
options().outputCollection("jmr1_out"), ValueObject.class);</programlisting> |
|
You can also specify a query to reduce the set of data that will be used |
|
to feed into the map-reduce operation. This will remove the document |
|
that contains [a,b] from consideration for map-reduce operations. |
|
<programlisting language="java">Query query = new Query(where("x").ne(new String[] { "a", "b" })); |
|
MapReduceResults<ValueObject> results = mongoOperations.mapReduce(query, "jmr1", "classpath:map.js", "classpath:reduce.js", |
|
options().outputCollection("jmr1_out"), ValueObject.class);</programlisting> |
|
Note that you can specify additional limit and sort values as well on |
|
the query but not skip values.</para> |
|
</section> |
|
</section> |
|
|
|
<section id="mongo.group"> |
|
<title>Group Operations</title> |
|
|
|
<para>As an alternative to using Map-Reduce to perform data aggregation, |
|
you can use the <ulink |
|
url="http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group"><literal>group</literal> |
|
operation</ulink> which feels similar to using SQL's group by query style, |
|
so it may feel more approachable vs. using Map-Reduce. Using the group |
|
operations does have some limitations, for example it is not supported in |
|
a shareded environment and it returns the full result set in a single BSON |
|
object, so the result should be small, less than 10,000 keys.</para> |
|
|
|
<para>Spring provides integration with MongoDB's group operation by |
|
providing methods on MongoOperations to simplify the creation and |
|
execution of group operations. It can convert the results of the group |
|
operation to a POJO and also integrates with Spring's <ulink |
|
url="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html">Resource |
|
abstraction</ulink> abstraction. This will let you place your JavaScript |
|
files on the file system, classpath, http server or any other Spring |
|
Resource implementation and then reference the JavaScript resources via an |
|
easy URI style syntax, e.g. 'classpath:reduce.js;. Externalizing |
|
JavaScript code in files if often preferable to embedding them as Java |
|
strings in your code. Note that you can still pass JavaScript code as Java |
|
strings if you prefer.</para> |
|
|
|
<section id="mongo.group.example"> |
|
<title>Example Usage</title> |
|
|
|
<para>In order to understand how group operations work the following |
|
example is used, which is somewhat artificial. For a more realistic |
|
example consult the book 'MongoDB - The definitive guide'. A collection |
|
named "group_test_collection" created with the following rows.</para> |
|
|
|
<programlisting>{ "_id" : ObjectId("4ec1d25d41421e2015da64f1"), "x" : 1 } |
|
{ "_id" : ObjectId("4ec1d25d41421e2015da64f2"), "x" : 1 } |
|
{ "_id" : ObjectId("4ec1d25d41421e2015da64f3"), "x" : 2 } |
|
{ "_id" : ObjectId("4ec1d25d41421e2015da64f4"), "x" : 3 } |
|
{ "_id" : ObjectId("4ec1d25d41421e2015da64f5"), "x" : 3 } |
|
{ "_id" : ObjectId("4ec1d25d41421e2015da64f6"), "x" : 3 }</programlisting> |
|
|
|
<para>We would like to group by the only field in each row, the 'x' |
|
field and aggregate the number of times each specific value of 'x' |
|
occurs. To do this we need to create an initial document that contains |
|
our count variable and also a reduce function which will increment it |
|
each time it is encountered. The Java code to execute the group |
|
operation is shown below</para> |
|
|
|
<programlisting language="java">GroupByResults<XObject> results = mongoTemplate.group("group_test_collection", |
|
GroupBy.key("x").initialDocument("{ count: 0 }").reduceFunction("function(doc, prev) { prev.count += 1 }"), |
|
XObject.class);</programlisting> |
|
|
|
<para>The first argument is the name of the collection to run the group |
|
operation over, the second is a fluent API that specifies properties of |
|
the group operation via a <classname>GroupBy</classname> class. In this |
|
example we are using just the <methodname>intialDocument</methodname> |
|
and <methodname>reduceFunction</methodname> methods. You can also |
|
specify a key-function, as well as a finalizer as part of the fluent |
|
API. If you have multiple keys to group by, you can pass in a comma |
|
separated list of keys.</para> |
|
|
|
<para>The raw results of the group operation is a JSON document that |
|
looks like this</para> |
|
|
|
<programlisting>{ |
|
"retval" : [ { "x" : 1.0 , "count" : 2.0} , |
|
{ "x" : 2.0 , "count" : 1.0} , |
|
{ "x" : 3.0 , "count" : 3.0} ] , |
|
"count" : 6.0 , |
|
"keys" : 3 , |
|
"ok" : 1.0 |
|
}</programlisting> |
|
|
|
<para>The document under the "retval" field is mapped onto the third |
|
argument in the group method, in this case XObject which is shown |
|
below.</para> |
|
|
|
<programlisting language="java">public class XObject { |
|
|
|
private float x; |
|
|
|
private float count; |
|
|
|
|
|
public float getX() { |
|
return x; |
|
} |
|
|
|
public void setX(float x) { |
|
this.x = x; |
|
} |
|
|
|
public float getCount() { |
|
return count; |
|
} |
|
|
|
public void setCount(float count) { |
|
this.count = count; |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return "XObject [x=" + x + " count = " + count + "]"; |
|
} |
|
}</programlisting> |
|
|
|
<para>You can also obtain the raw result as a |
|
<classname>DbObject</classname> by calling the method |
|
<methodname>getRawResults</methodname> on the |
|
<classname>GroupByResults</classname> class.</para> |
|
|
|
<para>There is an additional method overload of the group method on |
|
<interfacename>MongoOperations</interfacename> which lets you specify a |
|
<classname>Criteria</classname> object for selecting a subset of the |
|
rows. An example which uses a <classname>Criteria</classname> object, |
|
with some syntax sugar using static imports, as well as referencing a |
|
key-function and reduce function javascript files via a Spring Resource |
|
string is shown below.</para> |
|
|
|
<programlisting>import static org.springframework.data.mongodb.core.mapreduce.GroupBy.keyFunction; |
|
import static org.springframework.data.mongodb.core.query.Criteria.where; |
|
|
|
GroupByResults<XObject> results = mongoTemplate.group(where("x").gt(0), |
|
"group_test_collection", |
|
keyFunction("classpath:keyFunction.js").initialDocument("{ count: 0 }").reduceFunction("classpath:groupReduce.js"), XObject.class);</programlisting> |
|
</section> |
|
</section> |
|
|
|
<section id="mongo.aggregation"> |
|
<title>Aggregation Framework Support</title> |
|
|
|
<para>Spring Data MongoDB provides support for the Aggregation Framework |
|
introduced to MongoDB in version 2.2.</para> |
|
|
|
<para>The MongoDB Documentation describes the <ulink |
|
url="http://docs.mongodb.org/manual/core/aggregation/">Aggregation |
|
Framework</ulink> as follows:<quote>The MongoDB aggregation framework |
|
provides a means to calculate aggregated values without having to use |
|
map-reduce. While map-reduce is powerful, it is often more difficult than |
|
necessary for many simple aggregation tasks, such as totaling or averaging |
|
field values.</quote></para> |
|
|
|
<para>For further information see the full <ulink |
|
url="http://docs.mongodb.org/manual/aggregation/">reference |
|
documentation</ulink> of the aggregation framework and other data |
|
aggregation tools for MongoDB.</para> |
|
|
|
<section id="mongo.aggregation.basic-concepts"> |
|
<title>Basic Concepts</title> |
|
|
|
<para>The Aggregation Framework support in Spring Data MongoDB is based |
|
on the following key abstractions <classname>Aggregation</classname>, |
|
<classname>AggregationOperation</classname> and |
|
<classname>AggregationResults</classname>.</para> |
|
|
|
<itemizedlist> |
|
<listitem> |
|
<para><classname>Aggregation</classname></para> |
|
|
|
<para>An Aggregation represents a MongoDB |
|
<methodname>aggregate</methodname> operation and holds the |
|
description of the aggregation pipline instructions. Aggregations |
|
are created by inoking the appropriate |
|
<code>newAggregation(…)</code> static factory Method of the |
|
<classname>Aggregation</classname> class which takes the list of |
|
<classname>AggregateOperation</classname> as a parameter next to the |
|
optional input class.</para> |
|
|
|
<para>The actual aggregate operation is executed by the |
|
<methodname>aggregate</methodname> method of the |
|
<classname>MongoTemplate</classname> which also takes the desired |
|
output class as parameter.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><classname>AggregationOperation</classname></para> |
|
|
|
<para>An <classname>AggregationOperation</classname> represents a |
|
MongoDB aggregation pipeline operation and describes the processing |
|
that should be performed in this aggregation step. Although one |
|
could manually create an <classname>AggregationOperation</classname> |
|
the recommended way to construct an |
|
<classname>AggregateOperation</classname> is to use the static |
|
factory methods provided by the <classname>Aggregate</classname> |
|
class.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><classname>AggregationResults</classname></para> |
|
|
|
<para><classname>AggregationResults</classname> is the container for |
|
the result of an aggregate operation. It provides access to the raw |
|
aggregation result in the form of an |
|
<classname>DBObject</classname>, to the mapped objects and |
|
information which performed the aggregation.</para> |
|
</listitem> |
|
</itemizedlist> |
|
|
|
<para>The canonical example for using the Spring Data MongoDB support |
|
for the MongoDB Aggregation Framework looks as follows:</para> |
|
|
|
<programlisting language="java">import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; |
|
|
|
Aggregation agg = newAggregation( |
|
pipelineOP1(), |
|
pipelineOP2(), |
|
pipelineOPn() |
|
); |
|
|
|
AggregationResults<OutputType> results = mongoTemplate.aggregate(agg, "INPUT_COLLECTION_NAME", OutputType.class); |
|
List<OutputType> mappedResult = results.getMappedResults(); |
|
</programlisting> |
|
|
|
<para>Note that if you provide an input class as the first parameter to |
|
the <methodname>newAggregation</methodname> method the |
|
<classname>MongoTemplate</classname> will derive the name of the input |
|
collection from this class. Otherwise if you don't not specify an input |
|
class you must provide the name of the input collection explicitly. If |
|
an input-class and an input-collection is provided the latter takes |
|
precedence.</para> |
|
</section> |
|
|
|
<section id="mongo.aggregation.supported-aggregation-operations"> |
|
<title>Supported Aggregation Operations</title> |
|
|
|
<para>The MongoDB Aggregation Framework provides the following types of |
|
Aggregation Operations:</para> |
|
|
|
<itemizedlist> |
|
<listitem> |
|
<para>Pipeline Aggregation Operators</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Group Aggregation Operators</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Boolean Aggregation Operators</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Comparison Aggregation Operators</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Arithmetic Aggregation Operators</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>String Aggregation Operators</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Date Aggregation Operators</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Conditional Aggregation Operators</para> |
|
</listitem> |
|
</itemizedlist> |
|
|
|
<para>At the time of this writing we provide support for the following |
|
Aggregation Operations in Spring Data MongoDB.</para> |
|
|
|
<table> |
|
<title>Aggregation Operations currently supported by Spring Data |
|
MongoDB</title> |
|
|
|
<tgroup cols="2"> |
|
<tbody> |
|
<row> |
|
<entry>Pipeline Aggregation Operators</entry> |
|
|
|
<entry>project, skip, limit, unwind, group, sort, |
|
geoNear</entry> |
|
</row> |
|
|
|
<row> |
|
<entry>Group Aggregation Operators</entry> |
|
|
|
<entry>addToSet, first, last, max, min, avg, push, sum, |
|
(*count)</entry> |
|
</row> |
|
|
|
<row> |
|
<entry>Arithmetic Aggregation Operators</entry> |
|
|
|
<entry>add (*via plus), subtract (*via minus), multiply, divide, |
|
mod</entry> |
|
</row> |
|
|
|
<row> |
|
<entry>Comparison Aggregation Operators</entry> |
|
|
|
<entry>eq (*via: is), gt, gte, lt, lte, ne</entry> |
|
</row> |
|
</tbody> |
|
</tgroup> |
|
</table> |
|
|
|
<para>Note that the aggregation operations not listed here are currently |
|
not supported by Spring Data MongoDB. Comparison aggregation operators |
|
are expressed as <classname>Criteria</classname> expressions.</para> |
|
|
|
<para>*) The operation is mapped or added by Spring Data MongoDB.</para> |
|
</section> |
|
|
|
<section id="mongo.aggregation.projection"> |
|
<title>Projection Expressions</title> |
|
|
|
<para>Projection expressions are used to define the fields that are the |
|
outcome of a particular aggregation step. Projection expressions can be |
|
defined via the <methodname>project</methodname> method of the |
|
<classname>Aggregate</classname> class.</para> |
|
|
|
<example> |
|
<title>Projection expression examples</title> |
|
|
|
<programlisting language="java">project("name", "netPrice") // will generate {$project: {name: 1, netPrice: 1}} |
|
project().and("foo").as("bar") // will generate {$project: {bar: $foo}} |
|
project("a","b").and("foo").as("bar") // will generate {$project: {a: 1, b: 1, bar: $foo}}</programlisting> |
|
|
|
<para>Note that more examples for project operations can be found in |
|
the <classname>AggregationTests</classname> class.</para> |
|
</example> |
|
|
|
<para>Note that further details regarding the projection expressions can |
|
be found in the <ulink |
|
url="http://docs.mongodb.org/manual/reference/operator/aggregation/project/#pipe._S_project">corresponding |
|
section</ulink> of the MongoDB Aggregation Framework reference |
|
documentation.</para> |
|
|
|
<section id="mongo.aggregation.projection.expressions"> |
|
<title>Spring Expression Support in Projection Expressions</title> |
|
|
|
<para>As of Version 1.4.0 we support the use of SpEL expression in |
|
projection expressions via the <methodname>andExpression</methodname> |
|
method of the <classname>ProjectionOperation</classname> class. This |
|
allows you to define the desired expression as a SpEL expression which |
|
is translated into a corresponding MongoDB projection expression part |
|
on query execution. This makes it much easier to express complex |
|
calculations.</para> |
|
|
|
<example> |
|
<title>Complex calculations with SpEL expressions</title> |
|
|
|
<para>The following SpEL expression:</para> |
|
|
|
<programlisting language="java">1 + (q + 1) / (q - 1)</programlisting> |
|
|
|
<para>will be translated into the following projection expression |
|
part:</para> |
|
|
|
<programlisting language="javascript">{ "$add" : [ 1, { |
|
"$divide" : [ { |
|
"$add":["$q", 1]}, { |
|
"$subtract":[ "$q", 1]} |
|
] |
|
}]}</programlisting> |
|
</example> |
|
|
|
<para>Have a look at an example in more context in <xref |
|
linkend="mongo.aggregation.examples.example5"/> and <xref |
|
linkend="mongo.aggregation.examples.example6"/>. You can find more |
|
usage examples for supported SpEL expression constructs in |
|
<classname>SpelExpressionTransformerUnitTests</classname>.</para> |
|
</section> |
|
</section> |
|
|
|
<section id="mongo.aggregation.examples"> |
|
<title>Aggregation Framework Examples</title> |
|
|
|
<para>The following examples demonstrate the usage patterns for the |
|
MongoDB Aggregation Framework with Spring Data MongoDB.</para> |
|
|
|
<example id="mongo.aggregation.examples.example1"> |
|
<title>Aggregation Framework Example 1</title> |
|
|
|
<para>In this introductory example we want to aggregate a list of tags |
|
to get the occurrence count of a particular tag from a MongoDB |
|
collection called <code>"tags"</code> sorted by the occurrence count |
|
in descending order. This example demonstrates the usage of grouping, |
|
sorting, projections (selection) and unwinding (result |
|
splitting).</para> |
|
|
|
<programlisting language="java">class TagCount { |
|
String tag; |
|
int n; |
|
}</programlisting> |
|
|
|
<programlisting language="java">import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; |
|
|
|
Aggregation agg = newAggregation( |
|
project("tags"), |
|
unwind("tags"), |
|
group("tags").count().as("n"), |
|
project("n").and("tag").previousOperation(), |
|
sort(DESC, "n") |
|
); |
|
|
|
AggregationResults<TagCount> results = mongoTemplate.aggregate(agg, "tags", TagCount.class); |
|
List<TagCount> tagCount = results.getMappedResults();</programlisting> |
|
</example> |
|
|
|
<itemizedlist> |
|
<listitem> |
|
<para>In order to do this we first create a new aggregation via the |
|
<methodname>newAggregation</methodname> static factory method to |
|
which we pass a list of aggregation operations. These aggregate |
|
operations define the aggregation pipeline of our |
|
<classname>Aggregation</classname>.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>As a second step we select the <code>"tags"</code> field |
|
(which is an array of strings) from the input collection with the |
|
<methodname>project</methodname> operation.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>In a third step we use the <methodname>unwind</methodname> |
|
operation to generate a new document for each tag within the |
|
<code>"tags"</code> array.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>In the forth step we use the <methodname>group</methodname> |
|
operation to define a group for each <code>"tags"</code>-value for |
|
which we aggregate the occurrence count via the |
|
<methodname>count</methodname> aggregation operator and collect the |
|
result in a new field called <code>"n"</code>.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>As a fifth step we select the field <code>"n"</code> and |
|
create an alias for the id-field generated from the previous group |
|
operation (hence the call to <code>previousOperation()</code>) with |
|
the name <code>"tag"</code>.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>As the sixth step we sort the resulting list of tags by their |
|
occurrence count in descending order via the |
|
<methodname>sort</methodname> operation.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Finally we call the <methodname>aggregate</methodname> Method |
|
on the MongoTemplate in order to let MongoDB perform the acutal |
|
aggregation operation with the created |
|
<classname>Aggregation</classname> as an argument.</para> |
|
</listitem> |
|
</itemizedlist> |
|
|
|
<para>Note that the input collection is explicitly specified as the |
|
<code>"tags"</code> parameter to the <methodname>aggregate</methodname> |
|
Method. If the name of the input collection is not specified explicitly, |
|
it is derived from the input-class passed as first parameter to the |
|
<methodname>newAggreation</methodname> Method.</para> |
|
|
|
<example id="mongo.aggregation.examples.example2"> |
|
<title>Aggregation Framework Example 2</title> |
|
|
|
<para>This example is based on the <ulink |
|
url="http://docs.mongodb.org/manual/tutorial/aggregation-examples/#largest-and-smallest-cities-by-state">Largest |
|
and Smallest Cities by State</ulink> example from the MongoDB |
|
Aggregation Framework documentation. We added additional sorting to |
|
produce stable results with different MongoDB versions. Here we want |
|
to return the smallest and largest cities by population for each |
|
state, using the aggregation framework. This example demonstrates the |
|
usage of grouping, sorting and projections (selection).</para> |
|
|
|
<programlisting language="java">class ZipInfo { |
|
String id; |
|
String city; |
|
String state; |
|
@Field("pop") int population; |
|
@Field("loc") double[] location; |
|
} |
|
|
|
class City { |
|
String name; |
|
int population; |
|
} |
|
|
|
class ZipInfoStats { |
|
String id; |
|
String state; |
|
City biggestCity; |
|
City smallestCity; |
|
}</programlisting> |
|
|
|
<programlisting language="java">import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; |
|
|
|
TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class, |
|
group("state", "city") |
|
.sum("population").as("pop"), |
|
sort(ASC, "pop", "state", "city"), |
|
group("state") |
|
.last("city").as("biggestCity") |
|
.last("pop").as("biggestPop") |
|
.first("city").as("smallestCity") |
|
.first("pop").as("smallestPop"), |
|
project() |
|
.and("state").previousOperation() |
|
.and("biggestCity") |
|
.nested(bind("name", "biggestCity").and("population", "biggestPop")) |
|
.and("smallestCity") |
|
.nested(bind("name", "smallestCity").and("population", "smallestPop")), |
|
sort(ASC, "state") |
|
); |
|
|
|
AggregationResults<ZipInfoStats> result = mongoTemplate.aggregate(aggregation, ZipInfoStats.class); |
|
ZipInfoStats firstZipInfoStats = result.getMappedResults().get(0); |
|
</programlisting> |
|
</example> |
|
|
|
<itemizedlist> |
|
<listitem> |
|
<para>The class <classname>ZipInfo</classname> maps the structure of |
|
the given input-collection. The class |
|
<classname>ZipInfoStats</classname> defines the structure in the |
|
desired output format.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>As a first step we use the <methodname>group</methodname> |
|
operation to define a group from the input-collection. The grouping |
|
criteria is the combination of the fields <code>"state"</code> and |
|
<code>"city" </code>which forms the id structure of the group. We |
|
aggregate the value of the <code>"population"</code> property from |
|
the grouped elements with by using the <methodname>sum</methodname> |
|
operator saving the result in the field <code>"pop"</code>.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>In a second step we use the <methodname>sort</methodname> |
|
operation to sort the intermediate-result by the fields |
|
<code>"pop"</code>, <code>"state"</code> and <code>"city"</code> in |
|
ascending order, such that the smallest city is at the top and the |
|
biggest city is at the bottom of the result. Note that the sorting |
|
on "state" and <code>"city"</code> is implicitly performed against |
|
the group id fields which Spring Data MongoDB took care of.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>In the third step we use a <methodname>group</methodname> |
|
operation again to group the intermediate result by |
|
<code>"state"</code>. Note that <code>"state"</code> again |
|
implicitly references an group-id field. We select the name and the |
|
population count of the biggest and smallest city with calls to the |
|
<code>last(…)</code> and <code>first(...)</code> operator |
|
respectively via the <methodname>project</methodname> |
|
operation.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>As the forth step we select the <code>"state"</code> field |
|
from the previous <methodname>group</methodname> operation. Note |
|
that <code>"state"</code> again implicitly references an group-id |
|
field. As we do not want an implicit generated id to appear, we |
|
exclude the id from the previous operation via |
|
<code>and(previousOperation()).exclude()</code>. As we want to |
|
populate the nested <classname>City</classname> structures in our |
|
output-class accordingly we have to emit appropriate sub-documents |
|
with the nested method.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Finally as the fifth step we sort the resulting list of |
|
<classname>StateStats</classname> by their state name in ascending |
|
order via the <methodname>sort</methodname> operation.</para> |
|
</listitem> |
|
</itemizedlist> |
|
|
|
<para>Note that we derive the name of the input-collection from the |
|
<classname>ZipInfo</classname>-class passed as first parameter to the |
|
<methodname>newAggregation</methodname>-Method.</para> |
|
|
|
<example id="mongo.aggregation.examples.example3"> |
|
<title>Aggregation Framework Example 3</title> |
|
|
|
<para>This example is based on the <ulink |
|
url="http://docs.mongodb.org/manual/tutorial/aggregation-examples/#states-with-populations-over-10-million">States |
|
with Populations Over 10 Million </ulink>example from the MongoDB |
|
Aggregation Framework documentation. We added additional sorting to |
|
produce stable results with different MongoDB versions. Here we want |
|
to return all states with a population greater than 10 million, using |
|
the aggregation framework. This example demonstrates the usage of |
|
grouping, sorting and matching (filtering).</para> |
|
|
|
<programlisting language="java">class StateStats { |
|
@Id String id; |
|
String state; |
|
@Field("totalPop") int totalPopulation; |
|
}</programlisting> |
|
|
|
<programlisting language="java">import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; |
|
|
|
TypedAggregation<ZipInfo> agg = newAggregation(ZipInfo.class, |
|
group("state").sum("population").as("totalPop"), |
|
sort(ASC, previousOperation(), "totalPop"), |
|
match(where("totalPop").gte(10 * 1000 * 1000)) |
|
); |
|
|
|
AggregationResults<StateStats> result = mongoTemplate.aggregate(agg, StateStats.class); |
|
List<StateStats> stateStatsList = result.getMappedResults();</programlisting> |
|
</example> |
|
|
|
<itemizedlist> |
|
<listitem> |
|
<para>As a first step we group the input collection by the |
|
<code>"state"</code> field and calculate the sum of the |
|
<code>"population"</code> field and store the result in the new |
|
field <code>"totalPop"</code>.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>In the second step we sort the intermediate result by the |
|
id-reference of the previous group operation in addition to the |
|
<code>"totalPop"</code> field in ascending order.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para>Finally in the third step we filter the intermediate result by |
|
using a <methodname>match</methodname> operation which accepts a |
|
<classname>Criteria</classname> query as an argument.</para> |
|
</listitem> |
|
</itemizedlist> |
|
|
|
<para>Note that we derive the name of the input-collection from the |
|
<classname>ZipInfo</classname>-class passed as first parameter to the |
|
<methodname>newAggregation</methodname>-Method.</para> |
|
|
|
<example id="mongo.aggregation.examples.example4"> |
|
<title>Aggregation Framework Example 4</title> |
|
|
|
<para>This example demonstrates the use of simple arithmetic |
|
operations in the projection operation.</para> |
|
|
|
<programlisting language="java">class Product { |
|
String id; |
|
String name; |
|
double netPrice; |
|
int spaceUnits; |
|
}</programlisting> |
|
|
|
<programlisting language="java">import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; |
|
|
|
TypedAggregation<Product> agg = newAggregation(Product.class, |
|
project("name", "netPrice") |
|
.and("netPrice").plus(1).as("netPricePlus1") |
|
.and("netPrice").minus(1).as("netPriceMinus1") |
|
.and("netPrice").multiply(1.19).as("grossPrice") |
|
.and("netPrice").divide(2).as("netPriceDiv2") |
|
.and("spaceUnits").mod(2).as("spaceUnitsMod2") |
|
); |
|
|
|
AggregationResults<DBObject> result = mongoTemplate.aggregate(agg, DBObject.class); |
|
List<DBObject> resultList = result.getMappedResults();</programlisting> |
|
</example> |
|
|
|
<para>Note that we derive the name of the input-collection from the |
|
<classname>Product</classname>-class passed as first parameter to the |
|
<methodname>newAggregation</methodname>-Method.</para> |
|
|
|
<example id="mongo.aggregation.examples.example5"> |
|
<title>Aggregation Framework Example 5</title> |
|
|
|
<para>This example demonstrates the use of simple arithmetic |
|
operations derived from SpEL Expressions in the projection |
|
operation.</para> |
|
|
|
<programlisting language="java">class Product { |
|
String id; |
|
String name; |
|
double netPrice; |
|
int spaceUnits; |
|
}</programlisting> |
|
|
|
<programlisting language="java">import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; |
|
|
|
TypedAggregation<Product> agg = newAggregation(Product.class, |
|
project("name", "netPrice") |
|
.andExpression("netPrice + 1").as("netPricePlus1") |
|
.andExpression("netPrice - 1").as("netPriceMinus1") |
|
.andExpression("netPrice / 2").as("netPriceDiv2") |
|
.andExpression("netPrice * 1.19").as("grossPrice") |
|
.andExpression("spaceUnits % 2").as("spaceUnitsMod2") |
|
.andExpression("(netPrice * 0.8 + 1.2) * 1.19").as("grossPriceIncludingDiscountAndCharge") |
|
|
|
); |
|
|
|
AggregationResults<DBObject> result = mongoTemplate.aggregate(agg, DBObject.class); |
|
List<DBObject> resultList = result.getMappedResults();</programlisting> |
|
</example> |
|
|
|
<example id="mongo.aggregation.examples.example6"> |
|
<title>Aggregation Framework Example 6</title> |
|
|
|
<para>This example demonstrates the use of complex arithmetic |
|
operations derived from SpEL Expressions in the projection |
|
operation.</para> |
|
|
|
<para>Note: The additional parameters passed to the |
|
<methodname>addExpression</methodname> Method can be referenced via |
|
indexer expressions according to their position. In this example we |
|
reference the parameter <varname>shippingCosts</varname> which is the |
|
first parameter of the parameters array via <code>[0]</code>. External |
|
parameter expressions are replaced with their respective values when |
|
the SpEL expression is transformed into a MongoDB aggregation |
|
framework expression.</para> |
|
|
|
<programlisting language="java">class Product { |
|
String id; |
|
String name; |
|
double netPrice; |
|
int spaceUnits; |
|
}</programlisting> |
|
|
|
<programlisting language="java">import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; |
|
|
|
double shippingCosts = 1.2; |
|
|
|
TypedAggregation<Product> agg = newAggregation(Product.class, |
|
project("name", "netPrice") |
|
.andExpression("(netPrice * (1-discountRate) + [0]) * (1+taxRate)", shippingCosts).as("salesPrice") |
|
); |
|
|
|
AggregationResults<DBObject> result = mongoTemplate.aggregate(agg, DBObject.class); |
|
List<DBObject> resultList = result.getMappedResults();</programlisting> |
|
</example> |
|
|
|
<para>Note that we can also refer to other fields of the document within |
|
the SpEL expression.</para> |
|
</section> |
|
</section> |
|
|
|
<section id="mongo.custom-converters"> |
|
<title>Overriding default mapping with custom converters</title> |
|
|
|
<para>In order to have more fine grained control over the mapping process |
|
you can register Spring converters with the |
|
<classname>MongoConverter</classname> implementations such as the |
|
<classname>MappingMongoConverter</classname>.</para> |
|
|
|
<para>The <classname>MappingMongoConverter</classname> checks to see if |
|
there are any Spring converters that can handle a specific class before |
|
attempting to map the object itself. To 'hijack' the normal mapping |
|
strategies of the <classname>MappingMongoConverter</classname>, perhaps |
|
for increased performance or other custom mapping needs, you first need to |
|
create an implementation of the Spring |
|
<interfacename>Converter</interfacename> interface and then register it |
|
with the MappingConverter.</para> |
|
|
|
<note> |
|
<para>For more information on the Spring type conversion service see the |
|
reference docs <ulink |
|
url="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert">here</ulink>.</para> |
|
</note> |
|
|
|
<section id="mongo.custom-converters.writer"> |
|
<title>Saving using a registered Spring Converter</title> |
|
|
|
<para>An example implementation of the |
|
<interfacename>Converter</interfacename> that converts from a Person |
|
object to a <classname>com.mongodb.DBObject</classname> is shown |
|
below</para> |
|
|
|
<programlisting language="java">import org.springframework.core.convert.converter.Converter; |
|
|
|
import com.mongodb.BasicDBObject; |
|
import com.mongodb.DBObject; |
|
|
|
public class PersonWriteConverter implements Converter<Person, DBObject> { |
|
|
|
public DBObject convert(Person source) { |
|
DBObject dbo = new BasicDBObject(); |
|
dbo.put("_id", source.getId()); |
|
dbo.put("name", source.getFirstName()); |
|
dbo.put("age", source.getAge()); |
|
return dbo; |
|
} |
|
}</programlisting> |
|
</section> |
|
|
|
<section id="mongo.custom-converters.reader"> |
|
<title>Reading using a Spring Converter</title> |
|
|
|
<para>An example implementation of a Converter that converts from a |
|
DBObject ot a Person object is shownn below</para> |
|
|
|
<programlisting language="java">public class PersonReadConverter implements Converter<DBObject, Person> { |
|
|
|
public Person convert(DBObject source) { |
|
Person p = new Person((ObjectId) source.get("_id"), (String) source.get("name")); |
|
p.setAge((Integer) source.get("age")); |
|
return p; |
|
} |
|
}</programlisting> |
|
</section> |
|
|
|
<section id="mongo.custom-converters.xml"> |
|
<title>Registering Spring Converters with the MongoConverter</title> |
|
|
|
<para>The Mongo Spring namespace provides a convenience way to register |
|
Spring <interfacename>Converter</interfacename>s with the |
|
<classname>MappingMongoConverter</classname>. The configuration snippet |
|
below shows how to manually register converter beans as well as |
|
configuring the wrapping <classname>MappingMongoConverter</classname> |
|
into a <classname>MongoTemplate</classname>.</para> |
|
|
|
<programlisting language="xml"><mongo:db-factory dbname="database"/> |
|
|
|
<mongo:mapping-converter> |
|
<mongo:custom-converters> |
|
<mongo:converter ref="readConverter"/> |
|
<mongo:converter> |
|
<bean class="org.springframework.data.mongodb.test.PersonWriteConverter"/> |
|
</mongo:converter> |
|
</mongo:custom-converters> |
|
</mongo:mapping-converter> |
|
|
|
<bean id="readConverter" class="org.springframework.data.mongodb.test.PersonReadConverter"/> |
|
|
|
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> |
|
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/> |
|
<constructor-arg name="mongoConverter" ref="mappingConverter"/> |
|
</bean></programlisting> |
|
|
|
<para>You can also use the base-package attribute of the |
|
custom-converters element to enable classpath scanning for all |
|
<interfacename>Converter</interfacename> and |
|
<interfacename>GenericConverter</interfacename> implementations below |
|
the given package.</para> |
|
|
|
<programlisting language="xml"><mongo:mapping-converter> |
|
<mongo:custom-converters base-package="com.acme.**.converters" /> |
|
</mongo:mapping-converter></programlisting> |
|
</section> |
|
|
|
<section id="mongo.converter-disambiguation"> |
|
<title>Converter disambiguation</title> |
|
|
|
<para>Generally we inspect the <interfacename>Converter</interfacename> |
|
implementations for the source and target types they convert from and |
|
to. Depending on whether one of those is a type MongoDB can handle |
|
natively we will register the converter instance as reading or writing |
|
one. Have a look at the following samples:</para> |
|
|
|
<programlisting language="java">// Write converter as only the target type is one Mongo can handle natively |
|
class MyConverter implements Converter<Person, String> { … } |
|
|
|
// Read converter as only the source type is one Mongo can handle natively |
|
class MyConverter implements Converter<String, Person> { … }</programlisting> |
|
|
|
<para>In case you write a <interfacename>Converter</interfacename> whose |
|
source and target type are native Mongo types there's no way for us to |
|
determine whether we should consider it as reading or writing converter. |
|
Registering the converter instance as both might lead to unwanted |
|
results then. E.g. a <interfacename>Converter<String, |
|
Long></interfacename> is ambiguous although it probably does not make |
|
sense to try to convert all <classname>String</classname>s into |
|
<classname>Long</classname>s when writing. To be generally able to force |
|
the infrastructure to register a converter for one way only we provide |
|
<interfacename>@ReadingConverter</interfacename> as well as |
|
<interfacename>@WritingConverter</interfacename> to be used at the |
|
converter implementation.</para> |
|
</section> |
|
</section> |
|
|
|
<section id="mongo-template.index-and-collections"> |
|
<title>Index and Collection management</title> |
|
|
|
<para><classname>MongoTemplate</classname> provides a few methods for |
|
managing indexes and collections. These are collected into a helper |
|
interface called <interfacename>IndexOperations</interfacename>. You |
|
access these operations by calling the method |
|
<methodname>indexOps</methodname> and pass in either the collection name |
|
or the <literal>java.lang.Class</literal> of your entity (the collection |
|
name will be derived from the .class either by name or via annotation |
|
metadata).</para> |
|
|
|
<para>The <interfacename>IndexOperations</interfacename> interface is |
|
shown below</para> |
|
|
|
<programlisting language="java">public interface IndexOperations { |
|
|
|
void ensureIndex(IndexDefinition indexDefinition); |
|
|
|
void dropIndex(String name); |
|
|
|
void dropAllIndexes(); |
|
|
|
void resetIndexCache(); |
|
|
|
List<IndexInfo> getIndexInfo(); |
|
}</programlisting> |
|
|
|
<section id="mongo-template.index-and-collections.index"> |
|
<title>Methods for creating an Index</title> |
|
|
|
<para>We can create an index on a collection to improve query |
|
performance.</para> |
|
|
|
<example> |
|
<title>Creating an index using the MongoTemplate</title> |
|
|
|
<programlisting language="java">mongoTemplate.indexOps(Person.class).ensureIndex(new Index().on("name",Order.ASCENDING)); </programlisting> |
|
</example> |
|
|
|
<para><itemizedlist> |
|
<listitem> |
|
<para><emphasis role="bold">ensureIndex </emphasis>Ensure that an |
|
index for the provided IndexDefinition exists for the |
|
collection.</para> |
|
</listitem> |
|
</itemizedlist></para> |
|
|
|
<para>You can create both standard indexes and geospatial indexes using |
|
the classes <classname>IndexDefinition</classname> and |
|
<classname>GeoSpatialIndex</classname> respectfully. For example, given |
|
the Venue class defined in a previous section, you would declare a |
|
geospatial query as shown below</para> |
|
|
|
<programlisting language="java">mongoTemplate.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location"));</programlisting> |
|
</section> |
|
|
|
<section id="mongo-template.index-and-collections.access"> |
|
<title>Accessing index information</title> |
|
|
|
<para>The IndexOperations interface has the method getIndexInfo that |
|
returns a list of IndexInfo objects. This contains all the indexes |
|
defined on the collectcion. Here is an example that defines an index on |
|
the Person class that has age property.</para> |
|
|
|
<programlisting language="java">template.indexOps(Person.class).ensureIndex(new Index().on("age", Order.DESCENDING).unique(Duplicates.DROP)); |
|
|
|
List<IndexInfo> indexInfoList = template.indexOps(Person.class).getIndexInfo(); |
|
|
|
// Contains |
|
// [IndexInfo [fieldSpec={_id=ASCENDING}, name=_id_, unique=false, dropDuplicates=false, sparse=false], |
|
// IndexInfo [fieldSpec={age=DESCENDING}, name=age_-1, unique=true, dropDuplicates=true, sparse=false]] |
|
</programlisting> |
|
</section> |
|
|
|
<section id="mongo-template.index-and-collections.collection"> |
|
<title>Methods for working with a Collection</title> |
|
|
|
<para>It's time to look at some code examples showing how to use the |
|
<classname>MongoTemplate</classname>. First we look at creating our |
|
first collection.</para> |
|
|
|
<example> |
|
<title>Working with collections using the MongoTemplate</title> |
|
|
|
<programlisting language="java">DBCollection collection = null; |
|
if (!mongoTemplate.getCollectionNames().contains("MyNewCollection")) { |
|
collection = mongoTemplate.createCollection("MyNewCollection"); |
|
} |
|
|
|
mongoTemplate.dropCollection("MyNewCollection"); </programlisting> |
|
</example> |
|
|
|
<para><itemizedlist> |
|
<listitem> |
|
<para><emphasis role="bold">getCollectionNames</emphasis> Returns |
|
a set of collection names.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">collectionExists</emphasis> Check to |
|
see if a collection with a given name exists.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">createCollection</emphasis> Create an |
|
uncapped collection</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">dropCollection</emphasis> Drop the |
|
collection</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><emphasis role="bold">getCollection</emphasis> Get a |
|
collection by name, creating it if it doesn't exist.</para> |
|
</listitem> |
|
</itemizedlist></para> |
|
</section> |
|
</section> |
|
|
|
<section id="mongo-template.commands"> |
|
<title>Executing Commands</title> |
|
|
|
<para>You can also get at the MongoDB driver's <classname>DB.command( |
|
)</classname> method using the <methodname>executeCommand(…)</methodname> |
|
methods on <classname>MongoTemplate</classname>. These will also perform |
|
exception translation into Spring's |
|
<classname>DataAccessException</classname> hierarchy.</para> |
|
|
|
<section id="mongo-template.commands.execution"> |
|
<title>Methods for executing commands</title> |
|
|
|
<para><itemizedlist> |
|
<listitem> |
|
<para><literal>CommandResult</literal> <emphasis |
|
role="bold">executeCommand </emphasis> <literal>(DBObject command) |
|
</literal> Execute a MongoDB command.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal>CommandResult</literal> <emphasis |
|
role="bold">executeCommand </emphasis> <literal>(String |
|
jsonCommand) </literal> Execute the a MongoDB command expressed as |
|
a JSON string.</para> |
|
</listitem> |
|
</itemizedlist></para> |
|
</section> |
|
</section> |
|
|
|
<section id="mongodb.mapping-usage.events"> |
|
<title>Lifecycle Events</title> |
|
|
|
<para>Built into the MongoDB mapping framework are several |
|
<classname>org.springframework.context.ApplicationEvent</classname> events |
|
that your application can respond to by registering special beans in the |
|
<code>ApplicationContext</code>. By being based off Spring's |
|
ApplicationContext event infastructure this enables other products, such |
|
as Spring Integration, to easily receive these events as they are a well |
|
known eventing mechanism in Spring based applications.</para> |
|
|
|
<para>To intercept an object before it goes through the conversion process |
|
(which turns your domain object into a |
|
<classname>com.mongodb.DBObject</classname>), you'd register a subclass of |
|
<classname>AbstractMongoEventListener</classname> that overrides the |
|
<code>onBeforeConvert</code> method. When the event is dispatched, your |
|
listener will be called and passed the domain object before it goes into |
|
the converter.</para> |
|
|
|
<example> |
|
<programlisting language="java">public class BeforeConvertListener extends AbstractMongoEventListener<Person> { |
|
@Override |
|
public void onBeforeConvert(Person p) { |
|
... does some auditing manipulation, set timestamps, whatever ... |
|
} |
|
} </programlisting> |
|
</example> |
|
|
|
<para>To intercept an object before it goes into the database, you'd |
|
register a subclass of |
|
<classname>org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener</classname> |
|
that overrides the <code>onBeforeSave</code> method. When the event is |
|
dispatched, your listener will be called and passed the domain object and |
|
the converted <classname>com.mongodb.DBObject</classname>.</para> |
|
|
|
<example> |
|
<programlisting language="java">public class BeforeSaveListener extends AbstractMongoEventListener<Person> { |
|
@Override |
|
public void onBeforeSave(Person p, DBObject dbo) { |
|
… change values, delete them, whatever … |
|
} |
|
} </programlisting> |
|
</example> |
|
|
|
<para>Simply declaring these beans in your Spring ApplicationContext will |
|
cause them to be invoked whenever the event is dispatched.</para> |
|
|
|
<para>The list of callback methods that are present in |
|
AbstractMappingEventListener are</para> |
|
|
|
<itemizedlist> |
|
<listitem> |
|
<para><methodname>onBeforeConvert</methodname> - called in |
|
MongoTemplate insert, insertList and save operations before the object |
|
is converted to a DBObject using a MongoConveter.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><methodname>onBeforeSave</methodname> - called in MongoTemplate |
|
insert, insertList and save operations <emphasis>before</emphasis> |
|
inserting/saving the DBObject in the database.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><methodname>onAfterSave</methodname> - called in MongoTemplate |
|
insert, insertList and save operations <emphasis>after</emphasis> |
|
inserting/saving the DBObject in the database.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><methodname>onAfterLoad</methodname> - called in MongoTempnlate |
|
find, findAndRemove, findOne and getCollection methods after the |
|
DBObject is retrieved from the database.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><methodname>onAfterConvert</methodname> - called in |
|
MongoTempnlate find, findAndRemove, findOne and getCollection methods |
|
after the DBObject retrieved from the database was converted to a |
|
POJO.</para> |
|
</listitem> |
|
</itemizedlist> |
|
</section> |
|
|
|
<section id="mongo.exception"> |
|
<title>Exception Translation</title> |
|
|
|
<para>The Spring framework provides exception translation for a wide |
|
variety of database and mapping technologies. This has traditionally been |
|
for JDBC and JPA. The Spring support for MongoDB extends this feature to |
|
the MongoDB Database by providing an implementation of the |
|
<classname>org.springframework.dao.support.PersistenceExceptionTranslator</classname> |
|
interface.</para> |
|
|
|
<para>The motivation behind mapping to Spring's <ulink |
|
url="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/dao.html#dao-exceptions">consistent |
|
data access exception hierarchy</ulink> is that you are then able to write |
|
portable and descriptive exception handling code without resorting to |
|
coding against <ulink |
|
url="http://www.mongodb.org/about/contributors/error-codes/">MongoDB error |
|
codes</ulink>. All of Spring's data access exceptions are inherited from |
|
the root <classname>DataAccessException</classname> class so you can be |
|
sure that you will be able to catch all database related exception within |
|
a single try-catch block. Note, that not all exceptions thrown by the |
|
MongoDB driver inherit from the MongoException class. The inner exception |
|
and message are preserved so no information is lost.</para> |
|
|
|
<para>Some of the mappings performed by the |
|
<classname>MongoExceptionTranslator</classname> are: com.mongodb.Network |
|
to DataAccessResourceFailureException and |
|
<classname>MongoException</classname> error codes 1003, 12001, 12010, |
|
12011, 12012 to <classname>InvalidDataAccessApiUsageException</classname>. |
|
Look into the implementation for more details on the mapping.</para> |
|
</section> |
|
|
|
<section id="mongo.executioncallback"> |
|
<title>Execution callbacks</title> |
|
|
|
<para>One common design feature of all Spring template classes is that all |
|
functionality is routed into one of the templates execute callback |
|
methods. This helps ensure that exceptions and any resource management |
|
that maybe required are performed consistency. While this was of much |
|
greater need in the case of JDBC and JMS than with MongoDB, it still |
|
offers a single spot for exception translation and logging to occur. As |
|
such, using thexe execute callback is the preferred way to access the |
|
MongoDB driver's <classname>DB</classname> and |
|
<classname>DBCollection</classname> objects to perform uncommon operations |
|
that were not exposed as methods on |
|
<classname>MongoTemplate</classname>.</para> |
|
|
|
<para>Here is a list of execute callback methods.</para> |
|
|
|
<para><itemizedlist> |
|
<listitem> |
|
<para><literal><T> T</literal> <emphasis role="bold">execute |
|
</emphasis> <literal>(Class<?> entityClass, |
|
CollectionCallback<T> action) </literal> Executes the given |
|
CollectionCallback for the entity collection of the specified |
|
class.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal><T> T</literal> <emphasis role="bold">execute |
|
</emphasis> <literal>(String collectionName, |
|
CollectionCallback<T> action) </literal> Executes the given |
|
CollectionCallback on the collection of the given name.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal><T> T</literal> <emphasis role="bold">execute |
|
</emphasis> <literal>(DbCallback<T> action) Spring Data |
|
MongoDB provides support for the Aggregation Framework introduced to |
|
MongoDB in version 2.2.</literal> Executes a DbCallback translating |
|
any exceptions as necessary.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal><T> T</literal> <emphasis role="bold">execute |
|
</emphasis> <literal>(String collectionName, DbCallback<T> |
|
action) </literal> Executes a DbCallback on the collection of the |
|
given name translating any exceptions as necessary.</para> |
|
</listitem> |
|
|
|
<listitem> |
|
<para><literal><T> T</literal> <emphasis |
|
role="bold">executeInSession </emphasis> |
|
<literal>(DbCallback<T> action) </literal> Executes the given |
|
DbCallback within the same connection to the database so as to |
|
ensure consistency in a write heavy environment where you may read |
|
the data that you wrote.</para> |
|
</listitem> |
|
</itemizedlist></para> |
|
|
|
<para>Here is an example that uses the |
|
<interfacename>CollectionCallback</interfacename> to return information |
|
about an index</para> |
|
|
|
<programlisting language="java">boolean hasIndex = template.execute("geolocation", new CollectionCallbackBoolean>() { |
|
public Boolean doInCollection(Venue.class, DBCollection collection) throws MongoException, DataAccessException { |
|
List<DBObject> indexes = collection.getIndexInfo(); |
|
for (DBObject dbo : indexes) { |
|
if ("location_2d".equals(dbo.get("name"))) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
});</programlisting> |
|
</section> |
|
|
|
<section id="gridfs"> |
|
<title>GridFS support</title> |
|
|
|
<para>MongoDB supports storing binary files inside it's filesystem GridFS. |
|
Spring Data MongoDB provides a |
|
<interfacename>GridFsOperations</interfacename> interface as well as the |
|
according implementation <classname>GridFsTemplate</classname> to easily |
|
interact with the filesystem. You can setup a |
|
<classname>GridFsTemplate</classname> instance by handing it a |
|
<interfacename>MongoDbFactory</interfacename> as well as a |
|
<interfacename>MongoConverter</interfacename>:</para> |
|
|
|
<example> |
|
<title>JavaConfig setup for a GridFsTemplate</title> |
|
|
|
<programlisting language="java">class GridFsConfiguration extends AbstractMongoConfiguration { |
|
|
|
// … further configuration omitted |
|
|
|
@Bean |
|
public GridFsTemplate gridFsTemplate() { |
|
return new GridFsTemplate(mongoDbFactory(), mappingMongoConverter()); |
|
} |
|
}</programlisting> |
|
</example> |
|
|
|
<para>An according XML configuration looks like this:</para> |
|
|
|
<example> |
|
<title>XML configuration for a GridFsTemplate</title> |
|
|
|
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:mongo="http://www.springframework.org/schema/data/mongo" |
|
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo |
|
http://www.springframework.org/schema/data/mongo/spring-mongo.xsd |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<mongo:db-factory id="mongoDbFactory" dbname="database" /> |
|
<mongo:mapping-converter id="converter" /> |
|
|
|
<bean class="org.springframework.data.mongodb.gridfs.GridFsTemplate"> |
|
<constructor-arg ref="mongoDbFactory" /> |
|
<constructor-arg ref="converter" /> |
|
</bean> |
|
|
|
</beans></programlisting> |
|
</example> |
|
|
|
<para>The template can now be injected and used to perform storage and |
|
retrieval operations.</para> |
|
|
|
<example> |
|
<title>Using GridFsTemplate to store files</title> |
|
|
|
<programlisting language="java">class GridFsClient { |
|
|
|
@Autowired |
|
GridFsOperations operations; |
|
|
|
@Test |
|
public void storeFileToGridFs { |
|
|
|
FileMetadata metadata = new FileMetadata(); |
|
// populate metadata |
|
Resource file = … // lookup File or Resource |
|
|
|
operations.store(file.getInputStream(), "filename.txt", metadata); |
|
} |
|
}</programlisting> |
|
</example> |
|
|
|
<para>The <methodname>store(…)</methodname> operations take an |
|
<interfacename>InputStream</interfacename>, a filename and optionally |
|
metadata information about the file to store. The metadata can be an |
|
arbitrary object which will be marshalled by the |
|
<interfacename>MongoConverter</interfacename> configured with the |
|
<classname>GridFsTemplate</classname>. Alternatively you can also provide |
|
a <interfacename>DBObject</interfacename> as well.</para> |
|
|
|
<para>Reading files from the filesystem can either be achieved through the |
|
<methodname>find(…)</methodname> or |
|
<methodname>getResources(…)</methodname> methods. Let's have a look at the |
|
<methodname>find(…)</methodname> methods first. You can either find a |
|
single file matching a <classname>Query</classname> or multiple ones. To |
|
easily define file queries we provide the |
|
<classname>GridFsCriteria</classname> helper class. It provides static |
|
factory methods to encapsulate default metadata fields (e.g. |
|
<methodname>whereFilename()</methodname>, |
|
<methodname>whereContentType()</methodname>) or the custom one through |
|
<methodname>whereMetaData()</methodname>.</para> |
|
|
|
<example> |
|
<title>Using GridFsTemplate to query for files</title> |
|
|
|
<programlisting language="java">class GridFsClient { |
|
|
|
@Autowired |
|
GridFsOperations operations; |
|
|
|
@Test |
|
public void findFilesInGridFs { |
|
List<GridFSDBFile> result = operations.find(query(whereFilename().is("filename.txt"))) |
|
} |
|
}</programlisting> |
|
</example> |
|
|
|
<note> |
|
<para>Currently MongoDB does not support defining sort criterias when |
|
retrieving files from GridFS. Thus any sort criterias defined on the |
|
<classname>Query</classname> instance handed into the |
|
<methodname>find(…)</methodname> method will be disregarded.</para> |
|
</note> |
|
|
|
<para>The other option to read files from the GridFs is using the methods |
|
introduced by the <interfacename>ResourcePatternResolver</interfacename> |
|
interface. They allow handing an Ant path into the method ar thus retrieve |
|
files matching the given pattern.</para> |
|
|
|
<example> |
|
<title>Using GridFsTemplate to read files</title> |
|
|
|
<programlisting language="java">class GridFsClient { |
|
|
|
@Autowired |
|
GridFsOperations operations; |
|
|
|
@Test |
|
public void readFilesFromGridFs { |
|
GridFsResources[] txtFiles = operations.getResources("*.txt"); |
|
} |
|
}</programlisting> |
|
</example> |
|
|
|
<para><interfacename>GridFsOperations</interfacename> extending |
|
<interfacename>ResourcePatternResolver</interfacename> allows the |
|
<classname>GridFsTemplate</classname> e.g. to be plugged into an |
|
<interfacename>ApplicationContext</interfacename> to read Spring Config |
|
files from a MongoDB.</para> |
|
</section> |
|
</chapter>
|
|
|