diff options
-rw-r--r-- | share/www/script/test/changes.js | 53 | ||||
-rw-r--r-- | src/couchdb/couch_changes.erl | 15 | ||||
-rw-r--r-- | src/couchdb/couch_httpd_db.erl | 32 |
3 files changed, 95 insertions, 5 deletions
diff --git a/share/www/script/test/changes.js b/share/www/script/test/changes.js index 50649508..b1404ba0 100644 --- a/share/www/script/test/changes.js +++ b/share/www/script/test/changes.js @@ -181,6 +181,8 @@ couchTests.changes = function(debug) { T(change.id == "barz"); T(change.changes[0].rev == docBarz._rev); T(lines[3]=='"last_seq":4}'); + + } // test the filtered changes @@ -396,6 +398,57 @@ couchTests.changes = function(debug) { T(resp.results.length === 2); T(resp.results[0].id === "doc2"); T(resp.results[1].id === "doc4"); + + // test filtering on docids + // + + options = { + headers: {"Content-Type": "application/json"}, + body: JSON.stringify({"doc_ids": ["something", "anotherthing", "andmore"]}) + }; + + var req = CouchDB.request("POST", "/test_suite_db/_changes", 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 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 resp = JSON.parse(req.responseText); + T(resp.results.length === 2); + T(resp.results[0].id === "something"); + T(resp.results[1].id === "anotherthing"); + + + 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.setRequestHeader("Content-Type", "application/json"); + + xhr.send(options.body); + + T(db.save({"_id":"andmore", "bop" : "plankton"}).ok); + + + waitForSuccess(function() { + if (xhr.readyState != 4) { + throw("still waiting"); + } + console.log(xhr.readyState); + }, "andmore-only"); + + line = JSON.parse(xhr.responseText.split("\n")[0]); + console.log(line); + T(line.seq == 8); + T(line.id == "andmore"); + } + }); }; diff --git a/src/couchdb/couch_changes.erl b/src/couchdb/couch_changes.erl index dd884cad..6fc4597a 100644 --- a/src/couchdb/couch_changes.erl +++ b/src/couchdb/couch_changes.erl @@ -78,6 +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) -> case [list_to_binary(couch_httpd:unquote(Part)) || Part <- string:tokens(FilterName, "/")] of diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl index 51c9ae9a..412b6e99 100644 --- a/src/couchdb/couch_httpd_db.erl +++ b/src/couchdb/couch_httpd_db.erl @@ -56,6 +56,25 @@ handle_request(#httpd{path_parts=[DbName|RestParts],method=Method, 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_req(#httpd{path_parts=[_,<<"_changes">>]}=Req, _Db) -> + send_method_not_allowed(Req, "GET,HEAD,POST"). + + +handle_changes_req1(Req, Db, Docids) -> MakeCallback = fun(Resp) -> fun({change, Change, _}, "continuous") -> send_chunk(Resp, [?JSON_ENCODE(Change) | "\n"]); @@ -82,8 +101,13 @@ handle_changes_req(#httpd{method='GET'}=Req, Db) -> end end, ChangesArgs = parse_changes_query(Req), - ChangesFun = couch_changes:handle_changes(ChangesArgs, Req, Db), - WrapperFun = case ChangesArgs#changes_args.feed of + 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 "normal" -> {ok, Info} = couch_db:get_db_info(Db), CurrentEtag = couch_httpd:make_etag(Info), @@ -109,10 +133,8 @@ handle_changes_req(#httpd{method='GET'}=Req, Db) -> couch_stats_collector:track_process_count( {httpd, clients_requesting_changes} ), - WrapperFun(ChangesFun); + WrapperFun(ChangesFun). -handle_changes_req(#httpd{path_parts=[_,<<"_changes">>]}=Req, _Db) -> - send_method_not_allowed(Req, "GET,HEAD"). handle_compact_req(#httpd{method='POST',path_parts=[DbName,_,Id|_]}=Req, Db) -> ok = couch_db:check_is_admin(Db), |