Java tutorial
/* * Copyright 2013-2016 the original author or authors. * * 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. */ package com.github.vanroy.springdata.jest; import static org.springframework.data.elasticsearch.core.query.Criteria.*; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import org.elasticsearch.common.geo.GeoDistance; import org.elasticsearch.index.query.*; import org.springframework.data.elasticsearch.core.geo.GeoBox; import org.springframework.data.elasticsearch.core.geo.GeoPoint; import org.springframework.data.elasticsearch.core.query.Criteria; import org.springframework.data.geo.Box; import org.springframework.data.geo.Distance; import org.springframework.data.geo.Metrics; import org.springframework.data.geo.Point; import org.springframework.util.Assert; /** * CriteriaFilterProcessor * * @author Franck Marchand * @author Mohsin Husen * @author Artur Konczak * */ class CriteriaFilterProcessor { QueryBuilder createFilterFromCriteria(Criteria criteria) { List<QueryBuilder> fbList = new LinkedList<QueryBuilder>(); QueryBuilder filter = null; ListIterator<Criteria> chainIterator = criteria.getCriteriaChain().listIterator(); while (chainIterator.hasNext()) { QueryBuilder fb = null; Criteria chainedCriteria = chainIterator.next(); if (chainedCriteria.isOr()) { fb = QueryBuilders.boolQuery(); for (QueryBuilder f : createFilterFragmentForCriteria(chainedCriteria)) { ((BoolQueryBuilder) fb).should(f); } fbList.add(fb); } else if (chainedCriteria.isNegating()) { List<QueryBuilder> negationFilters = buildNegationFilter(criteria.getField().getName(), criteria.getFilterCriteriaEntries().iterator()); if (!negationFilters.isEmpty()) { fbList.addAll(negationFilters); } } else { fbList.addAll(createFilterFragmentForCriteria(chainedCriteria)); } } if (!fbList.isEmpty()) { if (fbList.size() == 1) { filter = fbList.get(0); } else { filter = QueryBuilders.boolQuery(); for (QueryBuilder f : fbList) { ((BoolQueryBuilder) filter).must(f); } } } return filter; } private List<QueryBuilder> createFilterFragmentForCriteria(Criteria chainedCriteria) { Iterator<CriteriaEntry> it = chainedCriteria.getFilterCriteriaEntries().iterator(); List<QueryBuilder> filterList = new LinkedList<QueryBuilder>(); String fieldName = chainedCriteria.getField().getName(); Assert.notNull(fieldName, "Unknown field"); QueryBuilder filter = null; while (it.hasNext()) { Criteria.CriteriaEntry entry = it.next(); filter = processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName); filterList.add(filter); } return filterList; } private QueryBuilder processCriteriaEntry(OperationKey key, Object value, String fieldName) { if (value == null) { return null; } QueryBuilder filter = null; switch (key) { case WITHIN: { GeoDistanceQueryBuilder geoDistanceQueryBuilder = QueryBuilders.geoDistanceQuery(fieldName); Assert.isTrue(value instanceof Object[], "Value of a geo distance filter should be an array of two values."); Object[] valArray = (Object[]) value; Assert.noNullElements(valArray, "Geo distance filter takes 2 not null elements array as parameter."); Assert.isTrue(valArray.length == 2, "Geo distance filter takes a 2-elements array as parameter."); Assert.isTrue( valArray[0] instanceof GeoPoint || valArray[0] instanceof String || valArray[0] instanceof Point, "First element of a geo distance filter must be a GeoPoint, a Point or a String"); Assert.isTrue(valArray[1] instanceof String || valArray[1] instanceof Distance, "Second element of a geo distance filter must be a String or a Distance"); StringBuilder dist = new StringBuilder(); if (valArray[1] instanceof Distance) { extractDistanceString((Distance) valArray[1], dist); } else { dist.append((String) valArray[1]); } if (valArray[0] instanceof GeoPoint) { GeoPoint loc = (GeoPoint) valArray[0]; geoDistanceQueryBuilder.lat(loc.getLat()).lon(loc.getLon()).distance(dist.toString()) .geoDistance(GeoDistance.PLANE); } else if (valArray[0] instanceof Point) { GeoPoint loc = GeoPoint.fromPoint((Point) valArray[0]); geoDistanceQueryBuilder.lat(loc.getLat()).lon(loc.getLon()).distance(dist.toString()) .geoDistance(GeoDistance.PLANE); } else { String loc = (String) valArray[0]; if (loc.contains(",")) { String c[] = loc.split(","); geoDistanceQueryBuilder.lat(Double.parseDouble(c[0])).lon(Double.parseDouble(c[1])) .distance(dist.toString()).geoDistance(GeoDistance.PLANE); } else { geoDistanceQueryBuilder.geohash(loc).distance(dist.toString()).geoDistance(GeoDistance.PLANE); } } filter = geoDistanceQueryBuilder; break; } case BBOX: { filter = QueryBuilders.geoBoundingBoxQuery(fieldName); Assert.isTrue(value instanceof Object[], "Value of a boundedBy filter should be an array of one or two values."); Object[] valArray = (Object[]) value; Assert.noNullElements(valArray, "Geo boundedBy filter takes a not null element array as parameter."); if (valArray.length == 1) { //GeoEnvelop oneParameterBBox((GeoBoundingBoxQueryBuilder) filter, valArray[0]); } else if (valArray.length == 2) { //2x GeoPoint //2x String twoParameterBBox((GeoBoundingBoxQueryBuilder) filter, valArray); } else { //error Assert.isTrue(false, "Geo distance filter takes a 1-elements array(GeoBox) or 2-elements array(GeoPoints or Strings(format lat,lon or geohash))."); } break; } } return filter; } /** * extract the distance string from a {@link org.springframework.data.geo.Distance} object. * * @param distance distance object to extract string from * @param sb StringBuilder to build the distance string */ private void extractDistanceString(Distance distance, StringBuilder sb) { // handle Distance object sb.append((int) distance.getValue()); Metrics metric = (Metrics) distance.getMetric(); switch (metric) { case KILOMETERS: sb.append("km"); break; case MILES: sb.append("mi"); break; } } private void oneParameterBBox(GeoBoundingBoxQueryBuilder filter, Object value) { Assert.isTrue(value instanceof GeoBox || value instanceof Box, "single-element of boundedBy filter must be type of GeoBox or Box"); GeoBox geoBBox; if (value instanceof Box) { Box sdbox = (Box) value; geoBBox = GeoBox.fromBox(sdbox); } else { geoBBox = (GeoBox) value; } filter.topLeft(geoBBox.getTopLeft().getLat(), geoBBox.getTopLeft().getLon()); filter.bottomRight(geoBBox.getBottomRight().getLat(), geoBBox.getBottomRight().getLon()); } private static boolean isType(Object[] array, Class clazz) { for (Object o : array) { if (!clazz.isInstance(o)) { return false; } } return true; } private void twoParameterBBox(GeoBoundingBoxQueryBuilder filter, Object[] values) { Assert.isTrue(isType(values, GeoPoint.class) || isType(values, String.class), " both elements of boundedBy filter must be type of GeoPoint or String(format lat,lon or geohash)"); if (values[0] instanceof GeoPoint) { GeoPoint topLeft = (GeoPoint) values[0]; GeoPoint bottomRight = (GeoPoint) values[1]; filter.topLeft(topLeft.getLat(), topLeft.getLon()); filter.bottomRight(bottomRight.getLat(), bottomRight.getLon()); } else { String topLeft = (String) values[0]; String bottomRight = (String) values[1]; filter.topLeft(topLeft); filter.bottomRight(bottomRight); } } private List<QueryBuilder> buildNegationFilter(String fieldName, Iterator<CriteriaEntry> it) { List<QueryBuilder> notFilterList = new LinkedList<QueryBuilder>(); while (it.hasNext()) { Criteria.CriteriaEntry criteriaEntry = it.next(); QueryBuilder notFilter = QueryBuilders.boolQuery() .mustNot(processCriteriaEntry(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName)); notFilterList.add(notFilter); } return notFilterList; } }