anchor.diff:  %% @doc Execute searches and retrieve results in JSON.
   -module(controller_search).
   
  --export([
  -    init/1,
  -    content_types_provided/2,
  -    to_json/2
  -]).
  +-export([ init/1
  +        , content_types_provided/2
  +        , to_json/2
  +        ]
  +       ).
   
   -include_lib("zotonic.hrl").
   -include_lib("controller_webmachine_helper.hrl").
   
   -record(state, {mode = undefined}).
  +
  +%%%-----------------------------------------------------------------------------
  +%%% Resource functions
  +%%%-----------------------------------------------------------------------------
   
   init(Args) ->
       Mode = proplists:get_value(mode, Args),
  
positive.code: argument(Argument) ->
      Argument.
  
  %% @doc Only allow whitelisted values.
  -spec whitelisted(proplists:proplist()) -> proplists:proplist().
  whitelisted(Arguments) ->
      lists:filter(
        fun({Key, _Value}) ->
                lists:member(Key, whitelist())
        end,
        Arguments
       ).
  
  %% @doc Get whitelisted search arguments.
  -spec whitelist() -> [atom()].
  whitelist() ->
      [cat, cat_exclude, cat_promote_recent, content_group, filter, hasobject, hassubject, text, sort, has_geo].
  
  %% @doc Combine separate facets (date_start_min, date_start_max) into one property
  %% (date_start.min, date_start.max).
  
negative.code:     {[{"application/json", to_json}], Req, State}.
  
  to_json(Req, State) ->
      Context  = z_context:new(Req, ?MODULE),
      {Type, Offset, Limit} = params(Req),
      Data =
          case State#state.mode of
              coordinates ->
                  %% We're only interested in the geolocation and id field, also it doesn't make sense
                  %% to retrieve results without coordinates
                  Query = [{source, [<<"geolocation">>]}, {has_geo, <<"true">>} | arguments(Req)],
                  Result = z_search:search({Type, Query}, {Offset + 1, Limit}, Context),
                  #{ result => [coordinates(R) || R <- Result#search_result.result]
                   , total => Result#search_result.total
                   };
              _ ->
                  Result = z_search:search({Type, arguments(Req)}, {Offset + 1, Limit}, Context),
                  #{ result => [ search_result(R, Context) || R <- Result#search_result.result
                                                                  , is_visible(R, Context)
                               ]
                   , total => Result#search_result.total
                   , facets => facets(Result#search_result.facets)
                   }
          end,
      {jsx:decode(Data), Req, State}.
  
  %%%-----------------------------------------------------------------------------
  %%% Internal functions
  %%%-----------------------------------------------------------------------------
  
  params(Req) ->
      RequestArgs = wrq:req_qs(Req),
      Type = list_to_atom(proplists:get_value("type", RequestArgs, "ginger_search")),
      Offset = list_to_integer(proplists:get_value("offset", RequestArgs, "0")),
      Limit = list_to_integer(proplists:get_value("limit", RequestArgs, "1000")),
      {Type, Offset, Limit}.
  
  %% @doc Process and filter request arguments.
  -spec arguments(wrq:rd()) -> proplists:proplist().
  arguments(Req) ->
      RequestArgs = wrq:req_qs(Req),
      Arguments = [argument({list_to_existing_atom(Key), Value}) || {Key, Value} <- RequestArgs],
      [{Key, Value} || {Key, Value} <- Arguments, lists:member(Key, whitelist())].
  
  %% @doc Pre-process request argument if needed.
  -spec argument({atom(), list() | binary()}) -> {atom(), list() | binary()}.
  