2010-11-10

MongoDB with Querydsl

Querydsl provides a typesafe querying layer on top of JPA, JDO, JDBC and other backends. This blog post presents a simple tutorial on how to get started with querying on MongoDB using Querydsl.

Maven integration


To get started with Querydsl for MongoDB using a Maven 2 based build environment, follow the following steps.

Add the following dependencies to your Maven project and make sure that the Maven 2 repo of Mysema Source is accessible from your POM :

<dependency>
  <groupId>com.mysema.querydsl</groupId>
  <artifactId>querydsl-mongodb</artifactId>
  <version>${querydsl.version}</version>
</dependency>

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.6.1</version>
</dependency>   

Querydsl uses the Annotation Processing Tool of Java 6 for code generation which needs to be configured as well :

<project>
  <build>
  <plugins>
    ...
    <plugin>
      <groupId>com.mysema.maven</groupId>
      <artifactId>maven-apt-plugin</artifactId>
      <version>1.0</version>
      <executions>
        <execution>
          <goals>
            <goal>process</goal>
          </goals>
          <configuration>
            <outputDirectory>${generatedSources}<outputDirectory>
            <processor>com.mysema.query.mongodb.morphia.MorphiaAnnotationProcessor</processor>
          </configuration>
        </execution>
      </executions>
    </plugin>
    ...
  </plugins>
  </build>
</project>

The MorphiaAnnotationProcessor finds domain types annotated with the com.google.code.morphia.annotations.Entity and com.google.code.morphia.annotations.Embedded annotations and generates query types for them.

Run clean install and you will get your Query types generated into target/generated-sources/java.

If you use Eclipse, run mvn eclipse:eclipse to update your Eclipse project to include target/generated-sources/java as a source folder.

Now you are able to construct Querydsl query instances and instances of the query domain model.


Ant integration


If you have an Ant based build environment you can use one of the packaged releases from the Downloads page.

Place the jar files from the full-deps bundle on your classpath and use the following tasks for Querydsl code generation :

<!-- APT based code generation -->
   <javac srcdir="${src}" classpathref="cp">
     <compilerarg value="-proc:only"/>      
     <compilerarg value="-processor"/>
     <compilerarg value="com.mysema.query.mongodb.morphia.MorphiaAnnotationProcessor"/>
     <compilerarg value="-s"/>
     <compilerarg value="${generated}"/>
   </javac>
    
   <!-- compilation -->
   <javac classpathref="cp" destdir="${build}">      
     <src path="${src}"/>
     <src path="${generated}"/>
   </javac> 

Replace src with your main source folder, generated with your folder for generated sources and build with your target folder.

Using query types


To create queries with Querydsl you need to instantiate variables and Query implementations. We will start with the variables.

Let's assume that your project has the following domain type :

@Entity
public class Customer {

    private @Id ObjectId id;

    private String firstName;
    
    private String lastName;

    public String getFirstName(){
        return firstName;
    }

    public String getLastName(){
        return lastName;
    }

    public void setFirstName(String fn){
        firstName = fn;
    }

    public void setLastName(String ln)[
        lastName = ln;
    }

    public ObjectId getId() {
        return id;
    }

    public void setId(ObjectId id) {
        this.id = id;
    }
}

Querydsl will generate a query type with the simple name QCustomer into the same package as Customer. QCustomer can be used as a statically typed variable in Querydsl queries as a representative for the Customer type.

QCustomer has a default instance variable which can be accessed as a static field :

QCustomer customer = QCustomer.customer;

Alternatively you can define your own Customer variables like this :

QCustomer customer = new QCustomer("myCustomer");

Querying


For the MongoDB-module MorphiaQuery is the main Query implementation. It is instantiated like this :

// test database
String dbname = "testdb"; 
// Morphia configuration
Morphia morphia = new Morphia().map(Customer.class); 
// datastore
Datastore ds = morphia.createDatastore(dbname); 
QCustomer customer = QCustomer.customer;
MorphiaQuery<Customer> query = new MorphiaQuery<Customer>(morphia, ds, customer);

To retrieve the customer with the first name Bob you would construct a query like this :

QCustomer customer = QCustomer.customer;
MorphiaQuery<Customer> query = new MorphiaQuery<Customer>(morphia, ds, customer);
Customer bob = query.where(customer.firstName.eq("Bob"))
  .uniqueResult();

The where part defines the filter and uniqueResult tells Querydsl to return a single element. Easy, right?

And to use multiple filters use it like this

query
    .where(customer.firstName.eq("Bob"), customer.lastName.eq("Wilson"));   

Or like this

query
    .where(
      customer.firstName.eq("Bob").and(customer.lastName.eq("Wilson")));


General usage


Use the the cascading methods of the MorphiaQuery class like this

  • where : Define the query filters, either in varargs form separated via commas or cascaded via the and-operator.
  • orderBy : Define the ordering of the result as an varargs array of order expressions. Use asc() and desc() on numeric, string and other comparable expression to access the OrderSpecifier instances.
  • limit, offset, restrict : Define the paging of the result. Limit for max results, offset for skipping rows and restrict for defining both in one call.

Ordering


The syntax for declaring ordering is

query
    .orderBy(customer.lastName.asc(), customer.firstName.desc())
    .list();


Where to go next


For an up-to-date guide on how to use Querydsl with MongoDB, see the latest Reference Documentation.

And if you have any issues, questions or ideas for enhancement post a thread in the Mysema Source Forum.

Happy querying!