anchor.diff:  -module(ginger_search).
   
  --export([search_query/2, merge_ginger_args/1]).
  +-export([
  +    search_query/2,
  +    get_unfindable_categories/1,
  +    merge_ginger_args/2
  +]).
   
   -include_lib("zotonic.hrl").
  +
  +-define(GINGER_SEARCH_PIVOT, ginger_search).
  +
  +%% @doc Supports all the usual query model arguments, adds default excludes.
  +search_query(#search_query{search={ginger_search, Args}}, Context) ->
  +    QueryArgs = merge_ginger_args(Args, Context),
  +    search_query:search(QueryArgs, Context).
  +
  +%% @doc Get categories marked unfindable that must be excluded from search results
  +-spec get_unfindable_categories(#context{}) -> list().
  +get_unfindable_categories(Context) ->
  +    Categories = z_search:query_(
  +        [
  +            {cat, category},
  +            {custompivot, ?GINGER_SEARCH_PIVOT},
  +            {filter, ["is_unfindable", true]}
  +        ],
  +        Context
  +    ),
  +    lists:map(
  +        fun(Category) ->
  +            m_rsc:p(Category, name, Context)
  +        end,
  +        Categories
  +    ).
   
   %% @doc Add property to proplist if not defined
   withdefault({Key, _} = Prop, Proplist) ->
  
positive.code:     DefaultArgs = [
          {is_findable, true},
          {is_published, true},
          {cat_exclude_defaults, true}
      ],
      MergedArgs1 = withdefault(DefaultArgs, MergedArgs),
  
      % Parse custom ginger_search arguments
      lists:flatmap(fun parse_argument/1, MergedArgs1).
      
  %% @doc Supports all the usual query model arguments, adds default excludes.
  search_query(#search_query{search={ginger_search, Args}}, Context) ->
      QueryArgs = merge_ginger_args(Args),
      search_query:search(QueryArgs, Context).
  
negative.code:             []
      end;
  
  parse_argument({cat_exclude_defaults, Val}, Context) ->
      parse_argument({cat_exclude_defaults, z_convert:to_bool(Val)}, Context);
  
  parse_argument({cat_exclude_unfindable, Val}, Context) ->
      case z_convert:to_bool(Val) of
          true ->
              [{cat_exclude, get_unfindable_categories(Context)}];
          false ->
              []
      end;
  
  % Filtering on undefined is supported from Zotonic 0.13.16
  parse_argument({has_geo, true}, _Context) ->
      [{filter, ["pivot_location_lat", ne, undefined]},
       {filter, ["pivot_location_lng", ne, undefined]}];
  
  parse_argument({has_geo, false}, _Context) ->
      [{filter, ["pivot_location_lat", eq, undefined]},
       {filter, ["pivot_location_lng", eq, undefined]}];
  
  parse_argument({has_geo, Val}, Context) ->
      parse_argument({has_geo, z_convert:to_bool(Val)}, Context);
  
  parse_argument({_, undefined}, _Context) ->
      [];
  
  parse_argument({Key, <<"undefined">>}, Context) ->
      parse_argument({Key, undefined}, Context);
  
  parse_argument(Arg, _Context) ->
      [Arg].
  
  %% @doc Process custom arguments and add defaults
  merge_ginger_args(Args, Context) ->
  
      % Always set these extra query arguments
      ExtraArgs = [
          {custompivot, ?GINGER_SEARCH_PIVOT}
      ],
      MergedArgs = lists:merge(ExtraArgs, Args),
  
  