summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/www/script/test/changes.js53
-rw-r--r--src/couchdb/couch_changes.erl15
-rw-r--r--src/couchdb/couch_httpd_db.erl32
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),