فهرست منبع

fix #381 : geo search support and minor fixes

Shinsuke Sugaya 9 سال پیش
والد
کامیت
8317cf22e6

+ 4 - 16
src/main/java/org/codelibs/fess/api/gsa/GsaApiManager.java

@@ -51,7 +51,6 @@ import org.codelibs.fess.entity.SearchRenderData;
 import org.codelibs.fess.entity.SearchRequestParams;
 import org.codelibs.fess.mylasta.direction.FessConfig;
 import org.codelibs.fess.util.ComponentUtil;
-import org.codelibs.fess.util.StreamUtil;
 import org.dbflute.optional.OptionalThing;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -381,14 +380,6 @@ public class GsaApiManager extends BaseApiManager implements WebApiManager {
             this.fessConfig = fessConfig;
         }
 
-        private String[] simplifyArray(String[] values) {
-            return StreamUtil.of(values).filter(q -> StringUtil.isNotBlank(q)).distinct().toArray(n -> new String[n]);
-        }
-
-        private String[] getParamValueArray(String param) {
-            return simplifyArray(request.getParameterValues(param));
-        }
-
         @Override
         public String getQuery() {
             return request.getParameter("q");
@@ -414,7 +405,7 @@ public class GsaApiManager extends BaseApiManager implements WebApiManager {
                 extraParams.put("additional", (String[]) additional.toArray(new String[additional.size()]));
             }
             */
-            return getParamValueArray("ex_q");
+            return getParamValueArray(request, "ex_q");
         }
 
         @Override
