Skip to content

Commit

Permalink
Fix handing over json to rhino by serializing it from the native java…
Browse files Browse the repository at this point in the history
… objects first.

Possible optimizations:
* make the dict and array wrappers work so they convert their children on demand (Thus only what is actually accessed will be converted)
* hand in the actual json from the db directly, that way js still has to parse it, but at least we save parsing the json from java another time
  • Loading branch information
dwt committed Aug 22, 2012
1 parent ef18a7d commit 7cf9280
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 13 deletions.
@@ -1,8 +1,10 @@
package com.couchbase.touchdb.javascript; package com.couchbase.touchdb.javascript;


import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;


import org.codehaus.jackson.map.ObjectMapper;
import org.elasticsearch.script.javascript.support.NativeList; import org.elasticsearch.script.javascript.support.NativeList;
import org.elasticsearch.script.javascript.support.NativeMap; import org.elasticsearch.script.javascript.support.NativeMap;
import org.mozilla.javascript.Context; import org.mozilla.javascript.Context;
Expand Down Expand Up @@ -61,6 +63,7 @@ else if(javaObject instanceof List) {
} }
} }


// REFACT: Extract superview for both the map and reduce blocks as they do pretty much the same thing
class TDViewMapBlockRhino implements TDViewMapBlock { class TDViewMapBlockRhino implements TDViewMapBlock {


private static WrapFactory wrapFactory = new CustomWrapFactory(); private static WrapFactory wrapFactory = new CustomWrapFactory();
Expand Down Expand Up @@ -102,21 +105,36 @@ public void map(Map<String, Object> document, TDViewMapEmitBlock emitter) {
// Error in the JavaScript view - CouchDB swallows the error and tries the next document // Error in the JavaScript view - CouchDB swallows the error and tries the next document
// REFACT: would be nice to check this in the constructor so we don't have to reparse every time // REFACT: would be nice to check this in the constructor so we don't have to reparse every time
// should also be much faster if we can insert the map function into this objects globals // should also be much faster if we can insert the map function into this objects globals
Log.e(TDDatabase.TAG, "Javascript syntax error in view:\n" + src); Log.e(TDDatabase.TAG, "Javascript syntax error in view:\n" + src, e);
return; return;
} }


//find the map function and execute it // Need to stringify the json tree, as the ContextWrapper is unable
Function mapFun = (Function)globalScope.get("map", globalScope); // to correctly convert nested json to their js representation.
Object[] functionArgs = { document }; // More specifically, if a dictionary is included that contains an array as a value
// that array will not be wrapped correctly but you'll get the plain
// java.util.ArrayList instead - and then an error.
ObjectMapper mapper = new ObjectMapper();
String json = null;
try {
json = mapper.writeValueAsString(document);
} catch (IOException e) {
// Can thrown different subclasses of IOException- but we really do not care,
// as this document was unserialized from JSON, so Jackson should be able to serialize it.
Log.e(TDDatabase.TAG, "Error reserializing json from the db: " + document, e);
return;
}

String mapInvocation = "map(" + json + ");";
try { try {
mapFun.call(ctx, globalScope, globalScope, functionArgs); ctx.evaluateString(globalScope, mapInvocation, "map invocation", 1, null);
} catch (org.mozilla.javascript.RhinoException e) { }
catch (org.mozilla.javascript.RhinoException e) {
// Error in the JavaScript view - CouchDB swallows the error and tries the next document // Error in the JavaScript view - CouchDB swallows the error and tries the next document
Log.e(TDDatabase.TAG, "Error in javascript view:\n" + src + "\n with document:\n" + document); Log.e(TDDatabase.TAG, "Error in javascript view:\n" + src + "\n with document:\n" + document, e);
return; return;

} }

//now pull values out of the place holder and emit them //now pull values out of the place holder and emit them
NativeArray mapResults = (NativeArray)globalScope.get("map_results", globalScope); NativeArray mapResults = (NativeArray)globalScope.get("map_results", globalScope);
for(int i=0; i<mapResults.getLength(); i++) { for(int i=0; i<mapResults.getLength(); i++) {
Expand All @@ -130,9 +148,6 @@ public void map(Map<String, Object> document, TDViewMapEmitBlock emitter) {
} }


} }



} finally { } finally {
Context.exit(); Context.exit();
} }
Expand Down
Expand Up @@ -38,6 +38,7 @@ Object ddocWithMap(String viewName, String mapFunction) throws Exception {
// REFACT: consider pulling up into TouchDBTestCase // REFACT: consider pulling up into TouchDBTestCase
List<Object> getView(String fullViewPath) throws Exception { List<Object> getView(String fullViewPath) throws Exception {
Map<String, Object> result = (Map<String, Object>) send(server, "GET", fullViewPath, TDStatus.OK, null); Map<String, Object> result = (Map<String, Object>) send(server, "GET", fullViewPath, TDStatus.OK, null);
assertEquals(0, result.get("offset"));
return (List<Object>) result.get("rows"); return (List<Object>) result.get("rows");
} }


Expand Down Expand Up @@ -197,6 +198,26 @@ public void testRealJavaScriptDesignDocument() {
// Query the view and check the result: // Query the view and check the result:
Object res = send(server, "GET", "/rhinodb/_design/doc/_view/test", TDStatus.OK, expectedResult); Object res = send(server, "GET", "/rhinodb/_design/doc/_view/test", TDStatus.OK, expectedResult);
} }

public void testJavaScriptDesignDocumentThatDealsWithArrays() throws Exception {
Object json = json(
"{" +
"\"producers\": [ \"\" ]," +
"\"collection\": \"product\"" +
"}");
sendBody(server, "PUT", "/rhinodb/doc1", json, TDStatus.CREATED, null);

Object ddoc = ddocWithMap("test", "function(doc) { if ('product' === doc.collection && doc.producers) { doc.producers.forEach(function(each) { emit(each, doc); }); } }");
sendBody(server, "PUT", "/rhinodb/_design/doc", ddoc, TDStatus.CREATED, null);

List<Object> rows = getView("/rhinodb/_design/doc/_view/test", 1);
Map<String,Object> resultRow = (Map) rows.get(0);
assertEquals("doc1", resultRow.get("id"));
assertEquals("", resultRow.get("key"));
Map<String,Object> value = (Map) resultRow.get("value");
assertEquals("doc1", value.get("_id"));
}

public void testShouldLeaveOutDocumentsWhenMapBlockThrowsAnException() throws Exception { public void testShouldLeaveOutDocumentsWhenMapBlockThrowsAnException() throws Exception {
sendBody(server, "PUT", "/rhinodb/good", json("{}"), TDStatus.CREATED, null); sendBody(server, "PUT", "/rhinodb/good", json("{}"), TDStatus.CREATED, null);
sendBody(server, "PUT", "/rhinodb/bad", json("{}"), TDStatus.CREATED, null); sendBody(server, "PUT", "/rhinodb/bad", json("{}"), TDStatus.CREATED, null);
Expand Down Expand Up @@ -230,5 +251,4 @@ public void testShouldDiscardDocumentsIfViewThrowsEcmaError() throws Exception {
assertEquals("good", resultRow.get("id")); assertEquals("good", resultRow.get("id"));
} }



} }

0 comments on commit 7cf9280

Please sign in to comment.