diff options
-rw-r--r-- | share/www/script/couch.js | 4 | ||||
-rw-r--r-- | share/www/script/couch_tests.js | 52 | ||||
-rw-r--r-- | src/couchdb/couch_db.hrl | 1 | ||||
-rw-r--r-- | src/couchdb/couch_httpd_view.erl | 8 | ||||
-rw-r--r-- | src/couchdb/couch_view.erl | 25 | ||||
-rw-r--r-- | src/couchdb/couch_view_group.erl | 7 | ||||
-rw-r--r-- | src/couchdb/couch_view_updater.erl | 27 |
7 files changed, 97 insertions, 27 deletions
diff --git a/share/www/script/couch.js b/share/www/script/couch.js index 0a2698ab..96612faa 100644 --- a/share/www/script/couch.js +++ b/share/www/script/couch.js @@ -129,6 +129,10 @@ function CouchDB(name, httpHeaders) { reduceFun = reduceFun.toSource ? reduceFun.toSource() : "(" + reduceFun.toString() + ")"; body.reduce = reduceFun; } + if (options && options.options != undefined) { + body.options = options.options; + delete options.options; + } this.last_req = this.request("POST", this.uri + "_temp_view" + encodeOptions(options), { headers: {"Content-Type": "application/json"}, body: JSON.stringify(body) diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js index 28a1b9f4..8bdebec1 100644 --- a/share/www/script/couch_tests.js +++ b/share/www/script/couch_tests.js @@ -773,6 +773,58 @@ var tests = { } }, + design_options: function(debug) { + var db = new CouchDB("test_suite_db"); + db.deleteDb(); + db.createDb(); + if (debug) debugger; + + //// test the includes_design option + var map = "function (doc) {emit(null, doc._id);}"; + + // we need a design doc even to test temp views with it + var designDoc = { + _id:"_design/fu", + language: "javascript", + options: { + include_design: true + }, + views: { + data: {"map": map} + } + }; + T(db.save(designDoc).ok); + + // should work for temp views + var rows = db.query(map, null, {options:{include_design: true}}).rows; + T(rows.length == 1); + T(rows[0].value == "_design/fu"); + + rows = db.query(map).rows; + T(rows.length == 0); + + // when true, should include design docs in views + rows = db.view("fu/data").rows; + T(rows.length == 1); + T(rows[0].value == "_design/fu"); + + // when false, should not + designDoc.options.include_design = false; + delete designDoc._rev; + designDoc._id = "_design/bingo"; + T(db.save(designDoc).ok); + rows = db.view("bingo/data").rows; + T(rows.length == 0); + + // should default to false + delete designDoc.options; + delete designDoc._rev; + designDoc._id = "_design/bango"; + T(db.save(designDoc).ok); + rows = db.view("bango/data").rows; + T(rows.length == 0); + }, + multiple_rows: function(debug) { var db = new CouchDB("test_suite_db"); db.deleteDb(); diff --git a/src/couchdb/couch_db.hrl b/src/couchdb/couch_db.hrl index eb449ab7..1afea912 100644 --- a/src/couchdb/couch_db.hrl +++ b/src/couchdb/couch_db.hrl @@ -174,6 +174,7 @@ fd=nil, name, def_lang, + design_options=[], views, id_btree=nil, current_seq=0, diff --git a/src/couchdb/couch_httpd_view.erl b/src/couchdb/couch_httpd_view.erl index 0d5c0df4..fc3302d7 100644 --- a/src/couchdb/couch_httpd_view.erl +++ b/src/couchdb/couch_httpd_view.erl @@ -68,15 +68,17 @@ handle_temp_view_req(#httpd{method='POST'}=Req, Db) -> end, {Props} = couch_httpd:json_body(Req), Language = proplists:get_value(<<"language">>, Props, <<"javascript">>), + {DesignOptions} = proplists:get_value(<<"options">>, Props, {[]}), MapSrc = proplists:get_value(<<"map">>, Props), Keys = proplists:get_value(<<"keys">>, Props, nil), case proplists:get_value(<<"reduce">>, Props, null) of null -> - {ok, View} = couch_view:get_temp_map_view(Db, Language, MapSrc), + {ok, View} = couch_view:get_temp_map_view(Db, Language, + DesignOptions, MapSrc), output_map_view(Req, View, Db, QueryArgs, Keys); RedSrc -> - {ok, View} = couch_view:get_temp_reduce_view(Db, Language, MapSrc, - RedSrc), + {ok, View} = couch_view:get_temp_reduce_view(Db, Language, + DesignOptions, MapSrc, RedSrc), output_reduce_view(Req, View, QueryArgs, Keys) end; diff --git a/src/couchdb/couch_view.erl b/src/couchdb/couch_view.erl index 3faec4b9..e790efd3 100644 --- a/src/couchdb/couch_view.erl +++ b/src/couchdb/couch_view.erl @@ -15,7 +15,7 @@ -export([start_link/0,fold/4,fold/5,less_json/2,less_json_keys/2,expand_dups/2, detuple_kvs/2,init/1,terminate/2,handle_call/3,handle_cast/2,handle_info/2, - code_change/3,get_reduce_view/4,get_temp_reduce_view/4,get_temp_map_view/3, + code_change/3,get_reduce_view/4,get_temp_reduce_view/5,get_temp_map_view/4, get_map_view/4,get_row_count/1,reduce_to_count/1,fold_reduce/7, extract_map_view/1]). @@ -28,9 +28,9 @@ start_link() -> gen_server:start_link({local, couch_view}, couch_view, [], []). -get_temp_updater(DbName, Type, MapSrc, RedSrc) -> +get_temp_updater(DbName, Type, DesignOptions, MapSrc, RedSrc) -> {ok, Pid} = gen_server:call(couch_view, - {start_temp_updater, DbName, Type, MapSrc, RedSrc}), + {start_temp_updater, DbName, Type, DesignOptions, MapSrc, RedSrc}), Pid. get_group_server(DbName, GroupId) -> @@ -51,17 +51,17 @@ get_group(Db, GroupId, Stale) -> MinUpdateSeq). -get_temp_group(Db, Type, MapSrc, RedSrc) -> +get_temp_group(Db, Type, DesignOptions, MapSrc, RedSrc) -> couch_view_group:request_group( - get_temp_updater(couch_db:name(Db), Type, MapSrc, RedSrc), + get_temp_updater(couch_db:name(Db), Type, DesignOptions, MapSrc, RedSrc), couch_db:get_update_seq(Db)). get_row_count(#view{btree=Bt}) -> {ok, {Count, _Reds}} = couch_btree:full_reduce(Bt), {ok, Count}. -get_temp_reduce_view(Db, Type, MapSrc, RedSrc) -> - {ok, #group{views=[View]}} = get_temp_group(Db, Type, MapSrc, RedSrc), +get_temp_reduce_view(Db, Type, DesignOptions, MapSrc, RedSrc) -> + {ok, #group{views=[View]}} = get_temp_group(Db, Type, DesignOptions, MapSrc, RedSrc), {ok, {temp_reduce, View}}. @@ -136,8 +136,8 @@ get_key_pos(Key, [_|Rest], N) -> get_key_pos(Key, Rest, N+1). -get_temp_map_view(Db, Type, Src) -> - {ok, #group{views=[View]}} = get_temp_group(Db, Type, Src, []), +get_temp_map_view(Db, Type, DesignOptions, Src) -> + {ok, #group{views=[View]}} = get_temp_group(Db, Type, DesignOptions, Src, []), {ok, View}. get_map_view(Db, GroupId, Name, Stale) -> @@ -220,8 +220,9 @@ terminate(_Reason,_State) -> ok. -handle_call({start_temp_updater, DbName, Lang, MapSrc, RedSrc}, _From, #server{root_dir=Root}=Server) -> - <<SigInt:128/integer>> = erlang:md5(term_to_binary({Lang, MapSrc, RedSrc})), +handle_call({start_temp_updater, DbName, Lang, DesignOptions, MapSrc, RedSrc}, + _From, #server{root_dir=Root}=Server) -> + <<SigInt:128/integer>> = erlang:md5(term_to_binary({Lang, DesignOptions, MapSrc, RedSrc})), Name = lists:flatten(io_lib:format("_temp_~.36B",[SigInt])), Pid = case ets:lookup(group_servers_by_name, {DbName, Name}) of @@ -235,7 +236,7 @@ handle_call({start_temp_updater, DbName, Lang, MapSrc, RedSrc}, _From, #server{r ok end, ?LOG_DEBUG("Spawning new temp update process for db ~s.", [DbName]), - {ok, NewPid} = couch_view_group:start_link({slow_view, DbName, Fd, Lang, MapSrc, RedSrc}), + {ok, NewPid} = couch_view_group:start_link({slow_view, DbName, Fd, Lang, DesignOptions, MapSrc, RedSrc}), true = ets:insert(couch_temp_group_fd_by_db, {DbName, Fd, Count + 1}), add_to_ets(NewPid, DbName, Name), NewPid; diff --git a/src/couchdb/couch_view_group.erl b/src/couchdb/couch_view_group.erl index 6d193516..4e7d7767 100644 --- a/src/couchdb/couch_view_group.erl +++ b/src/couchdb/couch_view_group.erl @@ -262,7 +262,7 @@ prepare_group({view, RootDir, DbName, GroupId}, ForceReset)-> catch delete_index_file(RootDir, DbName, GroupId), Error end; -prepare_group({slow_view, DbName, Fd, Lang, MapSrc, RedSrc}, _ForceReset) -> +prepare_group({slow_view, DbName, Fd, Lang, DesignOptions, MapSrc, RedSrc}, _ForceReset) -> case couch_db:open(DbName, []) of {ok, Db} -> View = #view{map_names=[<<"_temp">>], @@ -271,7 +271,7 @@ prepare_group({slow_view, DbName, Fd, Lang, MapSrc, RedSrc}, _ForceReset) -> def=MapSrc, reduce_funs= if RedSrc==[] -> []; true -> [{<<"_temp">>, RedSrc}] end}, {ok, init_group(Db, Fd, #group{type=slow_view, name= <<"_temp">>, db=Db, - views=[View], def_lang=Lang}, nil)}; + views=[View], def_lang=Lang, design_options=DesignOptions}, nil)}; Error -> Error end. @@ -311,6 +311,7 @@ open_db_group(DbName, GroupId) -> % maybe move to another module design_doc_to_view_group(#doc{id=Id,body={Fields}}) -> Language = proplists:get_value(<<"language">>, Fields, <<"javascript">>), + {DesignOptions} = proplists:get_value(<<"options">>, Fields, {[]}), {RawViews} = proplists:get_value(<<"views">>, Fields, {[]}), % add the views to a dictionary object, with the map source as the key @@ -338,7 +339,7 @@ design_doc_to_view_group(#doc{id=Id,body={Fields}}) -> {View#view{id_num=N},N+1} end, 0, dict:to_list(DictBySrc)), - Group = #group{name=Id, views=Views, def_lang=Language}, + Group = #group{name=Id, views=Views, def_lang=Language, design_options=DesignOptions}, Group#group{sig=erlang:md5(term_to_binary(Group))}. reset_group(#group{views=Views}=Group) -> diff --git a/src/couchdb/couch_view_updater.erl b/src/couchdb/couch_view_updater.erl index b6ae860f..956ac3f1 100644 --- a/src/couchdb/couch_view_updater.erl +++ b/src/couchdb/couch_view_updater.erl @@ -90,43 +90,52 @@ purge_index(#group{db=Db, views=Views, id_btree=IdBtree}=Group) -> views=Views2, purge_seq=couch_db:get_purge_seq(Db)}. -process_doc(Db, DocInfo, {Docs, #group{sig=Sig,name=GroupId}=Group, ViewKVs, +process_doc(Db, DocInfo, {Docs, #group{sig=Sig,name=GroupId,design_options=DesignOptions}=Group, ViewKVs, DocIdViewIdKeys}) -> % This fun computes once for each document #doc_info{id=DocId, deleted=Deleted} = DocInfo, - case DocId of - GroupId -> + IncludeDesign = proplists:get_value(<<"include_design">>, + DesignOptions, false), + case {IncludeDesign, DocId} of + {_, GroupId} -> % uh oh. this is the design doc with our definitions. See if % anything in the definition changed. - case couch_db:open_doc(Db, DocInfo) of + case couch_db:open_doc(Db, DocInfo, [conflicts, deleted_conflicts]) of {ok, Doc} -> case couch_view_group:design_doc_to_view_group(Doc) of #group{sig=Sig} -> % The same md5 signature, keep on computing - {Docs, Group, ViewKVs, DocIdViewIdKeys}; + case IncludeDesign of + true -> + {[Doc | Docs], Group, ViewKVs, DocIdViewIdKeys}; + _ -> + {Docs, Group, ViewKVs, DocIdViewIdKeys} + end; _ -> exit(reset) end; {not_found, deleted} -> exit(reset) end; - <<?DESIGN_DOC_PREFIX, _/binary>> -> % we skip design docs + {false, <<?DESIGN_DOC_PREFIX, _/binary>>} -> % we skip design docs {Docs, Group, ViewKVs, DocIdViewIdKeys}; _ -> {Docs2, DocIdViewIdKeys2} = if Deleted -> {Docs, [{DocId, []} | DocIdViewIdKeys]}; true -> - {ok, Doc} = couch_db:open_doc(Db, DocInfo, [conflicts, deleted_conflicts]), + {ok, Doc} = couch_db:open_doc(Db, DocInfo, + [conflicts, deleted_conflicts]), {[Doc | Docs], DocIdViewIdKeys} end, case couch_util:should_flush() of true -> {Group1, Results} = view_compute(Group, Docs2), - {ViewKVs3, DocIdViewIdKeys3} = view_insert_query_results(Docs2, Results, ViewKVs, DocIdViewIdKeys2), + {ViewKVs3, DocIdViewIdKeys3} = view_insert_query_results(Docs2, + Results, ViewKVs, DocIdViewIdKeys2), {ok, Group2} = write_changes(Group1, ViewKVs3, DocIdViewIdKeys3, - DocInfo#doc_info.update_seq), + DocInfo#doc_info.update_seq), garbage_collect(), ViewEmptyKeyValues = [{View, []} || View <- Group2#group.views], {[], Group2, ViewEmptyKeyValues, []}; |