blob: 959dd9af2a9c43fd1b91d7ade01c000ef77f62ab [file] [log] [blame]
% Licensed under the Apache License, Version 2.0 (the "License"); you may not
% use this file except in compliance with the License. You may obtain a copy of
% the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
% License for the specific language governing permissions and limitations under
% the License.
-module(mango_selector_geo).
-export([
make_geom/1
]).
-include_lib("couch/include/couch_db.hrl").
-include("mango.hrl").
make_geom(Selector) ->
% We extract the geospatial portion of the selector so that we can
% convert it into a geometry object for hastings. The rest of the
% selector will then be used to filter the returned docs.
twig:log(notice, "Selector ~p", [Selector]),
{GeomSelector, FilterSelector} = extract_geo_selector(Selector),
twig:log(notice, "GeomSelector ~p", [GeomSelector]),
Geom = convert(GeomSelector),
{Geom, FilterSelector}.
extract_geo_selector({[{<<"$and">>, Args}]}) ->
Pred = fun ({[{_Field, {[{Op, _}]}}]}) ->
lists:member(Op, geo_query_operators())
end,
{GeomOp, FilterOps} = lists:partition(Pred, Args),
% need to add in a check here to make sure there's only one geo
% query operator
GeomSelector = lists:nth(1, GeomOp),
case length(FilterOps) of
Len when Len > 2 ->
{GeomSelector, {[{<<"$and">>, FilterOps}]}};
Len when Len =:= 1 ->
% no need for an $and when only one operator left
{GeomSelector, lists:nth(1, FilterOps)}
end;
extract_geo_selector({[{<<"$geoWithin">>, Args}]}) ->
{[{<<"$geoWithin">>, Args}]};
extract_geo_selector({[{<<"$geoIntersect">>, Args}]}) ->
{[{<<"$geoIntersect">>, Args}]};
extract_geo_selector({[{<<"$geoNear">>, Args}]}) ->
{[{<<"$geoNear">>, Args}]};
extract_geo_selector({[{<<"$geoContains">>, Args}]}) ->
{[{<<"$geoContains">>, Args}]};
extract_geo_selector({[{Field, Cond}]}) ->
{{[{Field, extract_geo_selector(Cond)}]}, {[]}}.
geo_query_operators() ->
[<<"$geoWithin">>, <<"$geoIntersect">>, <<"$geoContains">>,
<<"$geoNear">>].
convert({[{<<"$geoWithin">>, {[{<<"$bbox">>, Coords}]}}]}) ->
{bbox, Coords};
convert({[{Field, Cond}]}) ->
convert(Cond).