Community contributed extensions

Updates in 1.1c:

Updates in 1.1b:

Updates in 1.1a:

Morphia module

The Morphia module bridge play.db.Model to mongodb.

This module depends on morphia project hosted on googlecode

Motivation

In the begining I want to use existing play-mongo plugin. However it lack some features I need, including:

I’ve come across with morphia project and feel it is exactly what I need. Juust need to write some code to bridge it to existing data model of play framework. And it comes out this plugin.

features

Morphia module provides complete bridge from play.db.Model to mongo, meaning your application needs very limited change to migrate from JPA/RDBMS to mongodb:

style query. However SQL/JQL query not supported. Take a look at the YABE sample code to see how things like "Post.find(“postedAt < ? order by postedAt desc”, postedAt).first();“ be translated into ”(Post) Post.filter(“postedAt <”, postedAt).order(“-postedAt”).get();". There is also an example in Tag.java showing you how SQL aggregation is completed using mongodb mapReduce. Note, you can’t simply use Fixtures.deleteXX() methods to get rid of testing data, use MorphiaFixtures.deleteXX() instead.

if you like use Long (an obvious motivation is CRUD’s route file recongnize number only...), you are free to do it. Just add this line in your application.conf: “morphia.id.type=Long” Morphia will handle everything for you Long type ID generation. You are free to mark your own Id field with any type (better to be String) like Tag.name. In that case, Morphia will
not manage your Id field

http request params.

Limitation

public List<play.db.Model> fetch(int offset, int size, String orderBy, String order, List<String> searchFields, String keywords, String where);

TODO List

Dependencies

You really really need the latest Play (even late than 1.1beta) and morphia@googlecode (even late than morphia-0.96-SNAPSHOT.jar). In the progress of development, there are many interactions with Play and morphia team and have some code changes in the latest trunk of both product. It’s safe to use the morphia-0.96-SNAPSHOT.jar file included in this plugin. But don’t use the one distibuted at http://code.google.com/p/morphia/.

Usage

Configuration

## Morphia module configuration
# load morphia module
module.morphia=${play.path}/modules/morphia
# where your mongodb server located?
morphia.db.host=ckweb
# what's your mongodb server port
morphia.db.port=27017 
# what's your database name
morphia.db.name=yabe
# Authentication to your mongodb server
#morphia.db.username=user
#morphia.db.password=pass
# configure your ID field type
# could be either ObjectId or Long, default to ObjectId
morphia.id.type=Long

Create domain model using Morphia

package models;
 
import play.data.validation.Email;
import play.data.validation.Required;
import play.modules.morphia.Model;

import com.google.code.morphia.annotations.Entity;
 
@Entity
public class User extends Model {
 
    @Email
    @Required
    public String email;
    
    @Required
    public String password;
    
    public String fullname;
    
    public boolean isAdmin;
    
    public User(String email, String password, String fullname) {
        this.email = email;
        this.password = password;
        this.fullname = fullname;
    }
    
    public static User connect(String email, String password) {
        return find("byEmailAndPassword", email, password).first();
    }
    
    public String toString() {
        return email;
    }
 
}

User.java is almost not changed from what it is in Play sample_and_tests. The only differences is Model is now play.modules.morphia.Model and Entity becomes com.google.code.morphia.annotations.Entity

Migrate JQL query to MongoDB query

JPA Style

public Post previous() {
        return Post.find("postedAt < ? order by postedAt desc", postedAt).first();
    } 
    

Morphia style:

public Post previous() {
        return (Post) Post.filter("postedAt <", postedAt).order("-postedAt").get();
    }

Aggregation with MapReduce

JPA style:

public static List<Map> getCloud() {
        List<Map> result = Tag.find(
            "select new map(t.name as tag, count(p.id) as pound) from Post p join p.tags as t group by t.name"
        ).fetch();
        return result;
    }

h4.. Morphia style:

