Skip to content

Commit

Permalink
Merge pull request #1565 from newmanw/mongodb-min-max-visitor
Browse files Browse the repository at this point in the history
[GEOT-5716] MongoFeatureSource to handle Min/Max Visitor
  • Loading branch information
tbarsballe committed Sep 15, 2017
2 parents 9975150 + 4d5cce9 commit 0941665
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 23 deletions.
7 changes: 0 additions & 7 deletions modules/unsupported/mongodb/pom.xml
Expand Up @@ -147,13 +147,6 @@
<!--systemPropertyVariables>
<embedmongo.port>${embedmongo.port}</embedmongo.port>
</systemPropertyVariables-->
<excludes>
<exclude>**/MongoSchemaDBStoreTest.java</exclude>
<exclude>**/MongoSchemaFileStoreTest.java</exclude>
<exclude>**/GeoJSONMongoDataStoreTest.java</exclude>
<exclude>**/GeoJSONMongoFeatureSourceTest.java</exclude>
<exclude>**/MongoTestUtil.java</exclude>
</excludes>
<forkCount>1</forkCount>
</configuration>
</plugin>
Expand Down
Expand Up @@ -33,9 +33,13 @@
import org.geotools.data.store.ContentEntry;
import org.geotools.data.store.ContentFeatureSource;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.feature.visitor.MaxVisitor;
import org.geotools.feature.visitor.MinVisitor;
import org.geotools.filter.SortByImpl;
import org.geotools.filter.visitor.PostPreProcessFilterSplittingVisitor;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.util.logging.Logging;
import org.opengis.feature.FeatureVisitor;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.BinaryComparisonOperator;
Expand Down Expand Up @@ -158,6 +162,62 @@ protected FeatureReader<SimpleFeatureType, SimpleFeature> getReaderInternal(Quer
}
return r;
}

@Override
protected boolean handleVisitor(Query query, FeatureVisitor visitor) throws IOException {
// Don't do the following shortcuts if we don't request all features, as it doesn't make sense.
if (query.getMaxFeatures() != Integer.MAX_VALUE) {
return false;
}

if (visitor instanceof MinVisitor) {
MinVisitor minVisitor = (MinVisitor) visitor;
List<Expression> expressions = minVisitor.getExpressions();
if (expressions.size() != 1 || !(expressions.get(0) instanceof PropertyName)) {
return false;
}

PropertyName propertyName = (PropertyName) expressions.get(0);
SortBy sortBy = new SortByImpl(propertyName, SortOrder.ASCENDING);

Query newQuery = new Query(query);
newQuery.setSortBy(new SortBy[] {sortBy});

// Sorting to get min only need to get one result
newQuery.setMaxFeatures(1);

FeatureReader<SimpleFeatureType, SimpleFeature> reader = getReader(newQuery);
if (reader.hasNext()) {
// Don't need to visit all features, retrieved the min value lets just tell the MinVisitor
minVisitor.setValue(propertyName.evaluate(reader.next()));
}
} else if (visitor instanceof MaxVisitor) {
MaxVisitor maxVisitor = (MaxVisitor) visitor;
List<Expression> expressions = maxVisitor.getExpressions();
if (expressions.size() != 1 || !(expressions.get(0) instanceof PropertyName)) {
return false;
}

PropertyName propertyName = (PropertyName) expressions.get(0);
SortBy sortBy = new SortByImpl(propertyName, SortOrder.DESCENDING);

Query newQuery = new Query(query);
newQuery.setSortBy(new SortBy[] {sortBy});

// Sorting to get max only need to get one result
newQuery.setMaxFeatures(1);

FeatureReader<SimpleFeatureType, SimpleFeature> reader = getReader(newQuery);
if (reader.hasNext()) {
// Don't need to visit all features, retrieved the min value lets just tell the MaxVisitor
maxVisitor.setValue(propertyName.evaluate(reader.next()));
}
} else {
return false;
}

return true;
}

