anchor.diff:          conflicts = Conflicts,
           limit = Limit,
           feed = ResponseType,
  -        filter_fun = FilterFun
  +        filter_fun = Filter
       } = Args,
       #changes_acc{
           db = Db,
           seq = StartSeq,
           prepend = Prepend,
  -        filter = FilterFun,
  +        filter = Filter,
           callback = Callback,
           user_acc = UserAcc,
           resp_type = ResponseType,
  
positive.code: 
  send_changes(Args, Acc0, FirstRound) ->
      #changes_args{
          dir = Dir,
          filter = FilterName,
          filter_args = FilterArgs
      } = Args,
      #changes_acc{
          db = Db,
          seq = StartSeq
      } = Acc0,
      case FirstRound of
      true ->
          case FilterName of
          "_doc_ids" when length(FilterArgs) =< ?MAX_DOC_IDS ->
              send_changes_doc_ids(
                  FilterArgs, Db, StartSeq, Dir, fun changes_enumerator/2, Acc0);
          "_design" ->
              send_changes_design_docs(
                  Db, StartSeq, Dir, fun changes_enumerator/2, Acc0);
          _ ->
              couch_db:changes_since(
                  Db, StartSeq, fun changes_enumerator/2, [{dir, Dir}], Acc0)
          end;
      false ->
          couch_db:changes_since(
              Db, StartSeq, fun changes_enumerator/2, [{dir, Dir}], Acc0)
      end.
  
  
  send_changes_doc_ids(DocIds, Db, StartSeq, Dir, Fun, Acc0) ->
      Lookups = couch_btree:lookup(Db#db.id_tree, DocIds),
      FullDocInfos = lists:foldl(
          fun({ok, FDI}, Acc) ->
              [FDI | Acc];
          (not_found, Acc) ->
              Acc
          end,
          [], Lookups),
      send_lookup_changes(FullDocInfos, StartSeq, Dir, Db, Fun, Acc0).
  
  
  send_changes_design_docs(Db, StartSeq, Dir, Fun, Acc0) ->
      FoldFun = fun(FullDocInfo, _, Acc) ->
          {ok, [FullDocInfo | Acc]}
      end,
      KeyOpts = [{start_key, <<"_design/">>}, {end_key_gt, <<"_design0">>}],
      {ok, _, FullDocInfos} = couch_btree:fold(
          Db#db.id_tree, FoldFun, [], KeyOpts),
      send_lookup_changes(FullDocInfos, 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
      throw:{stop, Acc} ->
          Acc
      end,
      case Dir of
      fwd ->
          {ok, FinalAcc#changes_acc{seq = couch_db:get_update_seq(Db)}};
      rev ->
          {ok, FinalAcc}
      end.
  
  
  
negative.code: get_callback_acc(Callback) when is_function(Callback, 2) ->
      {fun(Ev, Data, _) -> Callback(Ev, Data) end, ok}.
  
  
  configure_filter("_doc_ids", Style, Req, _Db) ->
      {doc_ids, Style, get_doc_ids(Req)};
  configure_filter("_design", Style, _Req, _Db) ->
      {design_docs, Style};
  configure_filter("_view", Style, Req, Db) ->
      ViewName = couch_httpd:qs_value(Req, "view", ""),
      if ViewName /= "" -> ok; true ->
          throw({bad_request, "`view` filter parameter is not provided."})
      end,
      ViewNameParts = string:tokens(ViewName, "/"),
      case [?l2b(couch_httpd:unquote(Part)) || Part <- ViewNameParts] of
          [DName, VName] ->
              {ok, DDoc} = open_ddoc(Db, <<"_design/", DName/binary>>),
              check_member_exists(DDoc, [<<"views">>, VName]),
              {view, Style, DDoc, VName};
          [] ->
              Msg = "`view` must be of the form `designname/viewname`",
              throw({bad_request, Msg})
      end;
  configure_filter([$_ | _], _Style, _Req, _Db) ->
      throw({bad_request, "unknown builtin filter name"});
  configure_filter("", main_only, _Req, _Db) ->
      {default, main_only};
  configure_filter("", all_docs, _Req, _Db) ->
      {default, all_docs};
  configure_filter(FilterName, Style, Req, Db) ->
      FilterNameParts = string:tokens(FilterName, "/"),
      case [?l2b(couch_httpd:unquote(Part)) || Part <- FilterNameParts] of
          [DName, FName] ->
              {ok, DDoc} = open_ddoc(Db, <<"_design/", DName/binary>>),
              check_member_exists(DDoc, [<<"filters">>, FName]),
              {custom, Style, Req, DDoc, FName};
          [] ->
              {default, Style};
          _Else ->
              Msg = "`filter` must be of the form `designname/filtername`",
              throw({bad_request, Msg})
      end.
  
  
  filter(Db, #full_doc_info{}=FDI, Filter) ->
      filter(Db, couch_doc:to_doc_info(FDI), Filter);
  filter(_Db, DocInfo, {default, Style}) ->
      apply_style(DocInfo, Style);
  filter(_Db, DocInfo, {doc_ids, Style, DocIds}) ->
      case lists:member(DocInfo#doc_info.id, DocIds) of
          true ->
              apply_style(DocInfo, Style);
          false ->
              []
      end;
  filter(_Db, DocInfo, {design_docs, Style}) ->
      case DocInfo#doc_info.id of
          <<"_design", _/binary>> ->
              apply_style(DocInfo, Style);
          _ ->
              []
      end;
  filter(Db, DocInfo, {view, Style, DDoc, VName}) ->
      Docs = open_revs(Db, DocInfo, Style),
      {ok, Passes} = couch_query_servers:filter_view(DDoc, VName, Docs),
      filter_revs(Passes, Docs);
  filter(Db, DocInfo, {custom, Style, Req0, DDoc, FName}) ->
      Req = case Req0 of
          {json_req, _} -> Req0;
          #httpd{} -> {json_req, couch_httpd_external:json_req_obj(Req0, Db)}
      end,
      Docs = open_revs(Db, DocInfo, Style),
      {ok, Passes} = couch_query_servers:filter_docs(Req, Db, DDoc, FName, Docs),
      filter_revs(Passes, Docs).
  
  
  get_doc_ids({json_req, {Props}}) ->
      check_docids(couch_util:get_value(<<"doc_ids">>, Props));
  get_doc_ids(#httpd{method='POST'}=Req) ->
      {Props} = couch_httpd:json_body_obj(Req),
      check_docids(couch_util:get_value(<<"doc_ids">>, Props));
  get_doc_ids(#httpd{method='GET'}=Req) ->
      DocIds = ?JSON_DECODE(couch_httpd:qs_value(Req, "doc_ids", "null")),
      check_docids(DocIds);
  get_doc_ids(_) ->
      throw({bad_request, no_doc_ids_provided}).
  
  
  check_docids(DocIds) when is_list(DocIds) ->
      lists:foreach(fun
          (DocId) when not is_binary(DocId) ->
              Msg = "`doc_ids` filter parameter is not a list of binaries.",
              throw({bad_request, Msg});
          (_) -> ok
      end, DocIds),
      DocIds;
  check_docids(_) ->
      Msg = "`doc_ids` filter parameter is not a list of binaries.",
      throw({bad_request, Msg}).
  
  
  open_ddoc(#db{name= <<"shards/", _/binary>> =ShardName}, DDocId) ->
      {_, Ref} = spawn_monitor(fun() ->
          exit(fabric:open_doc(mem3:dbname(ShardName), DDocId, []))
      end),
      receive
          {'DOWN', Ref, _, _, {ok, _}=Response} ->
              Response;
          {'DOWN', Ref, _, _, Response} ->
              throw(Response)
      end;
  open_ddoc(Db, DDocId) ->
      case couch_db:open_doc(Db, DDocId, [ejson_body]) of
          {ok, _} = Resp -> Resp;
          Else -> throw(Else)
      end.
  
  
  check_member_exists(#doc{body={Props}}, Path) ->
      couch_util:get_nested_json_value({Props}, Path).
  
  
  apply_style(#doc_info{revs=Revs}, main_only) ->
      [#rev_info{rev=Rev} | _] = Revs,
      [{[{<<"rev">>, couch_doc:rev_to_str(Rev)}]}];
  apply_style(#doc_info{revs=Revs}, all_docs) ->
      [{[{<<"rev">>, couch_doc:rev_to_str(R)}]} || #rev_info{rev=R} <- Revs].
  
  
  open_revs(Db, DocInfo, Style) ->
      DocInfos = case Style of
          main_only -> [DocInfo];
          all_docs -> [DocInfo#doc_info{revs=[R]}|| R <- DocInfo#doc_info.revs]
      end,
      OpenOpts = [deleted, conflicts],
      % Relying on list comprehensions to silence errors
      OpenResults = [couch_db:open_doc(Db, DI, OpenOpts) || DI <- DocInfos],
      [Doc || {ok, Doc} <- OpenResults].
  
  
  filter_revs(Passes, Docs) ->
      lists:flatmap(fun
          ({true, #doc{revs={RevPos, [RevId | _]}}}) ->
              RevStr = couch_doc:rev_to_str({RevPos, RevId}),
              Change = {[{<<"rev">>, RevStr}]},
              [Change];
          (_) ->
              []
      end, lists:zip(Passes, Docs)).
  
  
  get_changes_timeout(Args, Callback) ->
      #changes_args{
  