You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by be...@apache.org on 2010/12/27 19:40:34 UTC

svn commit: r1053132 - in /couchdb/trunk: share/server/filter.js share/server/loop.js share/www/script/test/changes.js src/couchdb/couch_changes.erl src/couchdb/couch_query_servers.erl

Author: benoitc
Date: Mon Dec 27 18:40:34 2010
New Revision: 1053132

URL: http://svn.apache.org/viewvc?rev=1053132&view=rev
Log:
Add the ability to use map view function to filter changes instead of
duplicating the 
effort in writing a filter function that does the same, which is 
apparently done a lot. 

Each time a value could be emitted, a change is returned. The url is : 

http://127.0.0.1:5984/testdb/_changes?filter=_view&view=dname/viewname 


Modified:
    couchdb/trunk/share/server/filter.js
    couchdb/trunk/share/server/loop.js
    couchdb/trunk/share/www/script/test/changes.js
    couchdb/trunk/src/couchdb/couch_changes.erl
    couchdb/trunk/src/couchdb/couch_query_servers.erl

Modified: couchdb/trunk/share/server/filter.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/server/filter.js?rev=1053132&r1=1053131&r2=1053132&view=diff
==============================================================================
--- couchdb/trunk/share/server/filter.js (original)
+++ couchdb/trunk/share/server/filter.js Mon Dec 27 18:40:34 2010
@@ -10,14 +10,36 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-var Filter = {
-  filter : function(fun, ddoc, args) {    
-    var results = [];
-    var docs = args[0];
-    var req = args[1];
-    for (var i=0; i < docs.length; i++) {
-      results.push((fun.apply(ddoc, [docs[i], req]) && true) || false);
-    };
-    respond([true, results]);
-  }
-};
+var Filter = (function() {
+
+  var view_emit = false;
+
+  return {
+      emit : function(key, value) {
+        view_emit = true;
+      },
+      filter : function(fun, ddoc, args) {
+        var results = [];
+        var docs = args[0];
+        var req = args[1];
+        for (var i=0; i < docs.length; i++) {
+          results.push((fun.apply(ddoc, [docs[i], req]) && true) || false);
+        };
+        respond([true, results]);
+      },
+      filter_view : function(fun, ddoc, args) {
+        // recompile
+        var source = fun.toSource();
+        fun = evalcx(source, filter_sandbox);
+
+        var results = [];
+        var docs = args[0];
+        for (var i=0; i < docs.length; i++) {
+          view_emit = false;
+          fun(docs[i]);
+          results.push((view_emit && true) || false);
+        };
+        respond([true, results]);
+      }
+    }
+})();

Modified: couchdb/trunk/share/server/loop.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/server/loop.js?rev=1053132&r1=1053131&r2=1053132&view=diff
==============================================================================
--- couchdb/trunk/share/server/loop.js (original)
+++ couchdb/trunk/share/server/loop.js Mon Dec 27 18:40:34 2010
@@ -11,6 +11,7 @@
 // the License.
 
 var sandbox = null;
