Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Play 2 Couchbase

Play 2 Couchbase

mathieuancelin

September 20, 2013
Tweet

More Decks by mathieuancelin

Other Decks in Programming

Transcript

  1. Mathieu ANCELIN • Ingénieur d’étude @SERLI • Java & OSS

    • JOnAS, GlassFish, Weld, etc ... • Poitou-Charentes JUG • Membre de l’expert group CDI 1.1 (JSR-346) • Membre de l’expert group OSGi Enterprise • @TrevorReznik
  2. SERLI • Société de conseil et d’ingénierie du SI •

    65 personnes • 80% de business Java • Contribution a des projets OSS • 10% de la force de travail sur l’OSS • Membre de l’EG JSR-346 • Membre de l’OSGi Alliance • www.serli.com @SerliFr
  3. SERLI • Société de conseil et d’ingénierie du SI •

    65 personnes • 80% de business Java • Contribution a des projets OSS • 10% de la force de travail sur l’OSS • Membre de l’EG JSR-346 • Membre de l’OSGi Alliance • www.serli.com @SerliFr
  4. Couchbase • Base de donnée NoSQL orientée documents • cache

    implémentant le protocole memcached • Fonctionnalités avancées sur documents JSON • Temps de lecture et écriture consistant • Changements de topologie sans downtime • Requêtage par vues • représentation des données pour le requêtage construite à l’insertion • notion de map/reduce • vues écrites en Javascript
  5. Play2 + Couchbase • Driver Java pour Couchbase disponible •

    basé sur Netty, Spymemcached, etc ... • API très complète • mais très Java-ish • Mais quel est le problème ???
  6.  CouchbaseClient  client  =              

                   new  CouchbaseClient(hosts,  bucket,  password);    client.set("my-­‐first-­‐document",  "Hello  Couchbase!").get();    client.get("my-­‐first-­‐document").get(); Java Driver : a future issue
  7.  CouchbaseClient  client  =              

                   new  CouchbaseClient(hosts,  bucket,  password);    client.set("my-­‐first-­‐document",  "Hello  Couchbase!").get();    client.get("my-­‐first-­‐document").get(); Java Driver : a future issue
  8. play2-couchbase • Plugin Play 2 • Configuration centralisée • Plusieurs

    buckets configurables • Utilisation massive des mécanismes d’asynchronisme de Play 2 • Futures, Promises, Enumerators • transformation de futures Java en future Scala • contribution au driver Java ? • Plusieurs plugins pour faciliter la vie du développeur • API Java & Scala • Accès au driver natif si besoin
  9. Configuration couchbase  {    buckets  =  [{      

     host="127.0.0.1"        port="8091"        base="pools"        bucket="mybucket"        pass=""        timeout="0"    }] } val  main  =  play.Project(appName,  appVersion,  appDependencies).settings(    appDependencies  +=  "org.ancelin.play2.couchbase"  %%  "play2-­‐couchbase"  %  "0.1-­‐SNAPSHOT",    resolvers  +=  "ancelin"  at          "https://raw.github.com/mathieuancelin/play2-­‐couchbase/master/repository/snapshots",    resolvers  +=  "Spy  Repository"  at  "http://files.couchbase.com/maven2"           )
  10. Depuis un contrôleur case  class  User(name:  String,  surname:  String,  email:

     String) object  UserController  extends  Controller  with  CouchbaseController  {    implicit  val  userReader  =  Json.reads[User]    def  getUser(key:  String)  =  CouchbaseAction("mybucket")  {  bucket  =>        bucket.get[User](key).map  {  maybeUser  =>            maybeUser.map  {  user  =>                Ok(views.html.user(user))                  .getOrElse(BadRequest(s"Unable  to  find  user  with  key:  $key"))            }        }    } }
  11. Depuis un contrôleur public  class  Application  extends  Controller  {  

     public  static  CouchbaseBucket  bucket  =  Couchbase.bucket("mybucket");    public  static  Result  getUser(final  String  key)  {        return  async(            bucket.get(key,  User.class).map(user  -­‐>  {                if  (user  ==  null)  {                    return  badRequest(                                  "Unable  to  find  user  with  key:  "  +  key);                }                return  ok(views.html.user.render(user));            })        );    } }
  12. Depuis un modèle case  class  Beer(id:  String,  name:  String,  brewery:

     String) object  Beer  {    implicit  val  beerFmt  =  Json.format[Beer]    val  bucket  =  Couchbase.bucket("mybucket")    def  findById(id:  String):  Future[Option[Beer]]  =  bucket.get[Beer](id)    def  save(beer:  Beer):  Future[OperationStatus]  =  bucket.set[Beer](beer)    def  remove(beer:  Beer):  Future[OperationStatus]  =  bucket.delete[Beer](beer)    def  findAll():  Future[List[Beer]]  =          bucket.find[Beer]("beer",  "by_name")                                        (new  Query().setIncludeDocs(true).setStale(Stale.FALSE))    def  findByName(name:  String):  Future[Option[Beer]]  =  {        val  query  =  new  Query().setIncludeDocs(true).setLimit(1)                    .setRangeStart(ComplexKey.of(name))                    .setRangeEnd(ComplexKey.of(s"$name\uefff").setStale(Stale.FALSE))        bucket.find[Beer]("beer",  "by_name")(query).map(_.headOption)    } }
  13. CRUD case  class  User(id:  String,  email:  String) object  User  {

       implicit  val  fmt  =  Json.format[User] } object  UserController  extends  CouchbaseCrudSourceController[User]  {    val  bucket  =  Couchbase.bucket("mybucket") }
  14. CRUD -­‐>            /users    

         controllers.UserController GET          /users/?doc=docName&view=viewName                                 POST        /users/                                                                                     GET          /users/{id}                                                                             PUT          /users/{id}                                                                             PUT          /users/{id}/partial                                                             DELETE    /users/{id}                                                                             POST        /users/find/?doc=docName&view=viewName&q=query       GET          /users/stream/?doc=docName&view=viewName&q=query   POST        /users/batch                                                                           PUT          /users/batch                                                                           DELETE    /users/batch                                                                          
  15. Couchbase as Cache 500:org.ancelin.play2.couchbase.plugins.CouchbaseCachePlugin couchbase  {    cache  {  

         bucket="mybucket"        enabled=true        namespace="play-­‐cache."    } } Cache.set("item.key",  connectedUser) val  maybeUser:  Option[User]  =  Cache.getAs[User]("item.key")
  16. Evolutions 600:org.ancelin.play2.couchbase.plugins.CouchbaseEvolutionsPlugin couchbase  {    evolutions  {      

     default  {            apply  =  true        }    } } {    "name":"my_doc",    "views":{        "withMedia":  {            "map":  "function  (doc,  meta)  {                              if(doc.entities.media)                    emit(dateToArray(doc.created_at))                                    }"        }    } }
  17. Fixtures 700:org.ancelin.play2.couchbase.plugins.CouchbaseFixturesPlugin couchbase  {    fixtures  {      

     mybucket  {              insert  =  true            key  =  "_id"                    }    } } [        {                "_id":  "1",                "name":  "John  Doe",                "email":  "[email protected]",                "password":  "password"        },        {                "_id":  "2",                "name":  "Jane  Doe",                "email":  "[email protected]",                "password":  "password"        } ]
  18. N1QL 1000:org.ancelin.play2.couchbase.plugins.CouchbaseN1QLPlugin couchbase  {      n1ql  {    

         host="127.0.0.1"          port=8093      } } case  class  Person(name:  String,  age:  Int) object  N1QLController  extends  Controller  {    implicit  val  personFormat  =  Json.format[Person]    def  find(age:  Int)  =  Action  {        Async  {            N1QL(  s"""  SELECT  name,  age  FROM  tutorial  WHERE  age  >  ${age}  """  )                                                                                                      .toList[Person].map  {  persons  =>                Ok(views.html.index(s"Persons  older  than  ${age}",  persons))            }        }    } }