private Query getQuery(Function inner, Context context) {
   RefLiteralPair innerPair = new RefLiteralPair(inner);
   if (!innerPair.isValid()) {
     return null;
   }
   GeoPointFieldMapper mapper =
       getGeoPointFieldMapper(
           innerPair.reference().info().ident().columnIdent().fqn(), context.mapperService);
   Shape shape = (Shape) innerPair.input().value();
   Geometry geometry = JtsSpatialContext.GEO.getGeometryFrom(shape);
   IndexGeoPointFieldData fieldData = context.fieldDataService.getForField(mapper);
   Filter filter;
   if (geometry.isRectangle()) {
     Rectangle boundingBox = shape.getBoundingBox();
     filter =
         new InMemoryGeoBoundingBoxFilter(
             new GeoPoint(boundingBox.getMaxY(), boundingBox.getMinX()),
             new GeoPoint(boundingBox.getMinY(), boundingBox.getMaxX()),
             fieldData);
   } else {
     Coordinate[] coordinates = geometry.getCoordinates();
     GeoPoint[] points = new GeoPoint[coordinates.length];
     for (int i = 0; i < coordinates.length; i++) {
       Coordinate coordinate = coordinates[i];
       points[i] = new GeoPoint(coordinate.y, coordinate.x);
     }
     filter = new GeoPolygonFilter(fieldData, points);
   }
   return new FilteredQuery(
       Queries.newMatchAllQuery(), context.indexCache.filter().cache(filter));
 }
      /**
       * @param parent the outer function. E.g. in the case of
       *     <pre>where distance(p1, POINT (10 20)) > 20</pre>
       *     this would be
       *     <pre>gt( \<inner function\>,  20)</pre>
       *
       * @param inner has to be the distance function
       */
      @Override
      public Query apply(Function parent, Function inner, Context context) {
        assert inner.info().ident().name().equals(DistanceFunction.NAME);

        RefLiteralPair distanceRefLiteral = new RefLiteralPair(inner);
        if (!distanceRefLiteral.isValid()) {
          // can't use distance filter without literal, fallback to genericFunction
          return null;
        }
        FunctionLiteralPair functionLiteralPair = new FunctionLiteralPair(parent);
        if (!functionLiteralPair.isValid()) {
          // must be something like eq(distance(..), non-literal) - fallback to genericFunction
          return null;
        }
        Double distance = DataTypes.DOUBLE.value(functionLiteralPair.input().value());

        String fieldName = distanceRefLiteral.reference().info().ident().columnIdent().fqn();
        FieldMapper mapper = getGeoPointFieldMapper(fieldName, context.mapperService);
        GeoPointFieldMapper geoMapper = ((GeoPointFieldMapper) mapper);
        IndexGeoPointFieldData fieldData = context.fieldDataService.getForField(mapper);

        Input geoPointInput = distanceRefLiteral.input();
        Double[] pointValue = (Double[]) geoPointInput.value();
        double lat = pointValue[1];
        double lon = pointValue[0];

        String parentName = functionLiteralPair.functionName();

        Double from = null;
        Double to = null;
        boolean includeLower = false;
        boolean includeUpper = false;

        switch (parentName) {
          case EqOperator.NAME:
            includeLower = true;
            includeUpper = true;
            from = distance;
            to = distance;
            break;
          case LteOperator.NAME:
            includeUpper = true;
            to = distance;
            break;
          case LtOperator.NAME:
            to = distance;
            break;
          case GteOperator.NAME:
            from = distance;
            includeLower = true;
            break;
          case GtOperator.NAME:
            from = distance;
            break;
          default:
            // invalid operator? give up
            return null;
        }
        GeoPoint geoPoint = new GeoPoint(lat, lon);
        Filter filter =
            new GeoDistanceRangeFilter(
                geoPoint,
                from,
                to,
                includeLower,
                includeUpper,
                GEO_DISTANCE,
                geoMapper,
                fieldData,
                optimizeBox);
        return new FilteredQuery(
            Queries.newMatchAllQuery(), context.indexCache.filter().cache(filter));
      }