+var filter_sandbox = null;
 
 function init_sandbox() {
   try {
@@ -33,6 +34,22 @@ function init_sandbox() {
 };
 init_sandbox();
 
+function init_filter_sandbox() {
+  try {
+    filter_sandbox = evalcx('');
+    for (var p in sandbox) {
+      if (sandbox.hasOwnProperty(p)) {
+        filter_sandbox[p] = sandbox[p];
+      }
+    }
+    filter_sandbox.emit = Filter.emit;
+  } catch(e) {
+    log(e.toSource());
+  }
+};
+
+init_filter_sandbox();
+
 // Commands are in the form of json arrays:
 // ["commandname",..optional args...]\n
 //
@@ -43,6 +60,7 @@ var DDoc = (function() {
     "lists"     : Render.list,
     "shows"    : Render.show,
     "filters"   : Filter.filter,
+    "views"     : Filter.filter_view, 
     "updates"  : Render.update,
     "validate_doc_update" : Validate.validate
   };

Modified: couchdb/trunk/share/www/script/test/changes.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/changes.js?rev=1053132&r1=1053131&r2=1053132&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/changes.js (original)
+++ couchdb/trunk/share/www/script/test/changes.js Mon Dec 27 18:40:34 2010
@@ -190,7 +190,7 @@ couchTests.changes = function(debug) {
     _id : "_design/changes_filter",
     "filters" : {
       "bop" : "function(doc, req) { return (doc.bop);}",
-      "dynamic" : stringFun(function(doc, req) { 
+      "dynamic" : stringFun(function(doc, req) {
         var field = req.query.field;
         return doc[field];
       }),
@@ -205,6 +205,13 @@ couchTests.changes = function(debug) {
     views : {
       local_seq : {
         map : "function(doc) {emit(doc._local_seq, null)}"
+      },
+      blah: {
+        map : 'function(doc) {' +
+              '  if (doc._id == "blah") {' +
+              '    emit(doc._id, null);' +
+              '  }' +
+              '}'
       }
     }
   };
@@ -213,7 +220,7 @@ couchTests.changes = function(debug) {
 
   var req = CouchDB.request("GET", "/test_suite_db/_changes?filter=changes_filter/bop");
   var resp = JSON.parse(req.responseText);
-  T(resp.results.length == 0); 
+  T(resp.results.length == 0);
 
   db.save({"bop" : "foom"});
   db.save({"bop" : false});
@@ -319,7 +326,16 @@ couchTests.changes = function(debug) {
   var resp = JSON.parse(req.responseText);
   var expect = (!is_safari && xhr) ? 3: 1;
   TEquals(expect, resp.results.length, "should return matching rows");
-  
+ 
+  // test filter on view function (map)
+  //
+  T(db.save({"_id":"blah", "bop" : "plankton"}).ok);
+  var req = CouchDB.request("GET", "/test_suite_db/_changes?filter=_view&view=changes_filter/blah");
+  var resp = JSON.parse(req.responseText);
+  T(resp.results.length === 1);
+  T(resp.results[0].id === "blah");
+
+
   // test for userCtx
   run_on_modified_server(
     [{section: "httpd",
@@ -407,7 +423,7 @@ couchTests.changes = function(debug) {
 
     var options = {
         headers: {"Content-Type": "application/json"},
-        body: JSON.stringify({"doc_ids": ["something", "anotherthing", "andmore"]})         
+        body: JSON.stringify({"doc_ids": ["something", "anotherthing", "andmore"]})
     };
 
     var req = CouchDB.request("POST", "/test_suite_db/_changes?filter=_doc_ids", options);
@@ -450,7 +466,6 @@ couchTests.changes = function(debug) {
         
         T(db.save({"_id":"andmore", "bop" : "plankton"}).ok);
 
-        
         waitForSuccess(function() {
             if (xhr.readyState != 4) {
               throw("still waiting");
@@ -461,7 +476,6 @@ couchTests.changes = function(debug) {
         T(line.seq == 8);
         T(line.id == "andmore");
     }
-    
   });
 
 };

Modified: couchdb/trunk/src/couchdb/couch_changes.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_changes.erl?rev=1053132&r1=1053131&r2=1053132&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_changes.erl (original)
+++ couchdb/trunk/src/couchdb/couch_changes.erl Mon Dec 27 18:40:34 2010
@@ -130,6 +130,9 @@ builtin_filter_fun("_doc_ids", Style, #h
     filter_docids(DocIds, Style);
 builtin_filter_fun("_design", Style, _Req, _Db) ->
     filter_designdoc(Style);
+builtin_filter_fun("_view", Style, Req, Db) ->
+    ViewName = couch_httpd:qs_value(Req, "view", ""),
+    filter_view(ViewName, Style, Db);
 builtin_filter_fun(_FilterName, _Style, _Req, _Db) ->
     throw({bad_request, "unknown builtin filter name"}).
 
@@ -153,6 +156,40 @@ filter_designdoc(Style) ->
             end
     end.
 
+filter_view("", _Style, _Db) ->
+    throw({bad_request, "`view` filter parameter is not provided."});
+filter_view(ViewName, Style, Db) ->
+    case [list_to_binary(couch_httpd:unquote(Part))
+            || Part <- string:tokens(ViewName, "/")] of
+        [] ->
+            throw({bad_request, "Invalid `view` parameter."});
+        [DName, VName] ->
+            DesignId = <<"_design/", DName/binary>>,
+            DDoc = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
+            % validate that the ddoc has the filter fun
+            #doc{body={Props}} = DDoc,
+            couch_util:get_nested_json_value({Props}, [<<"views">>, VName]),
+            fun(DocInfo) ->
+                DocInfos =
+                case Style of
+                main_only ->
+                    [DocInfo];
+                all_docs ->
+                    [DocInfo#doc_info{revs=[Rev]}|| Rev <- DocInfo#doc_info.revs]
+                end,
+                Docs = [Doc || {ok, Doc} <- [
+                        couch_db:open_doc(Db, DocInfo2, [deleted, conflicts])
+                            || DocInfo2 <- DocInfos]],
+
+                {ok, Passes} = couch_query_servers:filter_view(
+                    DDoc, VName, Docs
+                ),
+                [{[{<<"rev">>, couch_doc:rev_to_str({RevPos,RevId})}]}
+                    || {Pass, #doc{revs={RevPos,[RevId|_]}}}
+                    <- lists:zip(Passes, Docs), Pass == true]
+            end
+        end.
+
 builtin_results(Style, [#rev_info{rev=Rev}|_]=Revs) ->
     case Style of
         main_only ->

Modified: couchdb/trunk/src/couchdb/couch_query_servers.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_query_servers.erl?rev=1053132&r1=1053131&r2=1053132&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_query_servers.erl (original)
+++ couchdb/trunk/src/couchdb/couch_query_servers.erl Mon Dec 27 18:40:34 2010
@@ -19,6 +19,7 @@
 -export([start_doc_map/3, map_docs/2, stop_doc_map/1]).
 -export([reduce/3, rereduce/3,validate_doc_update/5]).
 -export([filter_docs/5]).
+-export([filter_view/3]).
 
 -export([with_ddoc_proc/2, proc_prompt/2, ddoc_prompt/3, ddoc_proc_prompt/3, json_doc/1]).
 
@@ -229,6 +230,11 @@ json_doc(nil) -> null;
 json_doc(Doc) ->
     couch_doc:to_json_obj(Doc, [revs]).
 
+filter_view(DDoc, VName, Docs) ->
+    JsonDocs = [couch_doc:to_json_obj(Doc, [revs]) || Doc <- Docs],
+    [true, Passes] = ddoc_prompt(DDoc, [<<"views">>, VName, <<"map">>], [JsonDocs]),
+    {ok, Passes}.
+
 filter_docs(Req, Db, DDoc, FName, Docs) ->
     JsonReq = case Req of
     {json_req, JsonObj} ->
@@ -237,7 +243,8 @@ filter_docs(Req, Db, DDoc, FName, Docs) 
         couch_httpd_external:json_req_obj(HttpReq, Db)
     end,
     JsonDocs = [couch_doc:to_json_obj(Doc, [revs]) || Doc <- Docs],
-    [true, Passes] = ddoc_prompt(DDoc, [<<"filters">>, FName], [JsonDocs, JsonReq]),
+    [true, Passes] = ddoc_prompt(DDoc, [<<"filters">>, FName],
+        [JsonDocs, JsonReq]),
     {ok, Passes}.
 
 ddoc_proc_prompt({Proc, DDocId}, FunPath, Args) ->