Every repository with this icon (
Every repository with this icon (
| name | age | message | |
|---|---|---|---|
| |
README.md | Sat Sep 05 14:48:45 -0700 2009 | |
| |
project/ | Tue Sep 01 15:05:49 -0700 2009 | |
| |
src/ | Sat Sep 05 14:48:45 -0700 2009 |
Surf
A CouchDB wrapper for Scala.
Vision, design, motivation
- Object pollution (classes must expose
_idand_rev, JSON documents will automatically be added a$$type$$property as a "type hint"). This is intended, intrusiveness for convenience.
Getting started
You need to have SBT installed. Please follow setup instructions.
git clone git://github.com/frank06/surf.git
cd surf
sbt
> update
> package
Note You can run tests by executing sbt test from the command line. Make sure you're running CouchDB in localhost, port 5984.
Features by example
Fire a Scala console (within SBT):
> console
Note It won't work when retrieving classes defined in the REPL (it's OK to save). Use BlogPost and BlogComment (dummy entities for testing purposes) both defined in SurfSpec.scala—where you can have a look for an overview of the API.
basic CRUD operations
scala> import surf._ scala> val db = Surf('blog_test) // note you can also use it like Surf("server", port, 'mydb) scala> val post = new BlogPost scala> db save post scala> val id = post._id scala> val post2: BlogPost = db get id scala> assert(post == post2) scala> val temp = db save new BlogPost scala> val id2 = temp._id scala> db delete tempfind by type
scala> val posts = db.find[BlogPost] scala> assert(posts(0) == post)conditional get (when provided the last revision for a given document, CouchDB will return an HTTP 304 Not Modified - equivalent to a None in surf)
scala> val option: Option[BlogPost] = db.conditionalGet(post._id, post._rev) scala> assert (option == None)ability to create relationships between documents via the
@BelongsToannotation (seeBlogComment's source)support for persisting mutable and immutable entities with arg-ed constructors (via Paranamer, waiting for named params reflection in Scala.)
scala> val comment = new BlogComment("my comment") scala> comment.post_=(post) scala> db save comment scala> val comment2: BlogComment = db.queryRelationship[BlogComment]('post, post._id).first
Nice, but now you will get a surf.SurfException: Http error code: 404; {"error":"not_found","reason":"missing_named_view"}. So you actually need to generate and push those views manually (until we find a better way). Easy peasy:
scala> db generateViewsForRelationships(classOf[BlogComment]) // which takes a Class varargs; now try again:
scala> val comment2: BlogComment = db.queryRelationship[BlogComment]('post, post._id).first
scala> assert(comment == comment2)
- duck typing / structural type (for
_idand_revaccessors) so that you don't have to mix-in any proprietary trait- you can use provided
Identifiable(mutable trait - which is used byBlogPostandBlogComment) - an example of an immutable class that conforms to type
Couchable(although Surf will update the_revvia reflection anyway):
- you can use provided
annotate with
@Skipany fields that shouldn't be persistedscala> import surf.annotation._ scala> case class GravyTrain(val _id: String, val text: String) { val _rev: String = null; @Skip var aantal: Int = _ } scala> db save GravyTrain("awesome", "16 blue ponies, 21 jetplanes, and 12 spinning midgets") scala> // unfortunately we can't retrieve this REPL-class object... but check out in Futon: "aantal" property is not persisted!manage
DesignDocumentswith any number of Map/reduce functions, and transforming results via>>scala> val dd = new DesignDocument scala> dd._id_=("_design/dd") scala> dd mapreduce_+ MapReduce('test, "function(doc) { }", None) scala> val map = """function(doc){if(doc._attachments) {emit(1,1);} else {emit(0,1);}}""" scala> val reduce = """function(keys, values) { return sum(values); }""" scala> dd mapreduce_+ MapReduce('attachments, map, Some(reduce)) scala> db save dd scala> val result = db queryView('dd, 'attachments, group(true)) scala> result >> { x => (x.asInstanceOf[Map[String, Any]])("value").asInstanceOf[BigInt] } firstJSON based on a thin wrapper around the super-fast lift-json
basic fulltext support through couchdb-lucene support via
queryFulltext(of course you need it installed, more documentation coming soon)scala> db queryFulltext('surf, 'text, q("jij"))
Issues?
Please report issues and contact me on Github. You can also reach me on twitter @frank06.
TO DO
- better API for design documents
- tighter integration with lift-json (especially nifty features such as extract, filter, etc)
- support persisting entities with inner classes, enums; better architecture to plug-in class support like
java.util.Date, java collections, etc - bulk docs / all or nothing support + attachments + stick to http://wiki.apache.org/couchdb/API_Cheatsheet
- support for refactoring/migrations... ideas here?
- authentification







