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) ->