couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j..@apache.org
Subject svn commit: r803663 - in /couchdb/trunk: share/www/script/couch.js share/www/script/couch_tests.js share/www/script/test/view_builtin.js src/couchdb/couch_db.hrl src/couchdb/couch_httpd_db.erl src/couchdb/couch_httpd_view.erl
Date Wed, 12 Aug 2009 18:48:25 GMT
Author: jan
Date: Wed Aug 12 18:48:25 2009
New Revision: 803663

URL: http://svn.apache.org/viewvc?rev=803663&view=rev
Log:
add native /db/_conflicts view, patch by Adam Kocolosk, closes COUCHDB-462

Added:
    couchdb/trunk/share/www/script/test/view_builtin.js
Modified:
    couchdb/trunk/share/www/script/couch.js
    couchdb/trunk/share/www/script/couch_tests.js
    couchdb/trunk/src/couchdb/couch_db.hrl
    couchdb/trunk/src/couchdb/couch_httpd_db.erl
    couchdb/trunk/src/couchdb/couch_httpd_view.erl

Modified: couchdb/trunk/share/www/script/couch.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/couch.js?rev=803663&r1=803662&r2=803663&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/couch.js [utf-8] (original)
+++ couchdb/trunk/share/www/script/couch.js [utf-8] Wed Aug 12 18:48:25 2009
@@ -191,10 +191,18 @@
   }
 
   this.allDocs = function(options,keys) {
+    return this.builtinView("_all_docs", options, keys)
+  }
+
+  this.conflicts = function(options,keys) {
+    return this.builtinView("_conflicts", options, keys)
+  }
+
+  this.builtinView = function(name, options, keys) {
     if(!keys) {
-      this.last_req = this.request("GET", this.uri + "_all_docs" + encodeOptions(options));
+      this.last_req = this.request("GET", this.uri + name + encodeOptions(options));
     } else {
-      this.last_req = this.request("POST", this.uri + "_all_docs" + encodeOptions(options),
{
+      this.last_req = this.request("POST", this.uri + name + encodeOptions(options), {
         headers: {"Content-Type": "application/json"},
         body: JSON.stringify({keys:keys})
       });

Modified: couchdb/trunk/share/www/script/couch_tests.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/couch_tests.js?rev=803663&r1=803662&r2=803663&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/couch_tests.js [utf-8] (original)
+++ couchdb/trunk/share/www/script/couch_tests.js [utf-8] Wed Aug 12 18:48:25 2009
@@ -51,6 +51,7 @@
 loadTest("content_negotiation.js");
 loadTest("design_docs.js");
 loadTest("invalid_docids.js");
+loadTest("view_builtin.js");
 loadTest("view_collation.js");
 loadTest("view_conflicts.js");
 loadTest("view_errors.js");

Added: couchdb/trunk/share/www/script/test/view_builtin.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_builtin.js?rev=803663&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/view_builtin.js (added)
+++ couchdb/trunk/share/www/script/test/view_builtin.js Wed Aug 12 18:48:25 2009
@@ -0,0 +1,74 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.view_builtin = function(debug) {
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+
+  // test _conflicts view
+
+  // no rows
+    var result = db.conflicts();
+    TEquals(0, result.rows.length, "should return 0 conflicts");
+
+  // one doc with a conflict
+    var doc_a = db.save({_id:"a", a:1});
+
+    // create conflict
+    var bulk_result = db.bulkSave([{_id:"a",a:2}], {all_or_nothing:true});
+    var result = db.conflicts();
+    TEquals(1, result.rows.length, "should return 1 conflicts");
+    TEquals("a", result.rows[0].id, "should return row id 'a'");
+    TEquals("a", result.rows[0].key, "should return row key 'a'");
+    TEquals(bulk_result[0].rev, result.rows[0].rev, 
+      "should return row key 'a'");
+
+  // multiple docs with conflicts
+    var doc_b = db.save({_id:"b", a:3});
+    var bulk_result = db.bulkSave([{_id:"b",a:4}], {all_or_nothing:true});
+    var result = db.conflicts();
+    TEquals(2, result.rows.length, "should return 2 conflicts");
+
+  // key, startkey, endkey, skip & count
+    var result = db.conflicts({key:"b"});
+    TEquals(1, result.rows.length, "should return 1 conflicts");
+
+    var result = db.conflicts({startkey:"b"});
+    TEquals(1, result.rows.length, "should return 1 conflicts");
+
+    var result = db.conflicts({startkey:"a", endkey:"b"});
+    TEquals(2, result.rows.length, "should return 2 conflicts");
+
+    var result = db.conflicts({startkey:"c"});
+    TEquals(0, result.rows.length, "should return 0 conflicts");
+
+    var result = db.conflicts({limit:1});
+    TEquals(1, result.rows.length, "should return 1 conflicts");
+
+    var result = db.conflicts({skip:1});
+    TEquals(1, result.rows.length, "should return 1 conflicts");
+    TEquals("b", result.rows[0].key, "should return row key 'b'");
+
+  // POST is not allowed yet
+    try {
+      var result = db.conflicts({}, ["a"]);
+    } catch (e) {
+      TEquals("method_not_allowed", e.error, "should not allow POST requests");
+    }
+
+  // multi key get
+    // var result = db.conflicts({}, ["a"]);
+    //  TEquals(1, result.rows.length, "should return 1 conflicts");
+    //  TEquals("a", result.rows[0].key, "should return row key 'a'");
+};

Modified: couchdb/trunk/src/couchdb/couch_db.hrl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.hrl?rev=803663&r1=803662&r2=803663&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_db.hrl (original)
+++ couchdb/trunk/src/couchdb/couch_db.hrl Wed Aug 12 18:48:25 2009
@@ -175,7 +175,8 @@
     stale = false,
     multi_get = false,
     callback = nil,
-    list = nil
+    list = nil,
+    deleted = false
 }).
 
 -record(view_fold_helper_funs, {

Modified: couchdb/trunk/src/couchdb/couch_httpd_db.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=803663&r1=803662&r2=803663&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_db.erl Wed Aug 12 18:48:25 2009
@@ -383,76 +383,23 @@
     all_docs_view(Req, Db, nil);
 
 db_req(#httpd{method='POST',path_parts=[_,<<"_all_docs">>]}=Req, Db) ->
-    {Fields} = couch_httpd:json_body_obj(Req),
-    case proplists:get_value(<<"keys">>, Fields, nil) of
-    nil ->
-        ?LOG_DEBUG("POST to _all_docs with no keys member.", []),
-        all_docs_view(Req, Db, nil);
-    Keys when is_list(Keys) ->
-        all_docs_view(Req, Db, Keys);
-    _ ->
-        throw({bad_request, "`keys` member must be a array."})
-    end;
+    post_keys_to_view(Req, Db, fun all_docs_view/3);
 
 db_req(#httpd{path_parts=[_,<<"_all_docs">>]}=Req, _Db) ->
     send_method_not_allowed(Req, "GET,HEAD,POST");
 
 db_req(#httpd{method='GET',path_parts=[_,<<"_all_docs_by_seq">>]}=Req, Db) ->
-    #view_query_args{
-        start_key = StartKey,
-        limit = Limit,
-        skip = SkipCount,
-        direction = Dir
-    } = QueryArgs = couch_httpd_view:parse_view_params(Req, nil, map),
-
-    {ok, Info} = couch_db:get_db_info(Db),
-    CurrentEtag = couch_httpd:make_etag(Info),
-    couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
-        TotalRowCount = proplists:get_value(doc_count, Info),
-        FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db,
-            TotalRowCount, #view_fold_helper_funs{
-                reduce_count = fun couch_db:enum_docs_since_reduce_to_count/1
-            }),
-        StartKey2 = case StartKey of
-            nil -> 0;
-            <<>> -> 100000000000;
-            {} -> 100000000000;
-            StartKey when is_integer(StartKey) -> StartKey
-        end,
-        {ok, FoldResult} = couch_db:enum_docs_since(Db, StartKey2, Dir,
-            fun(DocInfo, Offset, Acc) ->
-                #doc_info{
-                    id=Id,
-                    high_seq=Seq,
-                    revs=[#rev_info{rev=Rev,deleted=Deleted} | RestInfo]
-                } = DocInfo,
-                ConflictRevs = couch_doc:rev_to_strs(
-                    [Rev1 || #rev_info{deleted=false, rev=Rev1} <- RestInfo]),
-                DelConflictRevs = couch_doc:rev_to_strs(
-                    [Rev1 || #rev_info{deleted=true, rev=Rev1} <- RestInfo]),
-                Json = {
-                    [{<<"rev">>, couch_doc:rev_to_str(Rev)}] ++
-                    case ConflictRevs of
-                    []  -> [];
-                    _   -> [{<<"conflicts">>, ConflictRevs}]
-                    end ++
-                    case DelConflictRevs of
-                    []  ->  [];
-                    _   ->  [{<<"deleted_conflicts">>, DelConflictRevs}]
-                    end ++
-                    case Deleted of
-                    true -> [{<<"deleted">>, true}];
-                    false -> []
-                    end
-                },
-                FoldlFun({{Seq, Id}, Json}, Offset, Acc)
-            end, {Limit, SkipCount, undefined, [], nil}),
-        couch_httpd_view:finish_view_fold(Req, TotalRowCount, {ok, FoldResult})
-    end);
+    all_docs_by_seq_view(Req, Db);
 
 db_req(#httpd{path_parts=[_,<<"_all_docs_by_seq">>]}=Req, _Db) ->
     send_method_not_allowed(Req, "GET,HEAD");
 
+db_req(#httpd{method='GET',path_parts=[_,<<"_conflicts">>]}=Req, Db) ->
+    conflicts_view(Req, Db, nil);
+
+db_req(#httpd{path_parts=[_,<<"_conflicts">>]}=Req, _Db) ->
+    send_method_not_allowed(Req, "GET,HEAD");
+
 db_req(#httpd{method='POST',path_parts=[_,<<"_missing_revs">>]}=Req, Db) ->
     {JsonDocIdRevs} = couch_httpd:json_body_obj(Req),
     JsonDocIdRevs2 = [{Id, [couch_doc:parse_rev(RevStr) || RevStr <- RevStrs]} || {Id,
RevStrs} <- JsonDocIdRevs],
@@ -511,6 +458,18 @@
 db_req(#httpd{path_parts=[_, DocId | FileNameParts]}=Req, Db) ->
     db_attachment_req(Req, Db, DocId, FileNameParts).
 
+post_keys_to_view(Req, Db, ViewFun) ->
+    {Fields} = couch_httpd:json_body_obj(Req),
+    case proplists:get_value(<<"keys">>, Fields, nil) of
+    nil ->
+        ?LOG_DEBUG("POST to view with no keys member.", []),
+        ViewFun(Req, Db, nil);
+    Keys when is_list(Keys) ->
+        ViewFun(Req, Db, Keys);
+    _ ->
+        throw({bad_request, "`keys` member must be a array."})
+    end.
+
 all_docs_view(Req, Db, Keys) ->
     #view_query_args{
         start_key = StartKey,
@@ -597,6 +556,136 @@
         end
     end).
 
+all_docs_by_seq_view(Req, Db) ->
+    #view_query_args{
+        start_key = StartKey,
+        limit = Limit,
+        skip = SkipCount,
+        direction = Dir
+    } = QueryArgs = couch_httpd_view:parse_view_params(Req, nil, map),
+
+    {ok, Info} = couch_db:get_db_info(Db),
+    CurrentEtag = couch_httpd:make_etag(Info),
+    couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
+        TotalRowCount = proplists:get_value(doc_count, Info),
+        FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db,
+            TotalRowCount, #view_fold_helper_funs{
+                reduce_count = fun couch_db:enum_docs_since_reduce_to_count/1
+            }),
+        StartKey2 = case StartKey of
+            nil -> 0;
+            <<>> -> 100000000000;
+            {} -> 100000000000;
+            StartKey when is_integer(StartKey) -> StartKey
+        end,
+        {ok, FoldResult} = couch_db:enum_docs_since(Db, StartKey2, Dir,
+            fun(DocInfo, Offset, Acc) ->
+                #doc_info{
+                    id=Id,
+                    high_seq=Seq,
+                    revs=[#rev_info{rev=Rev,deleted=Deleted} | RestInfo]
+                } = DocInfo,
+                ConflictRevs = couch_doc:rev_to_strs(
+                    [Rev1 || #rev_info{deleted=false, rev=Rev1} <- RestInfo]),
+                DelConflictRevs = couch_doc:rev_to_strs(
+                    [Rev1 || #rev_info{deleted=true, rev=Rev1} <- RestInfo]),
+                Json = {
+                    [{<<"rev">>, couch_doc:rev_to_str(Rev)}] ++
+                    case ConflictRevs of
+                    []  -> [];
+                    _   -> [{<<"conflicts">>, ConflictRevs}]
+                    end ++
+                    case DelConflictRevs of
+                    []  ->  [];
+                    _   ->  [{<<"deleted_conflicts">>, DelConflictRevs}]
+                    end ++
+                    case Deleted of
+                    true -> [{<<"deleted">>, true}];
+                    false -> []
+                    end
+                },
+                FoldlFun({{Seq, Id}, Json}, Offset, Acc)
+            end, {Limit, SkipCount, undefined, [], nil}),
+        couch_httpd_view:finish_view_fold(Req, TotalRowCount, {ok, FoldResult})
+    end).
+
+conflicts_view(Req, Db, nil) ->
+    #view_query_args{
+        start_key = StartKey,
+        limit = Limit,
+        skip = SkipCount,
+        direction = Dir,
+        deleted = ShowDeletedConflicts
+    } = QueryArgs = couch_httpd_view:parse_view_params(Req, nil, map),
+
+    StartResp = fun(Req2, Etag, _TotalViewCount, _Offset, _Acc) ->
+        {ok, Resp} = couch_httpd:start_json_response(Req2, 200, [{"Etag",Etag}]),
+        {ok, Resp, "{\"rows\":[\r\n"}
+    end,
+
+    SendRow = fun(Resp, _Db, {{Id,Rev}, Value}, _IncludeDocs, RowFront) ->
+        {IsDeleted, Conflicts, DelConflicts} = Value,
+        JsonProps = lists:flatten([{key, Id},{id, Id}, {rev, Rev},
+            case IsDeleted of true -> {deleted, true}; _ -> [] end,
+            case Conflicts of [] -> []; _ -> {conflicts, Conflicts} end,
+            case DelConflicts of
+                [] -> [];
+                _ -> {deleted_conflicts, DelConflicts}
+            end
+        ]),
+        send_chunk(Resp, RowFront ++  ?JSON_ENCODE({JsonProps})),
+        {ok, ",\r\n"}
+    end,
+
+    CurrentEtag = couch_httpd:make_etag(couch_db:get_update_seq(Db)),
+    couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
+        HelperFuns = #view_fold_helper_funs{
+            start_response = StartResp,
+            send_row = SendRow,
+            reduce_count = fun couch_db:enum_docs_reduce_to_count/1
+        },
+        FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs,
+            CurrentEtag, Db, 0, HelperFuns),
+
+        EnumFun = fun(FullDocInfo, Offset, Acc) ->
+            IsDeleted = FullDocInfo#full_doc_info.deleted,
+            #doc_info{
+                id = Id,
+                revs = [RevInfo | ConflictInfo]
+            } = couch_doc:to_doc_info(FullDocInfo),
+            RevStr = couch_doc:rev_to_str(RevInfo#rev_info.rev),
+            Conflicts = couch_doc:rev_to_strs(
+                [Rev || #rev_info{deleted=false, rev=Rev} <- ConflictInfo]),
+            DelConflicts = couch_doc:rev_to_strs(
+                [Rev || #rev_info{deleted=true, rev=Rev} <- ConflictInfo]),
+            case {ShowDeletedConflicts, Conflicts, DelConflicts} of
+            {_, [], []} ->
+                {ok, Acc};
+            {false, [], _} ->
+                {ok, Acc};
+            {true, _, _} ->
+                Value = {IsDeleted, Conflicts, DelConflicts},
+                FoldlFun({{Id,RevStr}, Value}, Offset, Acc);
+            {false, _, _} ->
+                Value = {IsDeleted, Conflicts, []},
+                FoldlFun({{Id,RevStr}, Value}, Offset, Acc)
+            end
+        end,
+
+        Acc0 = {Limit, SkipCount, undefined, [], nil},
+        case couch_db:enum_docs(Db, StartKey, Dir, EnumFun, Acc0) of
+        {ok, {_, _, undefined, _, _}} ->
+            % nothing found in the view, send empty view
+            send_json(Req, 200, {[{rows, []}]});
+        {ok, {_, _, Resp, _, _}} ->
+            % end the view
+            send_chunk(Resp, "\r\n]}"),
+            end_json_response(Resp);
+        Error ->
+            throw(Error)
+        end
+    end).
+
 db_doc_req(#httpd{method='DELETE'}=Req, Db, DocId) ->
     % check for the existence of the doc to handle the 404 case.
     couch_doc_open(Db, DocId, nil, []),

Modified: couchdb/trunk/src/couchdb/couch_httpd_view.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_view.erl?rev=803663&r1=803662&r2=803663&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_view.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_view.erl Wed Aug 12 18:48:25 2009
@@ -314,6 +314,8 @@
     [{include_docs, parse_bool_param(Value)}];
 parse_view_param("list", Value) ->
     [{list, ?l2b(Value)}];
+parse_view_param("deleted", Value) ->
+    [{deleted, parse_bool_param(Value)}];
 parse_view_param("callback", _) ->
     []; % Verified in the JSON response functions
 parse_view_param(Key, Value) ->
@@ -398,7 +400,9 @@
 validate_view_query(include_docs, _Value, Args) ->
     Args;
 validate_view_query(extra, _Value, Args) ->
-    Args.
+    Args;
+validate_view_query(deleted, Value, Args) ->
+    Args#view_query_args{deleted = Value}.
 
 make_view_fold_fun(Req, QueryArgs, Etag, Db, TotalViewCount, HelperFuns) ->
     #view_query_args{



Mime
View raw message