@@ -432,20 +423,17 @@ public class GsaApiManager extends BaseApiManager implements WebApiManager {
 
         @Override
         public String[] getLanguages() {
-            return getParamValueArray("lang");
+            return getParamValueArray(request, "lang");
         }
 
         @Override
         public GeoInfo getGeoInfo() {
-            return null;
+            return createGeoInfo(request);
         }
 
         @Override
         public FacetInfo getFacetInfo() {
-            FacetInfo facetInfo = new FacetInfo();
-            facetInfo.field = getParamValueArray("facet.field");
-            facetInfo.query = getParamValueArray("facet.query");
-            return facetInfo;
+            return createFacetInfo(request);
         }
 
         @Override

+ 4 - 20
src/main/java/org/codelibs/fess/api/json/JsonApiManager.java

@@ -54,7 +54,6 @@ import org.codelibs.fess.util.ComponentUtil;
 import org.codelibs.fess.util.DocumentUtil;
 import org.codelibs.fess.util.FacetResponse;
 import org.codelibs.fess.util.FacetResponse.Field;
-import org.codelibs.fess.util.StreamUtil;
 import org.dbflute.optional.OptionalThing;
 import org.elasticsearch.script.Script;
 import org.lastaflute.web.util.LaRequestUtil;
@@ -688,14 +687,6 @@ public class JsonApiManager extends BaseApiManager {
             this.fessConfig = fessConfig;
         }
 
-        private String[] simplifyArray(String[] values) {
-            return StreamUtil.of(values).filter(q -> StringUtil.isNotBlank(q)).distinct().toArray(n -> new String[n]);
-        }
-
-        private String[] getParamValueArray(String param) {
-            return simplifyArray(request.getParameterValues(param));
-        }
-
         @Override
         public String getQuery() {
             return request.getParameter("q");
@@ -703,7 +694,7 @@ public class JsonApiManager extends BaseApiManager {
 
         @Override
         public String[] getExtraQueries() {
-            return getParamValueArray("ex_q");
+            return getParamValueArray(request, "ex_q");
         }
 
         @Override
@@ -721,24 +712,17 @@ public class JsonApiManager extends BaseApiManager {
 
         @Override
         public String[] getLanguages() {
-            return getParamValueArray("lang");
+            return getParamValueArray(request, "lang");
         }
 
         @Override
         public GeoInfo getGeoInfo() {
-            GeoInfo geoInfo = new GeoInfo();
-            geoInfo.latitude = request.getParameter("geo.latitude");
-            geoInfo.longitude = request.getParameter("geo.longitude");
-            geoInfo.distance = request.getParameter("geo.distance");
-            return geoInfo;
+            return createGeoInfo(request);
         }
 
         @Override
         public FacetInfo getFacetInfo() {
-            FacetInfo facetInfo = new FacetInfo();
-            facetInfo.field = getParamValueArray("facet.field");
-            facetInfo.query = getParamValueArray("facet.query");
-            return facetInfo;
+            return createFacetInfo(request);
         }
 
         @Override

+ 2 - 10
src/main/java/org/codelibs/fess/app/web/admin/searchlist/ListForm.java

@@ -75,14 +75,6 @@ public class ListForm implements SearchRequestParams, Serializable {
         return fields;
     }
 
-    // geo
-
-    public GeoInfo geo;
-
-    // facet
-
-    public FacetInfo facet;
-
     @Override
     public int getStartPosition() {
         if (start == null) {
@@ -110,12 +102,12 @@ public class ListForm implements SearchRequestParams, Serializable {
 
     @Override
     public GeoInfo getGeoInfo() {
-        return geo;
+        return null;
     }
 
     @Override
     public FacetInfo getFacetInfo() {
-        return facet;
+        return null;
     }
 
     @Override

+ 0 - 7
src/main/java/org/codelibs/fess/app/web/base/FessSearchAction.java

@@ -134,13 +134,6 @@ public abstract class FessSearchAction extends FessBaseAction {
     }
 
     protected void buildFormParams(final SearchForm form) {
-        if (form.facet == null) {
-            form.facet = queryHelper.getDefaultFacetInfo();
-        }
-
-        if (form.geo == null) {
-            form.geo = queryHelper.getDefaultGeoInfo();
-        }
 
         // label
         final List<Map<String, String>> labelTypeItems = labelTypeHelper.getLabelTypeItemList();

+ 6 - 12
src/main/java/org/codelibs/fess/app/web/base/SearchForm.java

@@ -57,16 +57,6 @@ public class SearchForm implements SearchRequestParams, Serializable {
     @ValidateTypeFailure
     public Integer pn;
 
-    // response redirect
-
-    // geo
-
-    public GeoInfo geo;
-
-    // facet
-
-    public FacetInfo facet;
-
     // advance
 
     @Override
@@ -117,12 +107,16 @@ public class SearchForm implements SearchRequestParams, Serializable {
 
     @Override
     public GeoInfo getGeoInfo() {
-        return geo;
+        GeoInfo geoInfo = createGeoInfo(LaRequestUtil.getRequest());
+        if (geoInfo != null) {
+            return geoInfo;
+        }
+        return ComponentUtil.getQueryHelper().getDefaultGeoInfo();
     }
 
     @Override
     public FacetInfo getFacetInfo() {
-        return facet;
+        return ComponentUtil.getQueryHelper().getDefaultFacetInfo();
     }
 
     @Override

+ 5 - 10
src/main/java/org/codelibs/fess/entity/FacetInfo.java

@@ -18,27 +18,22 @@ package org.codelibs.fess.entity;
 import java.util.Arrays;
 
 public class FacetInfo {
-    //@Maxbytelength(maxbytelength = 255)
     public String[] field;
 
-    //@Maxbytelength(maxbytelength = 255)
     public String[] query;
 
-    //@IntegerType
-    public String limit;
+    public Integer size;
 
-    //@IntegerType
-    public String minCount;
+    public Long minDocCount;
 
-    //@Maxbytelength(maxbytelength = 255)
     public String sort;
 
-    //@Maxbytelength(maxbytelength = 10)
     public String missing;
 
     @Override
     public String toString() {
-        return "FacetInfo [field=" + Arrays.toString(field) + ", q=" + Arrays.toString(query) + ", limit=" + limit + ", minCount="
-                + minCount + ", sort=" + sort + ", missing=" + missing + "]";
+        return "FacetInfo [field=" + Arrays.toString(field) + ", query=" + Arrays.toString(query) + ", size=" + size + ", minDocCount="
+                + minDocCount + ", sort=" + sort + ", missing=" + missing + "]";
     }
+
 }

+ 77 - 70
src/main/java/org/codelibs/fess/entity/GeoInfo.java

@@ -15,91 +15,98 @@
  */
 package org.codelibs.fess.entity;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
 import org.codelibs.core.lang.StringUtil;
-import org.elasticsearch.common.unit.DistanceUnit;
+import org.codelibs.fess.exception.InvalidQueryException;
+import org.codelibs.fess.mylasta.direction.FessConfig;
+import org.codelibs.fess.util.ComponentUtil;
+import org.codelibs.fess.util.StreamUtil;
+import org.elasticsearch.index.query.BoolQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
+import org.lastaflute.core.message.UserMessages;
 
 public class GeoInfo {
-    //@Mask(mask = "-?([0-9]+|[0-9]+\\.[0-9]+)")
-    //@Maxbytelength(maxbytelength = 20)
-    public String latitude;
-
-    //@Mask(mask = "-?([0-9]+|[0-9]+\\.[0-9]+)")
-    //@Maxbytelength(maxbytelength = 20)
-    public String longitude;
-
-    //@Mask(mask = "-?([0-9]+|[0-9]+\\.[0-9]+)")
-    //@Maxbytelength(maxbytelength = 20)
-    public String distance;
-
-    private boolean isInit = false;
 
     private QueryBuilder builder;
 
-    private void init() {
-        if (!isInit) {
-            isInit = true;
-
-            if (StringUtil.isBlank(latitude) || StringUtil.isBlank(longitude) || StringUtil.isBlank(distance)) {
-                clear();
-                return;
-            }
-
-            try {
-                final double dist = Double.parseDouble(distance);
-                double lat = Double.parseDouble(latitude);
-                double lon = Double.parseDouble(longitude);
-
-                if (dist <= 0) {
-                    clear();
-                    return;
-                }
-
-                if (lat > 90) {
-                    lat = 90;
-                } else if (lat < -90) {
-                    lat = -90;
-                }
-
-                if (lon > 180) {
-                    lon = lon % 360;
-                    if (lon > 180) {
-                        lon -= 360;
-                    }
-                } else if (lon < -180) {
-                    lon = lon % 360;
-                    if (lon < -180) {
-                        lon += 360;
-                    }
-                }
-
-                builder = QueryBuilders.geoDistanceQuery("geo_info").distance(dist, DistanceUnit.KILOMETERS).lat(lat).lon(lon);
-            } catch (final NumberFormatException e) {
-                clear();
+    public GeoInfo(HttpServletRequest request) {
+
+        final FessConfig fessConfig = ComponentUtil.getFessConfig();
+        final String[] geoFields = fessConfig.getQueryGeoFieldsAsArray();
+        final Map<String, List<QueryBuilder>> geoMap = new HashMap<>();
+
+        StreamUtil
+                .of(request.getParameterMap())
+                .filter(e -> e.getKey().startsWith("geo.") && e.getKey().endsWith(".point"))
+                .forEach(
+                        e -> {
+                            final String key = e.getKey();
+                            for (String geoField : geoFields) {
+                                if (key.startsWith("geo." + geoField + ".")) {
+                                    String distanceKey = key.replaceFirst(".point$", ".distance");
+                                    final String distance = request.getParameter(distanceKey);
+                                    if (StringUtil.isNotBlank(distance)) {
+                                        StreamUtil.of(e.getValue()).forEach(
+                                                pt -> {
+                                                    List<QueryBuilder> list = geoMap.get(geoField);
+                                                    if (list == null) {
+                                                        list = new ArrayList<>();
+                                                        geoMap.put(geoField, list);
+                                                    }
+                                                    String[] values = pt.split(",");
+                                                    if (values.length == 2) {
+                                                        try {
+                                                            double lat = Double.parseDouble(values[0]);
+                                                            double lon = Double.parseDouble(values[1]);
+                                                            list.add(QueryBuilders.geoDistanceQuery(geoField).distance(distance).lat(lat)
+                                                                    .lon(lon));
+                                                        } catch (Exception ex) {
+                                                            throw new InvalidQueryException(messages -> messages
+                                                                    .addErrorsInvalidQueryUnknown(UserMessages.GLOBAL_PROPERTY_KEY), ex
+                                                                    .getLocalizedMessage());
+                                                        }
+                                                    } else {
+                                                        throw new InvalidQueryException(messages -> messages
+                                                                .addErrorsInvalidQueryUnknown(UserMessages.GLOBAL_PROPERTY_KEY),
+                                                                "Invalid geo point: " + pt);
+                                                    }
+                                                });
+                                    }
+                                    break;
+                                }
+                            }
+                        });
+
+        QueryBuilder[] queryBuilders = geoMap.values().stream().map(list -> {
+            if (list.size() == 1) {
+                return list.get(0);
+            } else if (list.size() > 1) {
+                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
+                list.forEach(q -> boolQuery.should(q));
+                return boolQuery;
             }
+            return null;
+        }).filter(q -> q != null).toArray(n -> new QueryBuilder[n]);
+
+        if (queryBuilders.length == 1) {
+            builder = queryBuilders[0];
+        } else if (queryBuilders.length > 1) {
+            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
+            StreamUtil.of(queryBuilders).forEach(q -> boolQuery.must(q));
+            builder = boolQuery;
         }
-    }
-
-    private void clear() {
-        latitude = null;
-        longitude = null;
-        distance = null;
-        builder = null;
-    }
 
-    public boolean isAvailable() {
-        init();
-        return builder != null;
     }
 
     public QueryBuilder toQueryBuilder() {
-        init();
         return builder;
     }
 
-    @Override
-    public String toString() {
-        return "GeoInfo [latitude=" + latitude + ", longitude=" + longitude + ", distance=" + distance + ", isInit=" + isInit + "]";
-    }
 }

+ 44 - 0
src/main/java/org/codelibs/fess/entity/SearchRequestParams.java

@@ -18,6 +18,11 @@ package org.codelibs.fess.entity;
 import java.util.Locale;
 import java.util.Map;
 
+import javax.servlet.http.HttpServletRequest;
+
+import org.codelibs.core.lang.StringUtil;
+import org.codelibs.fess.util.StreamUtil;
+
 public interface SearchRequestParams {
 
     String getQuery();
@@ -44,4 +49,43 @@ public interface SearchRequestParams {
 
     Locale getLocale();
 
+    public default String[] simplifyArray(String[] values) {
+        return StreamUtil.of(values).filter(q -> StringUtil.isNotBlank(q)).distinct().toArray(n -> new String[n]);
+    }
+
+    public default String[] getParamValueArray(HttpServletRequest request, String param) {
+        return simplifyArray(request.getParameterValues(param));
+    }
+
+    public default FacetInfo createFacetInfo(HttpServletRequest request) {
+        String[] fields = getParamValueArray(request, "facet.field");
+        String[] queries = getParamValueArray(request, "facet.query");
+        if (fields.length == 0 && queries.length == 0) {
+            return null;
+        }
+        FacetInfo facetInfo = new FacetInfo();
+        facetInfo.field = fields;
+        facetInfo.query = queries;
+        String sizeStr = request.getParameter("facet.size");
+        if (StringUtil.isNotBlank(sizeStr)) {
+            facetInfo.size = Integer.parseInt(sizeStr);
+        }
+        String minDocCountStr = request.getParameter("facet.minDocCount");
+        if (StringUtil.isNotBlank(minDocCountStr)) {
+            facetInfo.minDocCount = Long.parseLong(minDocCountStr);
+        }
+        String sort = request.getParameter("facet.sort");
+        if (StringUtil.isNotBlank(sort)) {
+            facetInfo.sort = sort;
+        }
+        String missing = request.getParameter("facet.missing");
+        if (StringUtil.isNotBlank(missing)) {
+            facetInfo.missing = missing;
+        }
+        return facetInfo;
+    }
+
+    public default GeoInfo createGeoInfo(HttpServletRequest request) {
+        return new GeoInfo(request);
+    }
 }

+ 5 - 5
src/main/java/org/codelibs/fess/es/client/FessEsClient.java

@@ -865,7 +865,7 @@ public class FessEsClient implements Client {
                     context.skipRoleQuery();
                 }
                 // geo
-                    if (geoInfo != null && geoInfo.isAvailable()) {
+                    if (geoInfo != null && geoInfo.toQueryBuilder() != null) {
                         context.addQuery(boolQuery -> {
                             boolQuery.filter(geoInfo.toQueryBuilder());
                         });
@@ -896,11 +896,11 @@ public class FessEsClient implements Client {
                         } else if ("count".equals(facetInfo.sort)) {
                             termsBuilder.order(Order.count(true));
                         }
-                        if (facetInfo.limit != null) {
-                            termsBuilder.size(Integer.parseInt(facetInfo.limit));
+                        if (facetInfo.size != null) {
+                            termsBuilder.size(facetInfo.size);
                         }
-                        if (facetInfo.minCount != null) {
-                            termsBuilder.minDocCount(Long.parseLong(facetInfo.minCount));
+                        if (facetInfo.minDocCount != null) {
+                            termsBuilder.minDocCount(facetInfo.minDocCount);
                         }
                         if (facetInfo.missing != null) {
                             termsBuilder.missing(facetInfo.missing);

+ 14 - 0
src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java

@@ -367,6 +367,9 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
     /** The key of the configuration. e.g. 1000 */
     String QUERY_MAX_LENGTH = "query.max.length";
 
+    /** The key of the configuration. e.g. location */
+    String QUERY_GEO_FIELDS = "query.geo.fields";
+
     /** The key of the configuration. e.g. true */
     String QUERY_REPLACE_TERM_WITH_PREFIX_QUERY = "query.replace.term.with.prefix.query";
 
@@ -1927,6 +1930,13 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
      */
     Integer getQueryMaxLengthAsInteger();
 
+    /**
+     * Get the value for the key 'query.geo.fields'. <br>
+     * The value is, e.g. location <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getQueryGeoFields();
+
     /**
      * Get the value for the key 'query.replace.term.with.prefix.query'. <br>
      * The value is, e.g. true <br>
@@ -4164,6 +4174,10 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
             return getAsInteger(FessConfig.QUERY_MAX_LENGTH);
         }
 
+        public String getQueryGeoFields() {
+            return get(FessConfig.QUERY_GEO_FIELDS);
+        }
+
         public String getQueryReplaceTermWithPrefixQuery() {
             return get(FessConfig.QUERY_REPLACE_TERM_WITH_PREFIX_QUERY);
         }

+ 29 - 27
src/main/java/org/codelibs/fess/mylasta/direction/FessProp.java

@@ -119,7 +119,7 @@ public interface FessProp {
                 map = Collections.emptyMap();
             } else {
                 final Set<String> keySet = new HashSet<>();
-                map = StreamUtil.of(value.split("\n")).filter(s -> StringUtil.isNotBlank(s)).map(s -> {
+                map = StreamUtil.of(value.split("\n")).filter(StringUtil::isNotBlank).map(s -> {
                     final String[] pair = s.split("=");
                     if (pair.length == 1) {
                         return new Pair<>(StringUtil.EMPTY, pair[0].trim());
@@ -149,7 +149,7 @@ public interface FessProp {
                         return e.getValue();
                     }
                     return null;
-                }).filter(s -> StringUtil.isNotBlank(s)).toArray(n -> new String[n]);
+                }).filter(StringUtil::isNotBlank).toArray(n -> new String[n]);
     }
 
     public default void setDefaultSortValue(final String value) {
@@ -170,7 +170,7 @@ public interface FessProp {
                 map = Collections.emptyMap();
             } else {
                 final Set<String> keySet = new HashSet<>();
-                map = StreamUtil.of(value.split("\n")).filter(s -> StringUtil.isNotBlank(s)).map(s -> {
+                map = StreamUtil.of(value.split("\n")).filter(StringUtil::isNotBlank).map(s -> {
                     final String[] pair = s.split("=");
                     if (pair.length == 1) {
                         return new Pair<>(StringUtil.EMPTY, pair[0].trim());
@@ -196,7 +196,7 @@ public interface FessProp {
                         return e.getValue();
                     }
                     return null;
-                }).filter(s -> StringUtil.isNotBlank(s)).toArray(n -> new String[n]);
+                }).filter(StringUtil::isNotBlank).toArray(n -> new String[n]);
     }
 
     public default void setDefaultLabelValue(final String value) {
@@ -556,7 +556,7 @@ public interface FessProp {
     String getSupportedLanguages();
 
     public default String[] getSupportedLanguagesAsArray() {
-        return StreamUtil.of(getSupportedLanguages().split(",")).filter(s -> StringUtil.isNotBlank(s)).toArray(n -> new String[n]);
+        return StreamUtil.of(getSupportedLanguages().split(",")).filter(StringUtil::isNotBlank).toArray(n -> new String[n]);
     }
 
     String getOnlineHelpSupportedLangs();
@@ -565,28 +565,25 @@ public interface FessProp {
         if (StringUtil.isBlank(getOnlineHelpSupportedLangs())) {
             return false;
         }
-        return StreamUtil.of(getOnlineHelpSupportedLangs().split(",")).filter(s -> StringUtil.isNotBlank(s)).anyMatch(s -> s.equals(lang));
+        return StreamUtil.of(getOnlineHelpSupportedLangs().split(",")).filter(StringUtil::isNotBlank).anyMatch(s -> s.equals(lang));
     }
 
     String getSupportedUploadedJsExtentions();
 
     public default String[] getSupportedUploadedJsExtentionsAsArray() {
-        return StreamUtil.of(getSupportedUploadedJsExtentions().split(",")).filter(s -> StringUtil.isNotBlank(s))
-                .toArray(n -> new String[n]);
+        return StreamUtil.of(getSupportedUploadedJsExtentions().split(",")).filter(StringUtil::isNotBlank).toArray(n -> new String[n]);
     }
 
     String getSupportedUploadedCssExtentions();
 
     public default String[] getSupportedUploadedCssExtentionsAsArray() {
-        return StreamUtil.of(getSupportedUploadedCssExtentions().split(",")).filter(s -> StringUtil.isNotBlank(s))
-                .toArray(n -> new String[n]);
+        return StreamUtil.of(getSupportedUploadedCssExtentions().split(",")).filter(StringUtil::isNotBlank).toArray(n -> new String[n]);
     }
 
     String getSupportedUploadedMediaExtentions();
 
     public default String[] getSupportedUploadedMediaExtentionsAsArray() {
-        return StreamUtil.of(getSupportedUploadedMediaExtentions().split(",")).filter(s -> StringUtil.isNotBlank(s))
-                .toArray(n -> new String[n]);
+        return StreamUtil.of(getSupportedUploadedMediaExtentions().split(",")).filter(StringUtil::isNotBlank).toArray(n -> new String[n]);
     }
 
     String getJobTemplateTitleWeb();
@@ -630,7 +627,7 @@ public interface FessProp {
         Pattern[] patterns = (Pattern[]) propMap.get(CRAWLER_METADATA_CONTENT_EXCLUDES);
         if (patterns == null) {
             patterns =
-                    StreamUtil.of(getCrawlerMetadataContentExcludes().split(",")).filter(v -> StringUtil.isNotBlank(v))
+                    StreamUtil.of(getCrawlerMetadataContentExcludes().split(",")).filter(StringUtil::isNotBlank)
                             .map(v -> Pattern.compile(v)).toArray(n -> new Pattern[n]);
             propMap.put(CRAWLER_METADATA_CONTENT_EXCLUDES, patterns);
         }
@@ -643,7 +640,7 @@ public interface FessProp {
         @SuppressWarnings("unchecked")
         Map<String, Pair<String, String>> params = (Map<String, Pair<String, String>>) propMap.get(CRAWLER_METADATA_NAME_MAPPING);
         if (params == null) {
-            params = StreamUtil.of(getCrawlerMetadataNameMapping().split("\n")).filter(v -> StringUtil.isNotBlank(v)).map(v -> {
+            params = StreamUtil.of(getCrawlerMetadataNameMapping().split("\n")).filter(StringUtil::isNotBlank).map(v -> {
                 final String[] values = v.split("=");
                 if (values.length == 2) {
                     final String[] subValues = values[1].split(":");
@@ -663,19 +660,19 @@ public interface FessProp {
     String getSuggestPopularWordFields();
 
     public default String[] getSuggestPopularWordFieldsAsArray() {
-        return StreamUtil.of(getSuggestPopularWordFields().split("\n")).filter(s -> StringUtil.isNotBlank(s)).toArray(n -> new String[n]);
+        return StreamUtil.of(getSuggestPopularWordFields().split("\n")).filter(StringUtil::isNotBlank).toArray(n -> new String[n]);
     }
 
     String getSuggestPopularWordTags();
 
     public default String[] getSuggestPopularWordTagsAsArray() {
-        return StreamUtil.of(getSuggestPopularWordTags().split("\n")).filter(s -> StringUtil.isNotBlank(s)).toArray(n -> new String[n]);
+        return StreamUtil.of(getSuggestPopularWordTags().split("\n")).filter(StringUtil::isNotBlank).toArray(n -> new String[n]);
     }
 
     String getSuggestPopularWordExcludes();
 
     public default String[] getSuggestPopularWordExcludesAsArray() {
-        return StreamUtil.of(getSuggestPopularWordExcludes().split("\n")).filter(s -> StringUtil.isNotBlank(s)).toArray(n -> new String[n]);
+        return StreamUtil.of(getSuggestPopularWordExcludes().split("\n")).filter(StringUtil::isNotBlank).toArray(n -> new String[n]);
     }
 
     String getQueryReplaceTermWithPrefixQuery();
@@ -710,7 +707,7 @@ public interface FessProp {
         @SuppressWarnings("unchecked")
         Map<String, String> params = (Map<String, String>) propMap.get(QUERY_LANGUAGE_MAPPING);
         if (params == null) {
-            params = StreamUtil.of(getQueryLanguageMapping().split("\n")).filter(v -> StringUtil.isNotBlank(v)).map(v -> {
+            params = StreamUtil.of(getQueryLanguageMapping().split("\n")).filter(StringUtil::isNotBlank).map(v -> {
                 final String[] values = v.split("=");
                 if (values.length == 2) {
                     return new Pair<String, String>(values[0], values[1]);
@@ -742,15 +739,14 @@ public interface FessProp {
     String getSupportedUploadedFiles();
 
     public default boolean isSupportedUploadedFile(final String name) {
-        return StreamUtil.of(getSuggestPopularWordExcludes().split(",")).filter(s -> StringUtil.isNotBlank(s))
-                .anyMatch(s -> s.equals(name));
+        return StreamUtil.of(getSuggestPopularWordExcludes().split(",")).filter(StringUtil::isNotBlank).anyMatch(s -> s.equals(name));
     }
 
     String getLdapAdminUserObjectClasses();
 
     public default Attribute getLdapAdminUserObjectClassAttribute() {
         final Attribute oc = new BasicAttribute("objectClass");
-        StreamUtil.of(getLdapAdminUserObjectClasses().split(",")).filter(s -> StringUtil.isNotBlank(s)).forEach(s -> oc.add(s.trim()));
+        StreamUtil.of(getLdapAdminUserObjectClasses().split(",")).filter(StringUtil::isNotBlank).forEach(s -> oc.add(s.trim()));
         return oc;
     }
 
@@ -775,7 +771,7 @@ public interface FessProp {
 
     public default Attribute getLdapAdminRoleObjectClassAttribute() {
         final Attribute oc = new BasicAttribute("objectClass");
-        StreamUtil.of(getLdapAdminRoleObjectClasses().split(",")).filter(s -> StringUtil.isNotBlank(s)).forEach(s -> oc.add(s.trim()));
+        StreamUtil.of(getLdapAdminRoleObjectClasses().split(",")).filter(StringUtil::isNotBlank).forEach(s -> oc.add(s.trim()));
         return oc;
     }
 
@@ -800,7 +796,7 @@ public interface FessProp {
 
     public default Attribute getLdapAdminGroupObjectClassAttribute() {
         final Attribute oc = new BasicAttribute("objectClass");
-        StreamUtil.of(getLdapAdminGroupObjectClasses().split(",")).filter(s -> StringUtil.isNotBlank(s)).forEach(s -> oc.add(s.trim()));
+        StreamUtil.of(getLdapAdminGroupObjectClasses().split(",")).filter(StringUtil::isNotBlank).forEach(s -> oc.add(s.trim()));
         return oc;
     }
 
@@ -839,7 +835,7 @@ public interface FessProp {
     String getCrawlerWebProtocols();
 
     public default String[] getCrawlerWebProtocolsAsArray() {
-        return StreamUtil.of(getCrawlerWebProtocols().split(",")).filter(s -> StringUtil.isNotBlank(s)).map(s -> s.trim() + ":")
+        return StreamUtil.of(getCrawlerWebProtocols().split(",")).filter(StringUtil::isNotBlank).map(s -> s.trim() + ":")
                 .toArray(n -> new String[n]);
     }
 
@@ -850,7 +846,7 @@ public interface FessProp {
     String getCrawlerFileProtocols();
 
     public default String[] getCrawlerFileProtocolsAsArray() {
-        return StreamUtil.of(getCrawlerFileProtocols().split(",")).filter(s -> StringUtil.isNotBlank(s)).map(s -> s.trim() + ":")
+        return StreamUtil.of(getCrawlerFileProtocols().split(",")).filter(StringUtil::isNotBlank).map(s -> s.trim() + ":")
                 .toArray(n -> new String[n]);
     }
 
@@ -882,12 +878,18 @@ public interface FessProp {
     public default String[] getSearchDefaultPermissionsAsArray() {
         final PermissionHelper permissionHelper = ComponentUtil.getPermissionHelper();
         return StreamUtil.of(getRoleSearchDefaultPermissions().split(",")).map(p -> permissionHelper.encode(p))
-                .filter(s -> StringUtil.isNotBlank(s)).distinct().toArray(n -> new String[n]);
+                .filter(StringUtil::isNotBlank).distinct().toArray(n -> new String[n]);
     }
 
     public default String getSearchDefaultDisplayPermission() {
-        return StreamUtil.of(getRoleSearchDefaultPermissions().split(",")).filter(s -> StringUtil.isNotBlank(s)).distinct()
+        return StreamUtil.of(getRoleSearchDefaultPermissions().split(",")).filter(StringUtil::isNotBlank).distinct()
                 .collect(Collectors.joining("\n"));
     }
 
+    String getQueryGeoFields();
+
+    public default String[] getQueryGeoFieldsAsArray() {
+        return StreamUtil.of(getQueryGeoFields().split(",")).map(s -> s.trim()).filter(StringUtil::isNotBlank).toArray(n -> new String[n]);
+    }
+
 }

+ 1 - 1
src/main/resources/app.xml

@@ -36,7 +36,7 @@
 	<component name="queryHelper" class="org.codelibs.fess.helper.QueryHelper">
 		<property name="defaultFacetInfo">
 			<component class="org.codelibs.fess.entity.FacetInfo">
-				<property name="minCount">1</property>
+				<property name="minDocCount">1</property>
 				<property name="field">["label"]</property>
 				<property name="query">[
 					"timestamp:[now/d-1d TO *]",

+ 1 - 0
src/main/resources/fess_config.properties

@@ -179,6 +179,7 @@ index.indices.timeout=1m
 
 # query
 query.max.length=1000
+query.geo.fields=location
 query.replace.term.with.prefix.query=true
 query.default.languages=
 query.language.mapping=\

+ 22 - 100
src/test/java/org/codelibs/fess/entity/GeoInfoTest.java

@@ -16,119 +16,41 @@
 package org.codelibs.fess.entity;
 
 import org.codelibs.fess.unit.UnitFessTestCase;
+import org.dbflute.utflute.mocklet.MockletHttpServletRequest;
 
 public class GeoInfoTest extends UnitFessTestCase {
 
-    public void test_0_0_10() {
-        final String latitude = "0";
-        final String lonitude = "0";
-        final String distance = "10";
+    public void test_34_150_10() {
+        MockletHttpServletRequest request = getMockRequest();
+        request.setParameter("geo.location.point", "34,150");
+        request.setParameter("geo.location.distance", "10km");
 
-        final GeoInfo geoInfo = create(latitude, lonitude, distance);
-        assertTrue(geoInfo.isAvailable());
-        String result = "{\"geo_distance\":{\"geo_info\":[0.0,0.0],\"distance\":\"10.0km\"}}";
+        final GeoInfo geoInfo = new GeoInfo(request);
+        String result = "{\"geo_distance\":{\"location\":[150.0,34.0],\"distance\":\"10km\"}}";
         assertEquals(result, geoInfo.toQueryBuilder().toString().replaceAll("[ \n]", ""));
     }
 
-    public void test_90_180_10() {
-        final String latitude = "90";
-        final String lonitude = "180";
-        final String distance = "10";
+    public void test_34_150_10_x() {
+        MockletHttpServletRequest request = getMockRequest();
+        request.setParameter("geo.location.x.point", "34,150");
+        request.setParameter("geo.location.x.distance", "10km");
 
-        final GeoInfo geoInfo = create(latitude, lonitude, distance);
-        assertTrue(geoInfo.isAvailable());
-        String result = "{\"geo_distance\":{\"geo_info\":[180.0,90.0],\"distance\":\"10.0km\"}}";
+        final GeoInfo geoInfo = new GeoInfo(request);
+        String result = "{\"geo_distance\":{\"location\":[150.0,34.0],\"distance\":\"10km\"}}";
         assertEquals(result, geoInfo.toQueryBuilder().toString().replaceAll("[ \n]", ""));
     }
 
-    public void test_91_181_10() {
-        final String latitude = "91";
-        final String lonitude = "181";
-        final String distance = "10";
+    public void test_34_150_10_2() {
+        MockletHttpServletRequest request = getMockRequest();
+        request.setParameter("geo.location.1.point", "34,150");
+        request.setParameter("geo.location.1.distance", "10km");
+        request.setParameter("geo.location.2.point", "35,151");
+        request.setParameter("geo.location.2.distance", "1km");
 
-        final GeoInfo geoInfo = create(latitude, lonitude, distance);
-        assertTrue(geoInfo.isAvailable());
-        String result = "{\"geo_distance\":{\"geo_info\":[-179.0,90.0],\"distance\":\"10.0km\"}}";
+        final GeoInfo geoInfo = new GeoInfo(request);
+        String result =
+                "{\"bool\":{\"should\":[{\"geo_distance\":{\"location\":[151.0,35.0],\"distance\":\"1km\"}},{\"geo_distance\":{\"location\":[150.0,34.0],\"distance\":\"10km\"}}]}}";
         assertEquals(result, geoInfo.toQueryBuilder().toString().replaceAll("[ \n]", ""));
     }
 
-    public void test_91_361_10() {
-        final String latitude = "91";
-        final String lonitude = "361";
-        final String distance = "100";
-
-        final GeoInfo geoInfo = create(latitude, lonitude, distance);
-        assertTrue(geoInfo.isAvailable());
-        String result = "{\"geo_distance\":{\"geo_info\":[1.0,90.0],\"distance\":\"100.0km\"}}";
-        assertEquals(result, geoInfo.toQueryBuilder().toString().replaceAll("[ \n]", ""));
-    }
-
-    public void test__90__180_10() {
-        final String latitude = "-90";
-        final String lonitude = "-180";
-        final String distance = "10";
-
-        final GeoInfo geoInfo = create(latitude, lonitude, distance);
-        assertTrue(geoInfo.isAvailable());
-        String result = "{\"geo_distance\":{\"geo_info\":[-180.0,-90.0],\"distance\":\"10.0km\"}}";
-        assertEquals(result, geoInfo.toQueryBuilder().toString().replaceAll("[ \n]", ""));
-    }
-
-    public void test__91__181_10() {
-        final String latitude = "-91";
-        final String lonitude = "-181";
-        final String distance = "10";
-
-        final GeoInfo geoInfo = create(latitude, lonitude, distance);
-        assertTrue(geoInfo.isAvailable());
-        String result = "{\"geo_distance\":{\"geo_info\":[179.0,-90.0],\"distance\":\"10.0km\"}}";
-        assertEquals(result, geoInfo.toQueryBuilder().toString().replaceAll("[ \n]", ""));
-    }
-
-    public void test__91__361_10() {
-        final String latitude = "-91";
-        final String lonitude = "-361";
-        final String distance = "100";
-
-        final GeoInfo geoInfo = create(latitude, lonitude, distance);
-        assertTrue(geoInfo.isAvailable());
-        String result = "{\"geo_distance\":{\"geo_info\":[-1.0,-90.0],\"distance\":\"100.0km\"}}";
-        assertEquals(result, geoInfo.toQueryBuilder().toString().replaceAll("[ \n]", ""));
-    }
-
-    public void test_0_0_0() {
-        final String latitude = "0";
-        final String lonitude = "0";
-        final String distance = "0";
-
-        final GeoInfo geoInfo = create(latitude, lonitude, distance);
-        assertFalse(geoInfo.isAvailable());
-    }
-
-    public void test_x_0_0() {
-        final String latitude = "x";
-        final String lonitude = "0";
-        final String distance = "10";
-
-        final GeoInfo geoInfo = create(latitude, lonitude, distance);
-        assertFalse(geoInfo.isAvailable());
-    }
-
-    public void test_0_x_0() {
-        final String latitude = "0";
-        final String lonitude = "x";
-        final String distance = "10";
-
-        final GeoInfo geoInfo = create(latitude, lonitude, distance);
-        assertFalse(geoInfo.isAvailable());
-    }
-
-    private GeoInfo create(final String latitude, final String longitude, final String distance) {
-        final GeoInfo geoInfo = new GeoInfo();
-        geoInfo.latitude = latitude;
-        geoInfo.longitude = longitude;
-        geoInfo.distance = distance;
-        return geoInfo;
-    }
-
 }