diff options
-rw-r--r-- | THANKS | 1 | ||||
-rw-r--r-- | share/www/script/couch.js | 2 | ||||
-rw-r--r-- | share/www/script/test/list_views.js | 10 | ||||
-rw-r--r-- | share/www/script/test/view_multi_key_all_docs.js | 37 | ||||
-rw-r--r-- | share/www/script/test/view_multi_key_design.js | 78 | ||||
-rw-r--r-- | src/couchdb/couch_httpd.erl | 11 | ||||
-rw-r--r-- | src/couchdb/couch_httpd_db.erl | 3 | ||||
-rw-r--r-- | src/couchdb/couch_httpd_show.erl | 6 | ||||
-rw-r--r-- | src/couchdb/couch_httpd_view.erl | 3 |
9 files changed, 145 insertions, 6 deletions
@@ -71,5 +71,6 @@ suggesting improvements or submitting changes. Some of these people are: * Klaus Trainer <klaus.trainer@web.de> * Dale Harvey <dale@arandomurl.com> * Juuso Väänänen <juuso@vaananen.org> + * Jeff Zellner <jeff.zellner@gmail.com> For a list of authors see the `AUTHORS` file. diff --git a/share/www/script/couch.js b/share/www/script/couch.js index 913f58fb..bcc19652 100644 --- a/share/www/script/couch.js +++ b/share/www/script/couch.js @@ -272,7 +272,7 @@ function CouchDB(name, httpHeaders) { for (var name in options) { if (!options.hasOwnProperty(name)) { continue; }; var value = options[name]; - if (name == "key" || name == "startkey" || name == "endkey") { + if (name == "key" || name == "keys" || name == "startkey" || name == "endkey") { value = toJSON(value); } buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value)); diff --git a/share/www/script/test/list_views.js b/share/www/script/test/list_views.js index 8578622a..e98a212e 100644 --- a/share/www/script/test/list_views.js +++ b/share/www/script/test/list_views.js @@ -327,6 +327,16 @@ couchTests.list_views = function(debug) { T(/FirstKey: 2/.test(xhr.responseText)); T(/LastKey: 7/.test(xhr.responseText)); + // multi-key fetch with GET + var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView" + + "?keys=[2,4,5,7]"); + + T(xhr.status == 200, "multi key"); + T(!(/Key: 1 /.test(xhr.responseText))); + T(/Key: 2/.test(xhr.responseText)); + T(/FirstKey: 2/.test(xhr.responseText)); + T(/LastKey: 7/.test(xhr.responseText)); + // no multi-key fetch allowed when group=false xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=false", { body: '{"keys":[2,4,5,7]}' diff --git a/share/www/script/test/view_multi_key_all_docs.js b/share/www/script/test/view_multi_key_all_docs.js index 62e49665..1113be4d 100644 --- a/share/www/script/test/view_multi_key_all_docs.js +++ b/share/www/script/test/view_multi_key_all_docs.js @@ -25,24 +25,52 @@ couchTests.view_multi_key_all_docs = function(debug) { for(var i=0; i<rows.length; i++) T(rows[i].id == keys[i]); + // keys in GET parameters + rows = db.allDocs({keys:keys}, null).rows; + T(rows.length == keys.length); + for(var i=0; i<rows.length; i++) + T(rows[i].id == keys[i]); + rows = db.allDocs({limit: 1}, keys).rows; T(rows.length == 1); T(rows[0].id == keys[0]); + // keys in GET parameters + rows = db.allDocs({limit: 1, keys: keys}, null).rows; + T(rows.length == 1); + T(rows[0].id == keys[0]); + rows = db.allDocs({skip: 2}, keys).rows; T(rows.length == 3); for(var i=0; i<rows.length; i++) T(rows[i].id == keys[i+2]); + // keys in GET parameters + rows = db.allDocs({skip: 2, keys: keys}, null).rows; + T(rows.length == 3); + for(var i=0; i<rows.length; i++) + T(rows[i].id == keys[i+2]); + rows = db.allDocs({descending: "true"}, keys).rows; T(rows.length == keys.length); for(var i=0; i<rows.length; i++) T(rows[i].id == keys[keys.length-i-1]); + // keys in GET parameters + rows = db.allDocs({descending: "true", keys: keys}, null).rows; + T(rows.length == keys.length); + for(var i=0; i<rows.length; i++) + T(rows[i].id == keys[keys.length-i-1]); + rows = db.allDocs({descending: "true", skip: 3, limit:1}, keys).rows; T(rows.length == 1); T(rows[0].id == keys[1]); + // keys in GET parameters + rows = db.allDocs({descending: "true", skip: 3, limit:1, keys: keys}, null).rows; + T(rows.length == 1); + T(rows[0].id == keys[1]); + // Check we get invalid rows when the key doesn't exist rows = db.allDocs({}, [1, "i_dont_exist", "0"]).rows; T(rows.length == 3); @@ -51,4 +79,13 @@ couchTests.view_multi_key_all_docs = function(debug) { T(rows[1].error == "not_found"); T(!rows[1].id); T(rows[2].id == rows[2].key && rows[2].key == "0"); + + // keys in GET parameters + rows = db.allDocs({keys: [1, "i_dont_exist", "0"]}, null).rows; + T(rows.length == 3); + T(rows[0].error == "not_found"); + T(!rows[0].id); + T(rows[1].error == "not_found"); + T(!rows[1].id); + T(rows[2].id == rows[2].key && rows[2].key == "0"); }; diff --git a/share/www/script/test/view_multi_key_design.js b/share/www/script/test/view_multi_key_design.js index 19d270e5..b3cec657 100644 --- a/share/www/script/test/view_multi_key_design.js +++ b/share/www/script/test/view_multi_key_design.js @@ -54,6 +54,13 @@ couchTests.view_multi_key_design = function(debug) { T(rows[i].key == rows[i].value); } + // with GET keys + rows = db.view("test/all_docs",{keys:keys},null).rows; + for(var i=0;i<rows.length; i++) { + T(keys.indexOf(rows[i].key) != -1); + T(rows[i].key == rows[i].value); + } + var reduce = db.view("test/summate",{group:true},keys).rows; T(reduce.length == keys.length); for(var i=0; i<reduce.length; i++) { @@ -61,8 +68,18 @@ couchTests.view_multi_key_design = function(debug) { T(reduce[i].key == reduce[i].value); } + // with GET keys + reduce = db.view("test/summate",{group:true,keys:keys},null).rows; + T(reduce.length == keys.length); + for(var i=0; i<reduce.length; i++) { + T(keys.indexOf(reduce[i].key) != -1); + T(reduce[i].key == reduce[i].value); + } + // Test that invalid parameter combinations get rejected var badargs = [{startkey:0}, {endkey:0}, {key: 0}, {group_level: 2}]; + var getbadargs = [{startkey:0, keys:keys}, {endkey:0, keys:keys}, + {key:0, keys:keys}, {group_level: 2, keys:keys}]; for(var i in badargs) { try { @@ -71,6 +88,13 @@ couchTests.view_multi_key_design = function(debug) { } catch (e) { T(e.error == "query_parse_error"); } + + try { + db.view("test/all_docs",getbadargs[i],null); + T(0==1); + } catch (e) { + T(e.error = "query_parse_error"); + } } try { @@ -80,10 +104,20 @@ couchTests.view_multi_key_design = function(debug) { T(e.error == "query_parse_error"); } + try { + db.view("test/summate",{keys:keys},null); + T(0==1); + } catch (e) { + T(e.error == "query_parse_error"); + } + // Test that a map & reduce containing func support keys when reduce=false resp = db.view("test/summate", {reduce: false}, keys); T(resp.rows.length == 5); + resp = db.view("test/summate", {reduce: false, keys: keys}, null); + T(resp.rows.length == 5); + // Check that limiting by startkey_docid and endkey_docid get applied // as expected. var curr = db.view("test/multi_emit", {startkey_docid: 21, endkey_docid: 23}, [0, 2]).rows; @@ -96,34 +130,66 @@ couchTests.view_multi_key_design = function(debug) { T(curr[i].value == exp_val[i]); } + curr = db.view("test/multi_emit", {startkey_docid: 21, endkey_docid: 23, keys: [0, 2]}, null).rows; + T(curr.length == 6); + for( var i = 0 ; i < 6 ; i++) + { + T(curr[i].key == exp_key[i]); + T(curr[i].value == exp_val[i]); + } + // Check limit works curr = db.view("test/all_docs", {limit: 1}, keys).rows; T(curr.length == 1); T(curr[0].key == 10); + curr = db.view("test/all_docs", {limit: 1, keys: keys}, null).rows; + T(curr.length == 1); + T(curr[0].key == 10); + // Check offset works curr = db.view("test/multi_emit", {skip: 1}, [0]).rows; T(curr.length == 99); T(curr[0].value == 1); + curr = db.view("test/multi_emit", {skip: 1, keys: [0]}, null).rows; + T(curr.length == 99); + T(curr[0].value == 1); + // Check that dir works curr = db.view("test/multi_emit", {descending: "true"}, [1]).rows; T(curr.length == 100); T(curr[0].value == 99); T(curr[99].value == 0); + curr = db.view("test/multi_emit", {descending: "true", keys: [1]}, null).rows; + T(curr.length == 100); + T(curr[0].value == 99); + T(curr[99].value == 0); + // Check a couple combinations curr = db.view("test/multi_emit", {descending: "true", skip: 3, limit: 2}, [2]).rows; T(curr.length, 2); T(curr[0].value == 96); T(curr[1].value == 95); + curr = db.view("test/multi_emit", {descending: "true", skip: 3, limit: 2, keys: [2]}, null).rows; + T(curr.length, 2); + T(curr[0].value == 96); + T(curr[1].value == 95); + curr = db.view("test/multi_emit", {skip: 2, limit: 3, startkey_docid: "13"}, [0]).rows; T(curr.length == 3); T(curr[0].value == 15); T(curr[1].value == 16); T(curr[2].value == 17); + curr = db.view("test/multi_emit", {skip: 2, limit: 3, startkey_docid: "13", keys: [0]}, null).rows; + T(curr.length == 3); + T(curr[0].value == 15); + T(curr[1].value == 16); + T(curr[2].value == 17); + curr = db.view("test/multi_emit", {skip: 1, limit: 5, startkey_docid: "25", endkey_docid: "27"}, [1]).rows; T(curr.length == 2); @@ -131,8 +197,20 @@ couchTests.view_multi_key_design = function(debug) { T(curr[1].value == 27); curr = db.view("test/multi_emit", + {skip: 1, limit: 5, startkey_docid: "25", endkey_docid: "27", keys: [1]}, null).rows; + T(curr.length == 2); + T(curr[0].value == 26); + T(curr[1].value == 27); + + curr = db.view("test/multi_emit", {skip: 1, limit: 5, startkey_docid: "28", endkey_docid: "26", descending: "true"}, [1]).rows; T(curr.length == 2); T(curr[0].value == 27); T(curr[1].value == 26); + + curr = db.view("test/multi_emit", + {skip: 1, limit: 5, startkey_docid: "28", endkey_docid: "26", descending: "true", keys: [1]}, null).rows; + T(curr.length == 2); + T(curr[0].value == 27); + T(curr[1].value == 26); }; diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl index e1f6fc28..dea41eeb 100644 --- a/src/couchdb/couch_httpd.erl +++ b/src/couchdb/couch_httpd.erl @@ -15,7 +15,8 @@ -export([start_link/0, start_link/1, stop/0, handle_request/5]). --export([header_value/2,header_value/3,qs_value/2,qs_value/3,qs/1,path/1,absolute_uri/2,body_length/1]). +-export([header_value/2,header_value/3,qs_value/2,qs_value/3,qs/1,qs_json_value/3]). +-export([path/1,absolute_uri/2,body_length/1]). -export([verify_is_server_admin/1,unquote/1,quote/1,recv/2,recv_chunked/4,error_info/1]). -export([make_fun_spec_strs/1]). -export([make_arity_1_fun/1, make_arity_2_fun/1, make_arity_3_fun/1]). @@ -384,6 +385,14 @@ qs_value(Req, Key) -> qs_value(Req, Key, Default) -> couch_util:get_value(Key, qs(Req), Default). +qs_json_value(Req, Key, Default) -> + case qs_value(Req, Key, Default) of + Default -> + Default; + Result -> + ?JSON_DECODE(Result) + end. + qs(#httpd{mochi_req=MochiReq}) -> MochiReq:parse_qs(). diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl index 77b891fe..2ef2fe4f 100644 --- a/src/couchdb/couch_httpd_db.erl +++ b/src/couchdb/couch_httpd_db.erl @@ -375,7 +375,8 @@ db_req(#httpd{path_parts=[_,<<"_purge">>]}=Req, _Db) -> send_method_not_allowed(Req, "POST"); db_req(#httpd{method='GET',path_parts=[_,<<"_all_docs">>]}=Req, Db) -> - all_docs_view(Req, Db, nil); + Keys = couch_httpd:qs_json_value(Req, "keys", nil), + all_docs_view(Req, Db, Keys); db_req(#httpd{method='POST',path_parts=[_,<<"_all_docs">>]}=Req, Db) -> couch_httpd:validate_ctype(Req, "application/json"), diff --git a/src/couchdb/couch_httpd_show.erl b/src/couchdb/couch_httpd_show.erl index fa8cd972..70dd82e1 100644 --- a/src/couchdb/couch_httpd_show.erl +++ b/src/couchdb/couch_httpd_show.erl @@ -153,12 +153,14 @@ send_doc_update_response(Req, Db, DDoc, UpdateName, Doc, DocId) -> % view-list request with view and list from same design doc. handle_view_list_req(#httpd{method='GET', path_parts=[_, _, DesignName, _, ListName, ViewName]}=Req, Db, DDoc) -> - handle_view_list(Req, Db, DDoc, ListName, {DesignName, ViewName}, nil); + Keys = couch_httpd:qs_json_value(Req, "keys", nil), + handle_view_list(Req, Db, DDoc, ListName, {DesignName, ViewName}, Keys); % view-list request with view and list from different design docs. handle_view_list_req(#httpd{method='GET', path_parts=[_, _, _, _, ListName, ViewDesignName, ViewName]}=Req, Db, DDoc) -> - handle_view_list(Req, Db, DDoc, ListName, {ViewDesignName, ViewName}, nil); + Keys = couch_httpd:qs_json_value(Req, "keys", nil), + handle_view_list(Req, Db, DDoc, ListName, {ViewDesignName, ViewName}, Keys); handle_view_list_req(#httpd{method='GET'}=Req, _Db, _DDoc) -> send_error(Req, 404, <<"list_error">>, <<"Invalid path.">>); diff --git a/src/couchdb/couch_httpd_view.erl b/src/couchdb/couch_httpd_view.erl index 8829de02..63e737e8 100644 --- a/src/couchdb/couch_httpd_view.erl +++ b/src/couchdb/couch_httpd_view.erl @@ -57,7 +57,8 @@ design_doc_view(Req, Db, DName, ViewName, Keys) -> handle_view_req(#httpd{method='GET', path_parts=[_, _, DName, _, ViewName]}=Req, Db, _DDoc) -> - design_doc_view(Req, Db, DName, ViewName, nil); + Keys = couch_httpd:qs_json_value(Req, "keys", nil), + design_doc_view(Req, Db, DName, ViewName, Keys); handle_view_req(#httpd{method='POST', path_parts=[_, _, DName, _, ViewName]}=Req, Db, _DDoc) -> |