anchor.diff:          dir = Dir,
           since = Since
       } = Args1,
  -    {FilterFun, FilterArgs} = make_filter_fun(FilterName, Style, Req, Db0),
  -    Args = Args1#changes_args{filter_fun = FilterFun, filter_args = FilterArgs},
  +    Filter = configure_filter(FilterName, Style, Req, Db0),
  +    Args = Args1#changes_args{filter_fun = Filter},
       Start = fun() ->
           {ok, Db} = couch_db:reopen(Db0),
           StartSeq = case Dir of
  
positive.code: get_callback_acc(Callback) when is_function(Callback, 2) ->
      {fun(Ev, Data, _) -> Callback(Ev, Data) end, ok}.
  
  make_filter_fun([$_ | _] = FilterName, Style, Req, Db) ->
      builtin_filter_fun(FilterName, Style, Req, Db);
  make_filter_fun(_, main_only, _, _) ->
      fun ?MODULE:main_only_filter/1;
  make_filter_fun(_, all_docs, _, _) ->
      fun ?MODULE:all_docs_filter/1;
  make_filter_fun(FilterName, Style, Req, Db) ->
      {os_filter_fun(FilterName, Style, Req, Db), []}.
  
  os_filter_fun(FilterName, Style, Req, Db) ->
      case [list_to_binary(couch_httpd:unquote(Part))
              || Part <- string:tokens(FilterName, "/")] of
      [] ->
          fun(_Db2, #doc_info{revs=Revs}) ->
                  builtin_results(Style, Revs)
          end;
      [DName, FName] ->
          DesignId = <<"_design/", DName/binary>>,
          DDoc = couch_httpd_db:couch_doc_open(Db, DesignId, nil, [ejson_body]),
          % validate that the ddoc has the filter fun
          #doc{body={Props}} = DDoc,
          couch_util:get_nested_json_value({Props}, [<<"filters">>, FName]),
          fun(Db2, DocInfo) ->
              DocInfos =
              case Style of
              main_only ->
                  [DocInfo];
              all_docs ->
                  [DocInfo#doc_info{revs=[Rev]}|| Rev <- DocInfo#doc_info.revs]
              end,
              Docs = [Doc || {ok, Doc} <- [
                      couch_db:open_doc(Db2, DocInfo2, [deleted, conflicts])
                          || DocInfo2 <- DocInfos]],
              {ok, Passes} = couch_query_servers:filter_docs(
                  Req, Db2, DDoc, FName, Docs
              ),
              [{[{<<"rev">>, couch_doc:rev_to_str({RevPos,RevId})}]}
                  || {Pass, #doc{revs={RevPos,[RevId|_]}}}
                  <- lists:zip(Passes, Docs), Pass == true]
          end;
      _Else ->
          throw({bad_request,
              "filter parameter must be of the form `designname/filtername`"})
      end.
  
  builtin_filter_fun("_doc_ids", Style, {json_req, {Props}}, _Db) ->
      DocIds = couch_util:get_value(<<"doc_ids">>, Props),
      {filter_docids(DocIds, Style), DocIds};
  builtin_filter_fun("_doc_ids", Style, #httpd{method='POST'}=Req, _Db) ->
      {Props} = couch_httpd:json_body_obj(Req),
      DocIds =  couch_util:get_value(<<"doc_ids">>, Props, nil),
      {filter_docids(DocIds, Style), DocIds};
  builtin_filter_fun("_doc_ids", Style, #httpd{method='GET'}=Req, _Db) ->
      DocIds = ?JSON_DECODE(couch_httpd:qs_value(Req, "doc_ids", "null")),
      {filter_docids(DocIds, Style), DocIds};
  builtin_filter_fun("_design", Style, _Req, _Db) ->
      {filter_designdoc(Style), []};
  builtin_filter_fun("_view", Style, Req, Db) ->
      ViewName = couch_httpd:qs_value(Req, "view", ""),
      {filter_view(ViewName, Style, Db), []};
  builtin_filter_fun(_FilterName, _Style, _Req, _Db) ->
      throw({bad_request, "unknown builtin filter name"}).
  
  main_only_filter(#doc_info{revs=[#rev_info{rev=Rev}|_]}) ->
      [{[{<<"rev">>, couch_doc:rev_to_str(Rev)}]}].
  
  all_docs_filter(#doc_info{revs=Revs}) ->
      [{[{<<"rev">>, couch_doc:rev_to_str(Rev)}]} || #rev_info{rev=Rev} <- Revs].
  
  filter_docids(DocIds, Style) when is_list(DocIds)->
      fun(_Db, #doc_info{id=DocId, revs=Revs}) ->
              case lists:member(DocId, DocIds) of
                  true ->
                      builtin_results(Style, Revs);
                  _ -> []
              end
      end;
  filter_docids(_, _) ->
      throw({bad_request, "`doc_ids` filter parameter is not a list."}).
  
  filter_designdoc(Style) ->
      fun(_Db, #doc_info{id=DocId, revs=Revs}) ->
              case DocId of
              <<"_design", _/binary>> ->
                      builtin_results(Style, Revs);
                  _ -> []
              end
      end.
  
  filter_view("", _Style, _Db) ->
      throw({bad_request, "`view` filter parameter is not provided."});
  filter_view(ViewName, Style, Db) ->
      case [list_to_binary(couch_httpd:unquote(Part))
              || Part <- string:tokens(ViewName, "/")] of
          [] ->
              throw({bad_request, "Invalid `view` parameter."});
          [DName, VName] ->
              DesignId = <<"_design/", DName/binary>>,
              DDoc = couch_httpd_db:couch_doc_open(Db, DesignId, nil, [ejson_body]),
              % validate that the ddoc has the filter fun
              #doc{body={Props}} = DDoc,
              couch_util:get_nested_json_value({Props}, [<<"views">>, VName]),
              fun(Db2, DocInfo) ->
                  DocInfos =
                  case Style of
                  main_only ->
                      [DocInfo];
                  all_docs ->
                      [DocInfo#doc_info{revs=[Rev]}|| Rev <- DocInfo#doc_info.revs]
                  end,
                  Docs = [Doc || {ok, Doc} <- [
                          couch_db:open_doc(Db2, DocInfo2, [deleted, conflicts])
                              || DocInfo2 <- DocInfos]],
                  {ok, Passes} = couch_query_servers:filter_view(
                      DDoc, VName, Docs
                  ),
                  [{[{<<"rev">>, couch_doc:rev_to_str({RevPos,RevId})}]}
                      || {Pass, #doc{revs={RevPos,[RevId|_]}}}
                      <- lists:zip(Passes, Docs), Pass == true]
              end
          end.
  
  builtin_results(Style, [#rev_info{rev=Rev}|_]=Revs) ->
      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.
  
  get_changes_timeout(Args, Callback) ->
      #changes_args{
  
negative.code: 
  send_changes(Args, Acc0, FirstRound) ->
      #changes_args{
          dir = Dir
      } = Args,
      #changes_acc{
          db = Db,
          seq = StartSeq,
          filter = Filter
      } = Acc0,
      EnumFun = fun ?MODULE:changes_enumerator/2,
      case can_optimize(FirstRound, Filter) of
          {true, Fun} ->
              Fun(Db, StartSeq, Dir, EnumFun, Acc0, Filter);
          _ ->
              couch_db:changes_since(Db, StartSeq, EnumFun, [{dir, Dir}], Acc0)
      end.
  
  
  can_optimize(true, {doc_ids, _Style, DocIds})
          when length(DocIds) =< ?MAX_DOC_IDS ->
      {true, fun send_changes_doc_ids/6};
  can_optimize(true, {design_docs, _Style}) ->
      {true, fun send_changes_design_docs/6};
  can_optimize(_, _) ->
      false.
  
  
  send_changes_doc_ids(Db, StartSeq, Dir, Fun, Acc0, {doc_ids, _Style, DocIds}) ->
      Lookups = couch_btree:lookup(Db#db.id_tree, DocIds),
      FullInfos = lists:foldl(fun
          ({ok, FDI}, Acc) -> [FDI | Acc];
          (not_found, Acc) -> Acc
      end, [], Lookups),
      send_lookup_changes(FullInfos, StartSeq, Dir, Db, Fun, Acc0).
  
  
  send_changes_design_docs(Db, StartSeq, Dir, Fun, Acc0, {design_docs, _Style}) ->
      FoldFun = fun(FullDocInfo, _, Acc) ->
          {ok, [FullDocInfo | Acc]}
      end,
      KeyOpts = [{start_key, <<"_design/">>}, {end_key_gt, <<"_design0">>}],
      {ok, _, FullInfos} = couch_btree:fold(Db#db.id_tree, FoldFun, [], KeyOpts),
      send_lookup_changes(FullInfos, StartSeq, Dir, Db, Fun, Acc0).
  
  
  send_lookup_changes(FullDocInfos, StartSeq, Dir, Db, Fun, Acc0) ->
      FoldFun = case Dir of
          fwd -> fun lists:foldl/3;
          rev -> fun lists:foldr/3
      end,
      GreaterFun = case Dir of
          fwd -> fun(A, B) -> A > B end;
          rev -> fun(A, B) -> A =< B end
      end,
      DocInfos = lists:foldl(fun(FDI, Acc) ->
          DI = couch_doc:to_doc_info(FDI),
          case GreaterFun(DI#doc_info.high_seq, StartSeq) of
              true -> [DI | Acc];
              false -> Acc
          end
      end, [], FullDocInfos),
      SortedDocInfos = lists:keysort(#doc_info.high_seq, DocInfos),
      FinalAcc = try
          FoldFun(fun(DocInfo, Acc) ->
              case Fun(DocInfo, Acc) of
                  {ok, NewAcc} ->
                      NewAcc;
                  {stop, NewAcc} ->
                      throw({stop, NewAcc})
              end
          end, Acc0, SortedDocInfos)
      catch
          {stop, Acc} -> Acc
      end,
      case Dir of
          fwd -> {ok, FinalAcc#changes_acc{seq = couch_db:get_update_seq(Db)}};
          rev -> {ok, FinalAcc}
      end.
  
  
  