/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import java.io.IOException;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geotools.data.FeatureSource;
import org.geotools.data.complex.AppSchemaDataAccess;
import org.geotools.data.complex.FeatureTypeMapping;
import org.geotools.data.complex.filter.ComplexFilterSplitter;
import org.geotools.data.jdbc.FilterToSQLException;
import org.geotools.filter.FilterFactoryImplNamespaceAware;
import org.geotools.jdbc.JDBCDataStore;
import org.geotools.jdbc.NestedFilterToSQL;
import org.geotools.util.NullProgressListener;
import org.junit.Test;
import org.opengis.filter.Filter;
import org.opengis.filter.Or;
import org.opengis.filter.PropertyIsEqualTo;
import org.opengis.filter.PropertyIsNotEqualTo;
import org.w3c.dom.Document;
/**
* Test feature chaining with simple content type, e.g. for gml:name.
*
* @author Rini Angreani, CSIRO Earth Science and Resource Engineering
*/
public class SimpleAttributeFeatureChainWfsTest extends AbstractAppSchemaTestSupport {
@Override
protected SimpleAttributeFeatureChainMockData createTestData() {
return new SimpleAttributeFeatureChainMockData();
}
/**
* Test that feature chaining for gml:name works.
*/
@Test
public void testGetFeature() {
String path = "wfs?request=GetFeature&version=1.1.0&typeName=gsml:MappedFeature";
Document doc = getAsDOM(path);
LOGGER.info("MappedFeature with name feature chained Response:\n" + prettyString(doc));
assertXpathEvaluatesTo("4", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(4, "//gsml:MappedFeature", doc);
// mf1
checkMf1(doc);
// chaining geometry
assertXpathEvaluatesTo(
"133.8855 -23.6701",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf1']/gsml:shape/gml:Point/gml:pos",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf1']/gsml:shape/gml:Point/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4326",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf1']/gsml:shape/gml:Point/@srsName",
doc);
// mf2: extra values from denormalised tables
checkMf2(doc);
// chaining geometry
assertXpathEvaluatesTo(
"167.9388 -29.0434",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gsml:shape/gml:Point/gml:pos",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gsml:shape/gml:Point/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4326",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gsml:shape/gml:Point/@srsName",
doc);
// mf3
checkMf3(doc);
// chaining geometry
assertXpathEvaluatesTo(
"-1.2 53.5 -1.2 53.6 -1.1 53.6 -1.1 53.5 -1.2 53.5",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/gml:exterior/gml:LinearRing/gml:posList",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4283",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/@srsName",
doc);
// mf4
checkMf4(doc);
// chaining geometry
assertXpathEvaluatesTo(
"-1.3 53.5 -1.3 53.6 -1.2 53.6 -1.2 52.5 -1.3 53.5",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf4']/gsml:shape/gml:Polygon/gml:exterior/gml:LinearRing/gml:posList",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf4']/gsml:shape/gml:Polygon/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4283",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf4']/gsml:shape/gml:Polygon/@srsName",
doc);
}
/**
* Test reprojecting with feature chained geometry.
*/
@Test
public void testReprojecting() {
String path = "wfs?request=GetFeature&version=1.1.0&typeName=gsml:MappedFeature&srsName=EPSG:4326";
Document doc = getAsDOM(path);
LOGGER.info("Reprojected MappedFeature with name feature chained Response:\n"
+ prettyString(doc));
assertXpathEvaluatesTo("4", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(4, "//gsml:MappedFeature", doc);
// mf1
checkMf1(doc);
// chaining geometry
assertXpathEvaluatesTo(
"133.8855 -23.6701",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf1']/gsml:shape/gml:Point/gml:pos",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf1']/gsml:shape/gml:Point/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4326",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf1']/gsml:shape/gml:Point/@srsName",
doc);
// mf2: extra values from denormalised tables
checkMf2(doc);
// chaining geometry
assertXpathEvaluatesTo(
"167.9388 -29.0434",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gsml:shape/gml:Point/gml:pos",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gsml:shape/gml:Point/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4326",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gsml:shape/gml:Point/@srsName",
doc);
// mf3
checkMf3(doc);
// chaining geometry
assertXpathEvaluatesTo(
"-1.2 53.50000003577337 -1.2000000000000002 53.60000003565695 -1.1 53.60000003565695 -1.1 53.50000003577337 -1.2 53.50000003577337",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/gml:exterior/gml:LinearRing/gml:posList",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4326",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/@srsName",
doc);
// mf4
checkMf4(doc);
// chaining geometry
assertXpathEvaluatesTo(
"-1.2999999999999998 53.50000003577337 -1.3 53.60000003565695 -1.2000000000000002 53.60000003565695 -1.2 52.50000003687648 -1.2999999999999998 53.50000003577337",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf4']/gsml:shape/gml:Polygon/gml:exterior/gml:LinearRing/gml:posList",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf4']/gsml:shape/gml:Polygon/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4326",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf4']/gsml:shape/gml:Polygon/@srsName",
doc);
}
/**
* Test that filtering feature chained values works.
*/
@Test
public void testAttributeFilter() {
// filter by name
String xml = //
"<wfs:GetFeature " //
+ FeatureChainingWfsTest.GETFEATURE_ATTRIBUTES //
+ ">" //
+ " <wfs:Query typeName=\"gsml:MappedFeature\">" //
+ " <ogc:Filter>" //
+ " <ogc:PropertyIsEqualTo>" //
+ " <ogc:PropertyName>gml:name</ogc:PropertyName>" //
+ " <ogc:Literal>nametwo 4</ogc:Literal>" //
+ " </ogc:PropertyIsEqualTo>" //
+ " </ogc:Filter>" //
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
Document doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertXpathEvaluatesTo("1", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(1, "//gsml:MappedFeature", doc);
checkMf3(doc);
// chaining geometry
assertXpathEvaluatesTo(
"-1.2 53.5 -1.2 53.6 -1.1 53.6 -1.1 53.5 -1.2 53.5",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/gml:exterior/gml:LinearRing/gml:posList",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4283",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/@srsName",
doc);
xml = //
"<wfs:GetFeature " //
+ FeatureChainingWfsTest.GETFEATURE_ATTRIBUTES //
+ ">" //
+ " <wfs:Query typeName=\"gsml:MappedFeature\">" //
+ " <ogc:Filter>" //
+ " <ogc:PropertyIsEqualTo>" //
+ " <ogc:PropertyName>gml:name</ogc:PropertyName>" //
+ " <ogc:Literal>nametwo 3</ogc:Literal>" //
+ " </ogc:PropertyIsEqualTo>" //
+ " </ogc:Filter>" //
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertXpathEvaluatesTo("1", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(1, "//gsml:MappedFeature", doc);
checkMf2(doc);
// chaining geometry
assertXpathEvaluatesTo(
"167.9388 -29.0434",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gsml:shape/gml:Point/gml:pos",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gsml:shape/gml:Point/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4326",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gsml:shape/gml:Point/@srsName",
doc);
xml = //
"<wfs:GetFeature " //
+ FeatureChainingWfsTest.GETFEATURE_ATTRIBUTES //
+ ">" //
+ " <wfs:Query typeName=\"gsml:MappedFeature\">" //
+ " <ogc:Filter>" //
+ " <ogc:PropertyIsEqualTo>" //
+ " <ogc:PropertyName>gml:name</ogc:PropertyName>" //
+ " <ogc:Literal>nametwo 2</ogc:Literal>" //
+ " </ogc:PropertyIsEqualTo>" //
+ " </ogc:Filter>" //
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertXpathEvaluatesTo("1", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(1, "//gsml:MappedFeature", doc);
checkMf2(doc);
// chaining geometry
assertXpathEvaluatesTo(
"167.9388 -29.0434",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gsml:shape/gml:Point/gml:pos",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gsml:shape/gml:Point/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4326",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gsml:shape/gml:Point/@srsName",
doc);
}
/**
* Test filtering client properties.
*/
@Test
public void testClientPropertiesFilter() {
// filter by codespace coming from parent table
String xml = //
"<wfs:GetFeature " //
+ FeatureChainingWfsTest.GETFEATURE_ATTRIBUTES
+ ">" //
+ " <wfs:Query typeName=\"gsml:MappedFeature\">"
+ " <ogc:Filter>"
+ " <ogc:PropertyIsEqualTo>"
+ " <ogc:PropertyName>gml:name/@codeSpace</ogc:PropertyName>"
+ " <ogc:Literal>some:uri:mf3</ogc:Literal>"
+ " </ogc:PropertyIsEqualTo>"
+ " </ogc:Filter>"
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
Document doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertXpathEvaluatesTo("1", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(1, "//gsml:MappedFeature", doc);
checkMf3(doc);
// chaining geometry
assertXpathEvaluatesTo(
"-1.2 53.5 -1.2 53.6 -1.1 53.6 -1.1 53.5 -1.2 53.5",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/gml:exterior/gml:LinearRing/gml:posList",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4283",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/@srsName",
doc);
// filter by codespace coming from chained feature
xml = //
"<wfs:GetFeature " //
+ FeatureChainingWfsTest.GETFEATURE_ATTRIBUTES //
+ ">"
+ " <wfs:Query typeName=\"gsml:MappedFeature\">"
+ " <ogc:Filter>"
+ " <ogc:PropertyIsEqualTo>"
+ " <ogc:PropertyName>gml:name/@codeSpace</ogc:PropertyName>"
+ " <ogc:Literal>some uri 4</ogc:Literal>"
+ " </ogc:PropertyIsEqualTo>"
+ " </ogc:Filter>"
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertXpathEvaluatesTo("1", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(1, "//gsml:MappedFeature", doc);
checkMf3(doc);
// chaining geometry
assertXpathEvaluatesTo(
"-1.2 53.5 -1.2 53.6 -1.1 53.6 -1.1 53.5 -1.2 53.5",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/gml:exterior/gml:LinearRing/gml:posList",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4283",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/@srsName",
doc);
// filter by xlink:href coming from chained feature
xml = //
"<wfs:GetFeature " //
+ FeatureChainingWfsTest.GETFEATURE_ATTRIBUTES //
+ ">"
+ " <wfs:Query typeName=\"gsml:MappedFeature\">"
+ " <ogc:Filter>"
+ " <ogc:PropertyIsEqualTo>"
+ " <ogc:PropertyName>gml:name/@xlink:href</ogc:PropertyName>"
+ " <ogc:Literal>some:uri:4</ogc:Literal>"
+ " </ogc:PropertyIsEqualTo>"
+ " </ogc:Filter>"
+ " </wfs:Query> " //
+ "</wfs:GetFeature>";
doc = postAsDOM("wfs", xml);
LOGGER.info("WFS filter GetFeature response:\n" + prettyString(doc));
assertXpathEvaluatesTo("1", "/wfs:FeatureCollection/@numberOfFeatures", doc);
assertXpathCount(1, "//gsml:MappedFeature", doc);
checkMf3(doc);
// chaining geometry
assertXpathEvaluatesTo(
"-1.2 53.5 -1.2 53.6 -1.1 53.6 -1.1 53.5 -1.2 53.5",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/gml:exterior/gml:LinearRing/gml:posList",
doc);
assertXpathEvaluatesTo(
"2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/@srsDimension",
doc);
assertXpathEvaluatesTo(
"http://www.opengis.net/gml/srs/epsg.xml#4283",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gsml:shape/gml:Polygon/@srsName",
doc);
}
@Test
public void testNestedFilterEncoding() throws IOException, FilterToSQLException {
FeatureTypeInfo ftInfo = getCatalog().getFeatureTypeByName("gsml", "MappedFeature");
FeatureSource fs = ftInfo.getFeatureSource(new NullProgressListener(), null);
AppSchemaDataAccess da = (AppSchemaDataAccess) fs.getDataStore();
FeatureTypeMapping rootMapping = da.getMappingByNameOrElement(ftInfo.getQualifiedName());
// make sure nested filters encoding is enabled, otherwise skip test
assumeTrue(shouldTestNestedFiltersEncoding(rootMapping));
JDBCDataStore store = (JDBCDataStore) rootMapping.getSource().getDataStore();
NestedFilterToSQL nestedFilterToSQL = createNestedFilterEncoder(rootMapping);
FilterFactoryImplNamespaceAware ff = new FilterFactoryImplNamespaceAware();
ff.setNamepaceContext(rootMapping.getNamespaces());
/*
* test filter by name coming from child table
*/
PropertyIsEqualTo propertyIsEqualTo = ff.equals(ff.property("gml:name[2]"),
ff.literal("nameone 4"));
// Filter involving single nested attribute --> can be encoded
ComplexFilterSplitter splitter = new ComplexFilterSplitter(store.getFilterCapabilities(),
rootMapping);
splitter.visit(propertyIsEqualTo, null);
Filter preFilter = splitter.getFilterPre();
Filter postFilter = splitter.getFilterPost();
assertEquals(propertyIsEqualTo, preFilter);
assertEquals(Filter.INCLUDE, postFilter);
// filter must be "unrolled" (i.e. reverse mapped) first
Filter unrolled = AppSchemaDataAccess.unrollFilter(propertyIsEqualTo, rootMapping);
// Filter is nested
assertTrue(NestedFilterToSQL.isNestedFilter(unrolled));
String encodedFilter = nestedFilterToSQL.encodeToString(unrolled);
// this is the generated query in PostGIS, but the test limits to check the presence of the
// EXISTS keyword, as the actual SQL is dependent on the underlying database
// EXISTS (SELECT "chain_link_1"."PKEY"
// FROM "appschematest"."MAPPEDFEATURENAMEONE" "chain_link_1"
// WHERE "chain_link_1"."NAME" = 'nameone 4' AND
// "appschematest"."MAPPEDFEATUREWITHNESTEDNAME"."ID" = "chain_link_1"."MF_ID")
assertTrue(encodedFilter.contains("EXISTS"));
assertContainsFeatures(fs.getFeatures(propertyIsEqualTo), "mf3");
/*
* test filter on attribute of root feature type
*/
PropertyIsEqualTo ordinaryFilter = ff.equals(ff.property("gml:name[1]"),
ff.literal("GUNTHORPE FORMATION"));
// Filter involving direct attribute of root feature type --> can be encoded
ComplexFilterSplitter splitter2 = new ComplexFilterSplitter(store.getFilterCapabilities(),
rootMapping);
splitter2.visit(ordinaryFilter, null);
preFilter = splitter2.getFilterPre();
postFilter = splitter2.getFilterPost();
assertEquals(ordinaryFilter, preFilter);
assertEquals(Filter.INCLUDE, postFilter);
// Filter must be "unrolled" (i.e. reverse mapped) prior to being encoded
unrolled = AppSchemaDataAccess.unrollFilter(ordinaryFilter, rootMapping);
// Filter is NOT nested
assertFalse(NestedFilterToSQL.isNestedFilter(unrolled));
// IS THIS A BUG?!?
String ordinaryEncoded = nestedFilterToSQL.encodeToString(unrolled);
assertFalse(ordinaryEncoded.contains("EXISTS"));
assertContainsFeatures(fs.getFeatures(ordinaryFilter), "mf1");
/*
* test filter matching multiple mappings
*/
PropertyIsEqualTo multipleFilter = ff.equals(ff.property("gml:name"),
ff.literal("GUNTHORPE FORMATION"));
// Filter involves multiple nested attributes --> CANNOT be encoded
ComplexFilterSplitter splitter3 = new ComplexFilterSplitter(store.getFilterCapabilities(),
rootMapping);
splitter3.visit(multipleFilter, null);
preFilter = splitter3.getFilterPre();
postFilter = splitter3.getFilterPost();
assertEquals(Filter.INCLUDE, preFilter);
assertEquals(multipleFilter, postFilter);
// Filter must be "unrolled" (i.e. reverse mapped) first
unrolled = AppSchemaDataAccess.unrollFilter(multipleFilter, rootMapping);
// Filter is nested
assertTrue(NestedFilterToSQL.isNestedFilter(unrolled));
assertContainsFeatures(fs.getFeatures(multipleFilter), "mf1");
/*
* test combined filter, i.e. a filter composed of a "regular" filter, and a nested filter
*/
PropertyIsEqualTo regularFilter = ff.equals(ff.property("gml:name[1]"),
ff.literal("GUNTHORPE FORMATION"));
PropertyIsEqualTo nestedFilter = ff.equals(ff.property("gml:name[2]"),
ff.literal("nameone 4"));
Or combined = ff.or(regularFilter, nestedFilter);
// Filter can be encoded!
ComplexFilterSplitter splitterCombined = new ComplexFilterSplitter(
store.getFilterCapabilities(), rootMapping);
splitterCombined.visit(combined, null);
preFilter = splitterCombined.getFilterPre();
postFilter = splitterCombined.getFilterPost();
assertEquals(combined, preFilter);
assertEquals(Filter.INCLUDE, postFilter);
// Filter must be "unrolled" (i.e. reverse mapped) first
unrolled = AppSchemaDataAccess.unrollFilter(combined, rootMapping);
// Filter is nested
assertTrue(NestedFilterToSQL.isNestedFilter(unrolled));
String encodedCombined = nestedFilterToSQL.encodeToString(unrolled);
// this is the generated query in PostGIS, but the test limits to check the presence of the
// EXISTS keyword, as the actual SQL is dependent on the underlying database
// (LEX_D = 'GUNTHORPE FORMATION' OR EXISTS
// (SELECT "chain_link_1"."PKEY"
// FROM "appschematest"."MAPPEDFEATURENAMEONE" "chain_link_1"
// WHERE "chain_link_1"."NAME" = 'nameone 4' AND
// "appschematest"."MAPPEDFEATUREWITHNESTEDNAME"."ID" = "chain_link_1"."MF_ID"))
assertTrue(encodedCombined.matches("^\\(.*GUNTHORPE FORMATION.*OR.*EXISTS.*\\)$"));
assertContainsFeatures(fs.getFeatures(combined), "mf1", "mf3");
/*
* test filter comparing multiple nested attributes
*/
PropertyIsNotEqualTo notEquals = ff.notEqual(ff.property("gml:name[2]"),
ff.property("gml:name[3]"));
// Filter involves multiple nested attributes --> CANNOT be encoded
ComplexFilterSplitter splitterNotEquals = new ComplexFilterSplitter(
store.getFilterCapabilities(), rootMapping);
splitterNotEquals.visit(notEquals, null);
preFilter = splitterNotEquals.getFilterPre();
postFilter = splitterNotEquals.getFilterPost();
assertEquals(Filter.INCLUDE, preFilter);
assertEquals(notEquals, postFilter);
// Filter must be "unrolled" (i.e. reverse mapped) first
unrolled = AppSchemaDataAccess.unrollFilter(notEquals, rootMapping);
// Filter is nested
assertTrue(NestedFilterToSQL.isNestedFilter(unrolled));
assertContainsFeatures(fs.getFeatures(notEquals), "mf1", "mf2", "mf3", "mf4");
}
private void checkMf1(Document doc) {
assertXpathCount(4, "//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf1']/gml:name", doc);
// gml:name with values coming from the main table
assertXpathEvaluatesTo("GUNTHORPE FORMATION",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf1']/gml:name[1]", doc);
// gml:name with values coming from another table(MappedFeatureNameOne)
assertXpathEvaluatesTo("nameone 1",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf1']/gml:name[2]", doc);
// client property coming from another table(MappedFeatureNameOne)
assertXpathEvaluatesTo("some uri 1",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf1']/gml:name[2]/@codeSpace",
doc);
// gml:name with values coming from another table(MappedFeatureNameTwo)
assertXpathEvaluatesTo("nametwo 1",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf1']/gml:name[3]", doc);
// client property coming from the parent table
assertXpathEvaluatesTo("some:uri:mf1",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf1']/gml:name[3]/@codeSpace",
doc);
// gml:name as xlink:href from another table(MappedFeatureNameTwo)
assertXpathEvaluatesTo("some:uri:1",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf1']/gml:name[4]/@xlink:href",
doc);
}
private void checkMf2(Document doc) {
// mf2: extra values from denormalised tables
assertXpathCount(7, "//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gml:name", doc);
// gml:name with values coming from the main table
assertXpathEvaluatesTo("MERCIA MUDSTONE GROUP",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gml:name[1]", doc);
// gml:name with values coming from another table(MappedFeatureNameOne)
assertXpathEvaluatesTo("nameone 2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gml:name[2]", doc);
// client property coming from another table(MappedFeatureNameOne)
assertXpathEvaluatesTo("some uri 2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gml:name[2]/@codeSpace",
doc);
// gml:name with values coming from another table(MappedFeatureNameOne)
assertXpathEvaluatesTo("nameone 3",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gml:name[3]", doc);
// client property coming from another table(MappedFeatureNameOne)
assertXpathEvaluatesTo("some uri 3",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gml:name[3]/@codeSpace",
doc);
// gml:name with values coming from another table(MappedFeatureNameTwo)
assertXpathEvaluatesTo("nametwo 2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gml:name[4]", doc);
// client property coming from the parent table
assertXpathEvaluatesTo("some:uri:mf2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gml:name[4]/@codeSpace",
doc);
// gml:name with values coming from another table(MappedFeatureNameTwo)
assertXpathEvaluatesTo("nametwo 3",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gml:name[5]", doc);
// client property coming from the parent table
assertXpathEvaluatesTo("some:uri:mf2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gml:name[5]/@codeSpace",
doc);
// gml:name as xlink:href from another table(MappedFeatureNameTwo)
assertXpathEvaluatesTo("some:uri:2",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gml:name[6]/@xlink:href",
doc);
assertXpathEvaluatesTo("some:uri:3",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf2']/gml:name[7]/@xlink:href",
doc);
}
private void checkMf3(Document doc) {
assertXpathCount(4, "//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gml:name", doc);
// gml:name with values coming from the main table
assertXpathEvaluatesTo("CLIFTON FORMATION",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gml:name[1]", doc);
// gml:name with values coming from another table(MappedFeatureNameOne)
assertXpathEvaluatesTo("nameone 4",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gml:name[2]", doc);
// client property coming from another table(MappedFeatureNameOne)
assertXpathEvaluatesTo("some uri 4",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gml:name[2]/@codeSpace",
doc);
// gml:name with values coming from another table(MappedFeatureNameTwo)
assertXpathEvaluatesTo("nametwo 4",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gml:name[3]", doc);
// client property coming from the parent table
assertXpathEvaluatesTo("some:uri:mf3",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gml:name[3]/@codeSpace",
doc);
// gml:name as xlink:href from another table(MappedFeatureNameTwo)
assertXpathEvaluatesTo("some:uri:4",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf3']/gml:name[4]/@xlink:href",
doc);
}
private void checkMf4(Document doc) {
assertXpathCount(4, "//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf4']/gml:name", doc);
// gml:name with values coming from the main table
assertXpathEvaluatesTo("MURRADUC BASALT",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf4']/gml:name[1]", doc);
// gml:name with values coming from another table(MappedFeatureNameOne)
assertXpathEvaluatesTo("nameone 5",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf4']/gml:name[2]", doc);
// client property coming from another table(MappedFeatureNameOne)
assertXpathEvaluatesTo("some uri 5",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf4']/gml:name[2]/@codeSpace",
doc);
// gml:name with values coming from another table(MappedFeatureNameTwo)
assertXpathEvaluatesTo("nametwo 5",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf4']/gml:name[3]", doc);
// client property coming from the parent table
assertXpathEvaluatesTo("some:uri:mf4",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf4']/gml:name[3]/@codeSpace",
doc);
// gml:name as xlink:href from another table(MappedFeatureNameTwo)
assertXpathEvaluatesTo("some:uri:5",
"//gsml:MappedFeature[@gml:id='gsml.mappedfeature.mf4']/gml:name[4]/@xlink:href",
doc);
}
}