private static final String m_ = "function() {this.tags.forEach(function (t) {emit (t, {count:1});});}";
    private static final String r_ = "function(v, vs){var t=0;for(var i=0;i<vs.length;++i){t+=vs[i].count}return{tag: v, count:t}}";
    public static List<Map<String, Integer>> getCloud(Query<? extends Model> query) {
        List<Map<String, Integer>> result = new ArrayList<Map<String, Integer>>();
        Datastore ds = MorphiaPlugin.ds();
        DBCollection dbCol = ds.getCollection(Post.class);
        MapReduceOutput out = dbCol.mapReduce(m_, r_, null, query == null ? null : ((QueryImpl<? extends Model>)query).getQueryObject());
        for (Iterator<DBObject> itr = out.results().iterator(); itr.hasNext();) {
            DBObject dbo = itr.next();
            DBObject k_v = (DBObject)dbo.get("value");
            Map<String, Integer> m = new HashMap<String, Integer>();
            m.put((String)k_v.get("tag"), ((Double)k_v.get("count")).intValue());
            result.add(m);
        }
        return result;
    }
    

This part looks not so elegant. I hope it could be simplified in the future.

FAQ

Why do I get the following error while running my play app in prod mode?

play.exceptions.JavaExecutionException: Cannot load fixture initial-data.yml: The JPA context is not initialized. JPA Entity Manager automatically start when one or more classes annotated with the @javax.persistence.Entity annotation are found in the application.
        at play.jobs.Job.call(Job.java:119)
        at Invocation.Job(Play!)
Caused by: java.lang.RuntimeException: Cannot load fixture initial-data.yml: The JPA context is not initialized. JPA Entity Manager automatically start when one
 or more classes annotated with the @javax.persistence.Entity annotation are found in the application.
        at play.test.Fixtures.load(Fixtures.java:214)
        at Bootstrap.doJob(Bootstrap.java:21)
        at play.jobs.Job.doJobWithResult(Job.java:37)
        at play.jobs.Job.call(Job.java:110)
        

This problem happens on Play v1.1-beta1. Please switch to latest 1.1 version

How to create an “OR” relation query?

Query q = myPost.createQuery(); // create a Query
    OrBuilder or = q.or(); // get Or query builder
    or.add().field("title").contains("mongodb"); // select title contains "mongodb"
    or.add().field("content").contains("mongodb"); // or content contains "mongodb"    or.add().field("comment").contains("mongodb"); // or c
    List<play.db.Model> l = new ArrayList<play.db.Model>();
    l.add(q.order("-postedAt").limit(10).asList()); // sort the result by postedAt desc order and fetch at most 10 items
    return l;

How to do aggregation?

Scroll up this page until you see “Aggregation with MapReduce”.

There is a lot of compilation and runtime errors with this plugin!

Make sure you have checked “Dependences” section already

Why do you choose Morphia? I’ve heard that GuiceyMongo is faster

Well the goal of Play-Morphia is to provide a way to help Play app developer to migrate their existing apps from RDBMS (via JPA) to MongoDB with least effort. Morphia is by far the best thing I can find provides JPA style (annotation) access to mongodb. If you are developing new Play application, and would like to try a different way, GuiceyMongo might be a good choice for you.

How to create Entity with user defined @Id field?

@Override public Object getId() {return name;}
    @Override protected void setId_(Object id) {name = id.toString();}
    protected static Object processId_(Object id) {return id.toString();}

Make sure you always populated your @Id field before calling model.save(), don’t let framework to generate Id for you b/c framework has no knowledge on how to do it

It’s better to keep your @Id field type to be java.lang.String.

I got “Can not use dot-notation past ...” exception when I try to query entity using relationship, what happened?

Basically this error occurred when you try to query an entity using referenced entity property. E.g. Post p = Post.filter(“author.email”, “[email protected]”).first(). The solution is:

User bob = User.filter("email", "[email protected]").first();
Post p = Post.filter("author", bob).first();

If the relationship is embedded rather than reference, you can safely use “dot” notation to do the query

h3. How do I know if an entity object is an new object just constructed in the memory or represents a data (either by save or load from db) in a db?

Use obj.isNew()

I try to call Fixtures.deleteAll() in my unit test setup methods, but I found the data is still there, what happened?

The deleteAll() methods defined in play.test.Fixtures are hooked with JPA based database, try to use MorphiaFixtures.deleteAll() instead. Actually you are encouraged to use MorphiaFixtures in replace of Fixtures to deal with your Morphia models for all methods call.