@Override
protected boolean canOffset() {
Expand Down Expand Up @@ -237,7 +297,8 @@ DBCursor toCursor(Query q, List<Filter> postFilter, List<String> postFilterAttrs
BasicDBObject orderBy = new BasicDBObject();
for (SortBy sortBy : q.getSortBy()) {
String propName = sortBy.getPropertyName().getPropertyName();
orderBy.append(propName, sortBy.getSortOrder() == SortOrder.ASCENDING ? 1 : -1);
String property = mapper.getPropertyPath(propName);
orderBy.append(property, sortBy.getSortOrder() == SortOrder.ASCENDING ? 1 : -1);
}
c = c.sort(orderBy);
}
Expand Down
Expand Up @@ -25,6 +25,7 @@
import org.geotools.data.store.ContentEntry;
import org.geotools.data.store.ContentFeatureStore;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.feature.FeatureVisitor;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

Expand All @@ -49,6 +50,11 @@ public CollectionMapper getMapper() {
public void setMapper(CollectionMapper mapper) {
delegate.setMapper(mapper);
}

@Override
protected boolean handleVisitor(Query query, FeatureVisitor visitor) throws IOException {
return delegate.handleVisitor(query, visitor);
}

@Override
protected SimpleFeatureType buildFeatureType() throws IOException {
Expand Down
Expand Up @@ -257,7 +257,6 @@ public void testAndComparison() {
And and = ff.and(greaterThan, lessThan);
BasicDBObject obj = (BasicDBObject) and.accept(filterToMongo, null);
assertNotNull(obj);
System.out.println(obj.toJson());

BasicDBList andFilter = (BasicDBList) obj.get("$and");
assertNotNull(andFilter);
Expand Down
Expand Up @@ -68,13 +68,14 @@ public void testEqualToFilter() throws Exception {
Query q = new Query("ft1", f);

assertEquals(1, source.getCount(q));
assertEquals(new ReferencedEnvelope(2d,2d,2d,2d,DefaultGeographicCRS.WGS84), source.getBounds(q));
ReferencedEnvelope e = source.getBounds();
assertEquals(new ReferencedEnvelope(2d,0d,2d,0d,DefaultGeographicCRS.WGS84), source.getBounds(q));

SimpleFeatureCollection features = source.getFeatures(q);
SimpleFeatureIterator it = features.features();
try {
assertTrue(it.hasNext());
assertFeature(it.next(), 2);
assertFeature(it.next(), 0);
}
finally {
it.close();
Expand Down Expand Up @@ -151,14 +152,14 @@ public void testDateGreaterComparison() throws Exception {
SimpleFeatureSource source = dataStore.getFeatureSource("ft1");
Query q = new Query("ft1", gt);

assertEquals(1, source.getCount(q));
assertEquals(new ReferencedEnvelope(1d,1d,1d,1d,DefaultGeographicCRS.WGS84), source.getBounds(q));
assertEquals(2, source.getCount(q));
assertEquals(new ReferencedEnvelope(0d,2d,0d,2d,DefaultGeographicCRS.WGS84), source.getBounds(q));

SimpleFeatureCollection features = source.getFeatures(q);
SimpleFeatureIterator it = features.features();
try {
assertTrue(it.hasNext());
assertFeature(it.next(), 1);
assertFeature(it.next(), 0);
}
finally {
it.close();
Expand All @@ -170,12 +171,12 @@ public void testDateGreaterComparison() throws Exception {
ff.literal(MongoTestSetup.parseDate("2015-01-01T11:30:00.000Z")));
q = new Query("ft1", gt);

assertEquals(1, source.getCount(q));
assertEquals(new ReferencedEnvelope(1d,1d,1d,1d,DefaultGeographicCRS.WGS84), source.getBounds(q));
assertEquals(2, source.getCount(q));
assertEquals(new ReferencedEnvelope(0d,2d,0d,2d,DefaultGeographicCRS.WGS84), source.getBounds(q));
it = source.getFeatures(q).features();
try {
assertTrue(it.hasNext());
assertFeature(it.next(), 1);
assertFeature(it.next(), 0);
}
finally {
it.close();
Expand All @@ -184,7 +185,7 @@ public void testDateGreaterComparison() throws Exception {
// test no-match filter
gt = ff.greater(
ff.property("properties.dateProperty"),
ff.literal("2015-01-01T17:30:00.000Z"));
ff.literal("2015-01-01T22:30:00.000Z"));
q = new Query("ft1", gt);

// no feature should match
Expand All @@ -195,19 +196,19 @@ public void testDateLessComparison() throws Exception {
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
PropertyIsLessThan lt = ff.less(
ff.property("properties.dateProperty"),
ff.literal("2015-01-01T00:00:00.000Z"));
ff.literal("2015-01-01T16:00:00.000Z"));

SimpleFeatureSource source = dataStore.getFeatureSource("ft1");
Query q = new Query("ft1", lt);

assertEquals(1, source.getCount(q));
assertEquals(new ReferencedEnvelope(2d,2d,2d,2d,DefaultGeographicCRS.WGS84), source.getBounds(q));
assertEquals(new ReferencedEnvelope(0d,2d,0d,2d,DefaultGeographicCRS.WGS84), source.getBounds(q));

SimpleFeatureCollection features = source.getFeatures(q);
SimpleFeatureIterator it = features.features();
try {
assertTrue(it.hasNext());
assertFeature(it.next(), 2);
assertFeature(it.next(), 0);
}
finally {
it.close();
Expand All @@ -216,7 +217,7 @@ public void testDateLessComparison() throws Exception {
// test no-match filter
lt = ff.less(
ff.property("properties.dateProperty"),
ff.literal("0000-00-00T01:00:00.000Z"));
ff.literal("2015-01-01T00:00:00.000Z"));
q = new Query("ft1", lt);

// no feature should match
Expand Down
@@ -0,0 +1,125 @@
/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2015, Open Source Geospatial Foundation (OSGeo)
* (C) 2014-2015, Boundless
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.data.mongodb;

import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Date;

import org.geotools.data.Query;
import org.geotools.data.mongodb.geojson.GeoJSONMongoTestSetup;
import org.geotools.feature.visitor.MaxVisitor;
import org.geotools.feature.visitor.MinVisitor;
import org.geotools.filter.IllegalFilterException;
import org.junit.Test;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.PropertyName;

public class MongoFeatureSourceVisitorTest extends MongoTestSupport {

public MongoFeatureSourceVisitorTest() {
super(new GeoJSONMongoTestSetup());
}

boolean visited = false;
boolean valueSet = false;

class TestMinVisitor extends MinVisitor {

public TestMinVisitor(Expression expr) throws IllegalFilterException {
super(expr);
}

public void visit(Feature feature) {
super.visit(feature);
visited = true;
}
public void visit(SimpleFeature feature) {
super.visit(feature);
visited = true;
}

public void setValue(Object result) {
super.setValue(result);
valueSet = true;
}
}

@Override
protected void setUpInternal() throws Exception {
super.setUpInternal();
visited = false;
valueSet = false;
}

@Test
public void testMinVisitor() throws Exception {
FilterFactory ff = dataStore.getFilterFactory();
PropertyName p = ff.property("properties.dateProperty");

MinVisitor v = new TestMinVisitor(p);

dataStore.getFeatureSource("ft1").accepts(Query.ALL, v, null);

Date minDate = Date.from(Instant.from(DateTimeFormatter.ISO_DATE_TIME.parse("2015-01-01T00:00:00Z")));
assertEquals(minDate, v.getResult().getValue());

assertFalse(visited);
assertTrue(valueSet);
}

class TestMaxVisitor extends MaxVisitor {

public TestMaxVisitor(Expression expr) throws IllegalFilterException {
super(expr);
}

public void visit(Feature feature) {
super.visit(feature);
visited = true;
}
public void visit(SimpleFeature feature) {
super.visit(feature);
visited = true;
}

public void setValue(Object result) {
super.setValue(result);
valueSet = true;
}
}

@Test
public void testMaxVisitor() throws Exception {
FilterFactory ff = dataStore.getFilterFactory();
PropertyName p = ff.property("properties.dateProperty");

MaxVisitor v = new TestMaxVisitor(p);

dataStore.getFeatureSource("ft1").accepts(Query.ALL, v, null);

Date minDate = Date.from(Instant.from(DateTimeFormatter.ISO_DATE_TIME.parse("2015-01-01T21:30:00Z")));
assertEquals(minDate, v.getResult().getValue());

assertFalse(visited);
assertTrue(valueSet);
}
}
Expand Up @@ -75,6 +75,8 @@ protected void tearDownInternal() throws Exception {
protected Properties createExampleFixture() {
Properties fixture = new Properties();
fixture.put(MongoDataStoreFactory.DATASTORE_URI.key, "mongodb://geotools:geotools@localhost:27017/geotools");
fixture.put(MongoDataStoreFactory.SCHEMASTORE_URI.key, "mongodb://geotools:geotools@localhost:27017/geotools");

return fixture;
}

Expand Down
Expand Up @@ -33,7 +33,7 @@ public class GeoJSONMongoTestSetup extends MongoTestSetup {
static Date[] dateValues = new Date[] {
parseDate("2015-01-01T00:00:00.000Z"),
parseDate("2015-01-01T16:30:00.000Z"),
parseDate("0000-00-00T16:30:00.000Z")
parseDate("2015-01-01T21:30:00.000Z")
};

@Override
Expand Down

0 comments on commit 0941665

Please sign in to comment.