diff options
-rw-r--r-- | share/www/script/test/changes.js | 20 | ||||
-rw-r--r-- | src/couchdb/couch_changes.erl | 85 | ||||
-rw-r--r-- | src/couchdb/couch_httpd_db.erl | 28 |
3 files changed, 84 insertions, 49 deletions
diff --git a/share/www/script/test/changes.js b/share/www/script/test/changes.js index 877d1fcc..7ce3baaa 100644 --- a/share/www/script/test/changes.js +++ b/share/www/script/test/changes.js @@ -407,28 +407,40 @@ couchTests.changes = function(debug) { body: JSON.stringify({"doc_ids": ["something", "anotherthing", "andmore"]}) }; - var req = CouchDB.request("POST", "/test_suite_db/_changes", options); + var req = CouchDB.request("POST", "/test_suite_db/_changes?filter=_doc_ids", options); var resp = JSON.parse(req.responseText); T(resp.results.length === 0); T(db.save({"_id":"something", "bop" : "plankton"}).ok); - var req = CouchDB.request("POST", "/test_suite_db/_changes", options); + var req = CouchDB.request("POST", "/test_suite_db/_changes?filter=_doc_ids", options); var resp = JSON.parse(req.responseText); T(resp.results.length === 1); T(resp.results[0].id === "something"); T(db.save({"_id":"anotherthing", "bop" : "plankton"}).ok); - var req = CouchDB.request("POST", "/test_suite_db/_changes", options); + var req = CouchDB.request("POST", "/test_suite_db/_changes?filter=_doc_ids", options); var resp = JSON.parse(req.responseText); T(resp.results.length === 2); T(resp.results[0].id === "something"); T(resp.results[1].id === "anotherthing"); + + var docids = JSON.stringify(["something", "anotherthing", "andmore"]), + req = CouchDB.request("GET", "/test_suite_db/_changes?filter=_doc_ids&doc_ids="+docids, options); + var resp = JSON.parse(req.responseText); + T(resp.results.length === 2); + T(resp.results[0].id === "something"); + T(resp.results[1].id === "anotherthing"); + + var req = CouchDB.request("GET", "/test_suite_db/_changes?filter=_design"); + var resp = JSON.parse(req.responseText); + T(resp.results.length === 1); + T(resp.results[0].id === "_design/erlang"); if (!is_safari && xhr) { // filter docids with continuous xhr = CouchDB.newXhr(); - xhr.open("POST", "/test_suite_db/_changes?feed=continuous&timeout=500&since=7", true); + xhr.open("POST", "/test_suite_db/_changes?feed=continuous&timeout=500&since=7&filter=_doc_ids", true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(options.body); diff --git a/src/couchdb/couch_changes.erl b/src/couchdb/couch_changes.erl index 6fc4597a..22661331 100644 --- a/src/couchdb/couch_changes.erl +++ b/src/couchdb/couch_changes.erl @@ -78,33 +78,21 @@ get_callback_acc(Callback) when is_function(Callback, 2) -> {fun(Ev, Data, _) -> Callback(Ev, Data) end, ok}. %% @type Req -> #httpd{} | {json_req, JsonObj()} -make_filter_fun({docids, Docids}, Style, _Req, _Db) -> - fun(#doc_info{id=DocId, revs=[#rev_info{rev=Rev}|_]=Revs}) -> - case lists:member(DocId, Docids) of - true -> - case Style of - main_only -> - [{[{<<"rev">>, couch_doc:rev_to_str(Rev)}]}]; - all_docs -> - [{[{<<"rev">>, couch_doc:rev_to_str(R)}]} - || #rev_info{rev=R} <- Revs] - end; - _ -> [] - end - end; - make_filter_fun(FilterName, Style, Req, Db) -> + FilterName1 = list_to_binary(FilterName), + case FilterName1 of + (<<"_", _/binary>>) -> + builtin_filter_fun(FilterName1, Style, Req, Db); + (_OSFun) -> + os_filter_fun(FilterName, Style, Req, Db) + end. + +os_filter_fun(FilterName, Style, Req, Db) -> case [list_to_binary(couch_httpd:unquote(Part)) || Part <- string:tokens(FilterName, "/")] of [] -> - fun(#doc_info{revs=[#rev_info{rev=Rev}|_]=Revs}) -> - case Style of - main_only -> - [{[{<<"rev">>, couch_doc:rev_to_str(Rev)}]}]; - all_docs -> - [{[{<<"rev">>, couch_doc:rev_to_str(R)}]} - || #rev_info{rev=R} <- Revs] - end + fun(#doc_info{revs=Revs}) -> + builtin_results(Style, Revs) end; [DName, FName] -> DesignId = <<"_design/", DName/binary>>, @@ -135,6 +123,57 @@ make_filter_fun(FilterName, Style, Req, Db) -> "filter parameter must be of the form `designname/filtername`"}) end. +builtin_filter_fun(<<"_doc_ids",_/binary>>, Style, + #httpd{method='POST'}=Req, _Db) -> + {Props} = couch_httpd:json_body_obj(Req), + DocIds = couch_util:get_value(<<"doc_ids">>, Props, nil), + filter_docids(DocIds, Style); +builtin_filter_fun(<<"_doc_ids", _/binary>>, Style, + #httpd{method='GET'}=Req, _Db) -> + QS = couch_httpd:qs(Req), + DocIds = case couch_util:get_value("doc_ids", QS, nil) of + nil -> + throw({bad_request, "`doc_ids` parameter is not set"}); + DocIds1 -> + ?JSON_DECODE(DocIds1) + end, + filter_docids(DocIds, Style); +builtin_filter_fun(<<"_design", _/binary>>, Style, _Req, _Db) -> + filter_designdoc(Style); +builtin_filter_fun(_FilterName, _Style, _Req, _Db) -> + throw({bad_request, + "unkown builtin filter name"}). + +filter_docids(DocIds, Style) when is_list(DocIds)-> + fun(#doc_info{id=DocId, revs=Revs}) -> + case lists:member(DocId, DocIds) of + true -> + builtin_results(Style, Revs); + _ -> [] + end + end; +filter_docids(_, _) -> + throw({bad_request, "`doc_ids` member is undefined or not a + list."}). + +filter_designdoc(Style) -> + fun(#doc_info{id=DocId, revs=Revs}) -> + case DocId of + <<"_design", _/binary>> -> + builtin_results(Style, Revs); + _ -> [] + end + end. + +builtin_results(Style, [#rev_info{rev=Rev}|_]=Revs) -> + case Style of + main_only -> + [{[{<<"rev">>, couch_doc:rev_to_str(Rev)}]}]; + all_docs -> + [{[{<<"rev">>, couch_doc:rev_to_str(R)}]} + || #rev_info{rev=R} <- Revs] + end. + get_changes_timeout(Args, Callback) -> #changes_args{ heartbeat = Heartbeat, diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl index 7362c979..c11d2aef 100644 --- a/src/couchdb/couch_httpd_db.erl +++ b/src/couchdb/couch_httpd_db.erl @@ -55,26 +55,15 @@ handle_request(#httpd{path_parts=[DbName|RestParts],method=Method, do_db_req(Req, Handler) end. -handle_changes_req(#httpd{method='GET'}=Req, Db) -> - handle_changes_req1(Req, Db, nil); - handle_changes_req(#httpd{method='POST'}=Req, Db) -> couch_httpd:validate_ctype(Req, "application/json"), - {Props} = couch_httpd:json_body_obj(Req), - case couch_util:get_value(<<"doc_ids">>, Props, nil) of - nil -> - handle_changes_req1(Req, Db, nil); - Docids when is_list(Docids) -> - handle_changes_req1(Req, Db, Docids); - _ -> - throw({bad_request, "`docids` member must be a array."}) - end; - + handle_changes_req1(Req, Db); +handle_changes_req(#httpd{method='GET'}=Req, Db) -> + handle_changes_req1(Req, Db); handle_changes_req(#httpd{path_parts=[_,<<"_changes">>]}=Req, _Db) -> send_method_not_allowed(Req, "GET,HEAD,POST"). - -handle_changes_req1(Req, Db, Docids) -> +handle_changes_req1(Req, Db) -> MakeCallback = fun(Resp) -> fun({change, Change, _}, "continuous") -> send_chunk(Resp, [?JSON_ENCODE(Change) | "\n"]); @@ -101,13 +90,8 @@ handle_changes_req1(Req, Db, Docids) -> end end, ChangesArgs = parse_changes_query(Req), - ChangesArgs1 = case Docids of - nil -> ChangesArgs; - _ -> ChangesArgs#changes_args{filter={docids, Docids}} - end, - - ChangesFun = couch_changes:handle_changes(ChangesArgs1, Req, Db), - WrapperFun = case ChangesArgs1#changes_args.feed of + ChangesFun = couch_changes:handle_changes(ChangesArgs, Req, Db), + WrapperFun = case ChangesArgs#changes_args.feed of "normal" -> {ok, Info} = couch_db:get_db_info(Db), CurrentEtag = couch_httpd:make_etag(Info), |