C0 code coverage information

Generated on 2009-08-09 21:22:54 with etap 0.3.4.

Name Total lines Lines of code Total coverage Code coverage
couch_view_updater ?? ?? ??
0% 
....1 % Licensed under the Apache License, Version 2.0 (the "License"); you may not
....2 % use this file except in compliance with the License. You may obtain a copy of
....3 % the License at
....4 %
....5 %   http://www.apache.org/licenses/LICENSE-2.0
....6 %
....7 % Unless required by applicable law or agreed to in writing, software
....8 % distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
....9 % WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
...10 % License for the specific language governing permissions and limitations under
...11 % the License.
...12 
...13 -module(couch_view_updater).
...14 
...15 -export([update/2]).
...16 
...17 -include("couch_db.hrl").
...18 
...19 update(Owner, Group) ->
...20     #group{
...21         db = #db{name=DbName} = Db,
...22         name = GroupName,
...23         current_seq = Seq,
...24         purge_seq = PurgeSeq
...25     } = Group,
...26     couch_task_status:add_task(<<"View Group Indexer">>, <>, <<"Starting index update">>),
...27 
...28     DbPurgeSeq = couch_db:get_purge_seq(Db),
...29     Group2 =
...30     if DbPurgeSeq == PurgeSeq ->
...31         Group;
...32     DbPurgeSeq == PurgeSeq + 1 ->
...33         couch_task_status:update(<<"Removing purged entries from view index.">>),
...34         purge_index(Group);
...35     true ->
...36         couch_task_status:update(<<"Resetting view index due to lost purge entries.">>),
...37         exit(reset)
...38     end,
...39 
...40     ViewEmptyKVs = [{View, []} || View <- Group2#group.views],
...41     % compute on all docs modified since we last computed.
...42     TotalChanges = couch_db:count_changes_since(Db, Seq),
...43     % update status every half second
...44     couch_task_status:set_update_frequency(500),
...45     {ok, {_,{UncomputedDocs, Group3, ViewKVsToAdd, DocIdViewIdKeys}}}
...46         = couch_db:enum_docs_since(
...47             Db,
...48             Seq,
...49             fun(DocInfo, _, {ChangesProcessed, Acc}) ->
...50                 couch_task_status:update("Processed ~p of ~p changes (~p%)",
...51                         [ChangesProcessed, TotalChanges, (ChangesProcessed*100) div TotalChanges]),
...52                 {ok, {ChangesProcessed+1, process_doc(Db, Owner, DocInfo, Acc)}}
...53             end,
...54             {0, {[], Group2, ViewEmptyKVs, []}}
...55             ),
...56     couch_task_status:set_update_frequency(0),
...57     couch_task_status:update("Finishing."),
...58     {Group4, Results} = view_compute(Group3, UncomputedDocs),
...59     {ViewKVsToAdd2, DocIdViewIdKeys2} = view_insert_query_results(
...60             UncomputedDocs, Results, ViewKVsToAdd, DocIdViewIdKeys),
...61     couch_query_servers:stop_doc_map(Group4#group.query_server),
...62     NewSeq = couch_db:get_update_seq(Db),
...63     {ok, Group5} = write_changes(Group4, ViewKVsToAdd2, DocIdViewIdKeys2,
...64                 NewSeq),
...65     exit({new_group, Group5#group{query_server=nil}}).
...66 
...67 
...68 purge_index(#group{db=Db, views=Views, id_btree=IdBtree}=Group) ->
...69     {ok, PurgedIdsRevs} = couch_db:get_last_purged(Db),
...70     Ids = [Id || {Id, _Revs} <- PurgedIdsRevs],
...71     {ok, Lookups, IdBtree2} = couch_btree:query_modify(IdBtree, Ids, [], Ids),
...72 
...73     % now populate the dictionary with all the keys to delete
...74     ViewKeysToRemoveDict = lists:foldl(
...75         fun({ok,{DocId,ViewNumRowKeys}}, ViewDictAcc) ->
...76             lists:foldl(
...77                 fun({ViewNum, RowKey}, ViewDictAcc2) ->
...78                     dict:append(ViewNum, {RowKey, DocId}, ViewDictAcc2)
...79                 end, ViewDictAcc, ViewNumRowKeys);
...80         ({not_found, _}, ViewDictAcc) ->
...81             ViewDictAcc
...82         end, dict:new(), Lookups),
...83 
...84     % Now remove the values from the btrees
...85     Views2 = lists:map(
...86         fun(#view{id_num=Num,btree=Btree}=View) ->
...87             case dict:find(Num, ViewKeysToRemoveDict) of
...88             {ok, RemoveKeys} ->
...89                 {ok, Btree2} = couch_btree:add_remove(Btree, [], RemoveKeys),
...90                 View#view{btree=Btree2};
...91             error -> % no keys to remove in this view
...92                 View
...93             end
...94         end, Views),
...95     Group#group{id_btree=IdBtree2,
...96             views=Views2,
...97             purge_seq=couch_db:get_purge_seq(Db)}.
...98 
...99 % This fun computes once for each document
..100 process_doc(Db, Owner, DocInfo, {Docs, Group, ViewKVs, DocIdViewIdKeys}) ->
..101     #group{ design_options = DesignOptions } = Group,
..102 
..103     #doc_info{id=DocId, revs=[#rev_info{deleted=Deleted}|_]} = DocInfo,
..104     IncludeDesign = proplists:get_value(<<"include_design">>,
..105         DesignOptions, false),
..106     LocalSeq = proplists:get_value(<<"local_seq">>,
..107         DesignOptions, false),
..108     DocOpts = case LocalSeq of
..109         true ->
..110             [conflicts, deleted_conflicts, local_seq];
..111         _ ->
..112             [conflicts, deleted_conflicts]
..113     end,
..114     case {IncludeDesign, DocId} of
..115     {false, <>} -> % we skip design docs
..116         {Docs, Group, ViewKVs, DocIdViewIdKeys};
..117     _ ->
..118         {Docs2, DocIdViewIdKeys2} =
..119         if Deleted ->
..120             {Docs, [{DocId, []} | DocIdViewIdKeys]};
..121         true ->
..122             {ok, Doc} = couch_db:open_doc_int(Db, DocInfo,
..123                 DocOpts),
..124             {[Doc | Docs], DocIdViewIdKeys}
..125         end,
..126 
..127         case couch_util:should_flush() of
..128         true ->
..129             {Group1, Results} = view_compute(Group, Docs2),
..130             {ViewKVs3, DocIdViewIdKeys3} = view_insert_query_results(Docs2,
..131                 Results, ViewKVs, DocIdViewIdKeys2),
..132             {ok, Group2} = write_changes(Group1, ViewKVs3, DocIdViewIdKeys3,
..133                 DocInfo#doc_info.high_seq),
..134             if is_pid(Owner) ->
..135                 ok = gen_server:cast(Owner, {partial_update, self(), Group2});
..136             true -> ok end,
..137             garbage_collect(),
..138             ViewEmptyKeyValues = [{View, []} || View <- Group2#group.views],
..139             {[], Group2, ViewEmptyKeyValues, []};
..140         false ->
..141             {Docs2, Group, ViewKVs, DocIdViewIdKeys2}
..142         end
..143     end.
..144 
..145 view_insert_query_results([], [], ViewKVs, DocIdViewIdKeysAcc) ->
..146     {ViewKVs, DocIdViewIdKeysAcc};
..147 view_insert_query_results([Doc|RestDocs], [QueryResults | RestResults], ViewKVs, DocIdViewIdKeysAcc) ->
..148     {NewViewKVs, NewViewIdKeys} = view_insert_doc_query_results(Doc, QueryResults, ViewKVs, [], []),
..149     NewDocIdViewIdKeys = [{Doc#doc.id, NewViewIdKeys} | DocIdViewIdKeysAcc],
..150     view_insert_query_results(RestDocs, RestResults, NewViewKVs, NewDocIdViewIdKeys).
..151 
..152 
..153 view_insert_doc_query_results(_Doc, [], [], ViewKVsAcc, ViewIdKeysAcc) ->
..154     {lists:reverse(ViewKVsAcc), lists:reverse(ViewIdKeysAcc)};
..155 view_insert_doc_query_results(#doc{id=DocId}=Doc, [ResultKVs|RestResults], [{View, KVs}|RestViewKVs], ViewKVsAcc, ViewIdKeysAcc) ->
..156     % Take any identical keys and combine the values
..157     ResultKVs2 = lists:foldl(
..158         fun({Key,Value}, [{PrevKey,PrevVal}|AccRest]) ->
..159             case Key == PrevKey of
..160             true ->
..161                 case PrevVal of
..162                 {dups, Dups} ->
..163                     [{PrevKey, {dups, [Value|Dups]}} | AccRest];
..164                 _ ->
..165                     [{PrevKey, {dups, [Value,PrevVal]}} | AccRest]
..166                 end;
..167             false ->
..168                 [{Key,Value},{PrevKey,PrevVal}|AccRest]
..169             end;
..170         (KV, []) ->
..171            [KV]
..172         end, [], lists:sort(ResultKVs)),
..173     NewKVs = [{{Key, DocId}, Value} || {Key, Value} <- ResultKVs2],
..174     NewViewKVsAcc = [{View, NewKVs ++ KVs} | ViewKVsAcc],
..175     NewViewIdKeys = [{View#view.id_num, Key} || {Key, _Value} <- ResultKVs2],
..176     NewViewIdKeysAcc = NewViewIdKeys ++ ViewIdKeysAcc,
..177     view_insert_doc_query_results(Doc, RestResults, RestViewKVs, NewViewKVsAcc, NewViewIdKeysAcc).
..178 
..179 view_compute(Group, []) ->
..180     {Group, []};
..181 view_compute(#group{def_lang=DefLang, query_server=QueryServerIn}=Group, Docs) ->
..182     {ok, QueryServer} =
..183     case QueryServerIn of
..184     nil -> % doc map not started
..185         Definitions = [View#view.def || View <- Group#group.views],
..186         couch_query_servers:start_doc_map(DefLang, Definitions);
..187     _ ->
..188         {ok, QueryServerIn}
..189     end,
..190     {ok, Results} = couch_query_servers:map_docs(QueryServer, Docs),
..191     {Group#group{query_server=QueryServer}, Results}.
..192 
..193 
..194 
..195 write_changes(Group, ViewKeyValuesToAdd, DocIdViewIdKeys, NewSeq) ->
..196     #group{id_btree=IdBtree} = Group,
..197 
..198     AddDocIdViewIdKeys = [{DocId, ViewIdKeys} || {DocId, ViewIdKeys} <- DocIdViewIdKeys, ViewIdKeys /= []],
..199     RemoveDocIds = [DocId || {DocId, ViewIdKeys} <- DocIdViewIdKeys, ViewIdKeys == []],
..200     LookupDocIds = [DocId || {DocId, _ViewIdKeys} <- DocIdViewIdKeys],
..201     {ok, LookupResults, IdBtree2}
..202         = couch_btree:query_modify(IdBtree, LookupDocIds, AddDocIdViewIdKeys, RemoveDocIds),
..203     KeysToRemoveByView = lists:foldl(
..204         fun(LookupResult, KeysToRemoveByViewAcc) ->
..205             case LookupResult of
..206             {ok, {DocId, ViewIdKeys}} ->
..207                 lists:foldl(
..208                     fun({ViewId, Key}, KeysToRemoveByViewAcc2) ->
..209                         dict:append(ViewId, {Key, DocId}, KeysToRemoveByViewAcc2)
..210                     end,
..211                     KeysToRemoveByViewAcc, ViewIdKeys);
..212             {not_found, _} ->
..213                 KeysToRemoveByViewAcc
..214             end
..215         end,
..216         dict:new(), LookupResults),
..217 
..218     Views2 = [
..219         begin
..220             KeysToRemove = couch_util:dict_find(View#view.id_num, KeysToRemoveByView, []),
..221             {ok, ViewBtree2} = couch_btree:add_remove(View#view.btree, AddKeyValues, KeysToRemove),
..222             View#view{btree = ViewBtree2}
..223         end
..224     ||
..225         {View, AddKeyValues} <- ViewKeyValuesToAdd
..226     ],
..227     Group2 = Group#group{views=Views2, current_seq=NewSeq, id_btree=IdBtree2},
..228     {ok, Group2}.
..229 

Generated using etap 0.3.4.