Author: jsorel
Date: Thu Apr 26 12:42:41 2018
New Revision: 1830206
URL: http://svn.apache.org/viewvc?rev=1830206&view=rev
Log:
FeatureSet : add JoinFeatureSet implementation, similar to SQL Join for two
distinct feature set
Added:
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/JoinFeatureSet.java
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/JoinFeatureSetTest.java
Modified:
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
Added:
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/JoinFeatureSet.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/JoinFeatureSet.java?rev=1830206&view=auto
==============================================================================
---
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/JoinFeatureSet.java
(added)
+++
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/JoinFeatureSet.java
Thu Apr 26 12:42:41 2018
@@ -0,0 +1,475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.sis.internal.storage;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.filter.DefaultLiteral;
+import org.apache.sis.filter.DefaultPropertyIsEqualTo;
+import org.apache.sis.internal.feature.AttributeConvention;
+import org.apache.sis.internal.storage.query.SimpleQuery;
+import org.apache.sis.metadata.iso.DefaultMetadata;
+import org.apache.sis.metadata.iso.citation.DefaultCitation;
+import org.apache.sis.metadata.iso.identification.DefaultDataIdentification;
+import org.apache.sis.referencing.NamedIdentifier;
+import org.apache.sis.storage.DataStore;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.FeatureSet;
+import org.apache.sis.storage.Query;
+import org.apache.sis.storage.UnsupportedQueryException;
+import org.apache.sis.util.collection.BackingStoreException;
+import org.apache.sis.util.iso.SimpleInternationalString;
+import org.apache.sis.util.logging.WarningListeners;
+import org.opengis.feature.Feature;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.MatchAction;
+import org.opengis.filter.PropertyIsEqualTo;
+import org.opengis.filter.expression.Expression;
+import org.opengis.geometry.Envelope;
+import org.opengis.metadata.Metadata;
+import org.opengis.util.GenericName;
+
+/**
+ * A join feature set merges features from two different sources following a
+ * SQL Join condition.
+ *
+ * <p>
+ * This implementation is read-only.
+ * </p>
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+public class JoinFeatureSet extends AbstractFeatureSet implements FeatureSet {
+
+ /**
+ * Join operation type.
+ */
+ public enum Type {
+ /**
+ * Both side must have a value to be included.
+ */
+ INNER,
+
+ /**
+ * A least left side must have a value to be included.
+ */
+ LEFT_OUTER,
+
+ /**
+ * A least right side must have a value to be included.
+ */
+ RIGHT_OUTER
+ }
+
+ private final FeatureSet left;
+ private final FeatureSet right;
+ private String leftAlias;
+ private String rightAlias;
+ private final Type joinType;
+ private final PropertyIsEqualTo condition;
+
+ //cache
+ private FeatureType type;
+
+
+ public JoinFeatureSet(final WarningListeners<DataStore> listeners,
FeatureSet left,
+ String leftAlias, FeatureSet right, String rightAlias, Type
joinType, PropertyIsEqualTo condition) {
+ super(listeners);
+ this.left = left;
+ this.right = right;
+ this.leftAlias = leftAlias;
+ this.rightAlias = rightAlias;
+ this.joinType = joinType;
+ this.condition = condition;
+ }
+
+ /**
+ * Gets the join condition.
+ *
+ * @return Filter
+ */
+ public PropertyIsEqualTo getJoinCondition() {
+ return condition;
+ }
+
+ /**
+ * Gets the join type.
+ *
+ * @return join type
+ */
+ public Type getJoinType() {
+ return joinType;
+ }
+
+ /**
+ * Gets the left feature source.
+ *
+ * @return left join feature set
+ */
+ public FeatureSet getLeft() {
+ return left;
+ }
+
+ /**
+ * Gets the right feature source.
+ *
+ * @return right join feature set
+ */
+ public FeatureSet getRight() {
+ return right;
+ }
+
+ @Override
+ public FeatureType getType() throws DataStoreException {
+ if (type == null) {
+ final FeatureType leftType = left.getType();
+ final FeatureType rightType = right.getType();
+ final GenericName leftName = leftType.getName();
+ final GenericName rightName = rightType.getName();
+ if (leftAlias == null) leftAlias = leftName.toString();
+ if (rightAlias == null) rightAlias = rightName.toString();
+
+ final FeatureTypeBuilder ftb = new FeatureTypeBuilder();
+
ftb.addAttribute(String.class).setName(AttributeConvention.IDENTIFIER_PROPERTY).setDefaultValue("");
+
ftb.addAssociation(leftType).setName(leftAlias).setMinimumOccurs(0).setMaximumOccurs(1);
+
ftb.addAssociation(rightType).setName(rightAlias).setMinimumOccurs(0).setMaximumOccurs(1);
+ ftb.setName(leftName.tip().toString() + '-' +
rightName.tip().toString());
+ type = ftb.build();
+ }
+
+ return type;
+ }
+
+ @Override
+ public Metadata getMetadata() throws DataStoreException {
+ final FeatureType type = getType();
+ final DefaultMetadata metadata = new DefaultMetadata();
+ final DefaultDataIdentification ident = new
DefaultDataIdentification();
+ final DefaultCitation citation = new DefaultCitation();
+ citation.setTitle(new
SimpleInternationalString(type.getName().toString()));
+ citation.setIdentifiers(Arrays.asList(new
NamedIdentifier(type.getName())));
+ ident.setCitation(citation);
+ metadata.setIdentificationInfo(Arrays.asList(ident));
+ return metadata;
+ }
+
+ /**
+ * Envelope is not stored or computed.
+ *
+ * @return always null
+ * @throws DataStoreException
+ */
+ @Override
+ public Envelope getEnvelope() throws DataStoreException {
+ return null;
+ }
+
+ @Override
+ public FeatureSet subset(Query query) throws UnsupportedQueryException,
DataStoreException {
+ if (query instanceof SimpleQuery) {
+ return SimpleQuery.executeOnCPU(this, (SimpleQuery) query);
+ }
+ return FeatureSet.super.subset(query);
+ }
+
+ @Override
+ public Stream<Feature> features(boolean parallel) throws
DataStoreException {
+ final JoinIterator ite;
+ switch (joinType) {
+ case INNER : ite = new JoinInnerRowIterator(); break;
+ case LEFT_OUTER : ite = new JoinOuterRowIterator(true); break;
+ case RIGHT_OUTER : ite = new JoinOuterRowIterator(false); break;
+ default:
+ throw new IllegalArgumentException("Unknown Join type : " +
joinType);
+ }
+ final Stream<Feature> stream = StreamSupport.stream(
+ Spliterators.spliteratorUnknownSize(ite, Spliterator.ORDERED),
+ false);
+ stream.onClose(ite::close);
+ return stream;
+ }
+
+ /**
+ * Aggregate all feature from selectors to a single complex feature.
+ *
+ * @return aggregated features
+ * @throws DataStoreException
+ */
+ private Feature toFeature(final Feature left, final Feature right) throws
DataStoreException{
+ final FeatureType type = getType(); //force creating type.
+ final Feature f = type.newInstance();
+
+ String id = "";
+
+ if (left != null) {
+ id +=
left.getPropertyValue(AttributeConvention.IDENTIFIER_PROPERTY.toString());
+ f.setPropertyValue(leftAlias,left);
+ }
+ if (right != null) {
+ if (left != null) id += " ";
+ id +=
right.getPropertyValue(AttributeConvention.IDENTIFIER_PROPERTY.toString());
+ f.setPropertyValue(rightAlias,right);
+ }
+
+ f.setPropertyValue(AttributeConvention.IDENTIFIER_PROPERTY.toString(),
id);
+ return f;
+ }
+
+ private interface JoinIterator extends Iterator<Feature>, AutoCloseable {
+
+ @Override
+ public default void close() {}
+
+ }
+
+ /**
+ * Iterate on both collections with an Inner join condition.
+ */
+ private class JoinInnerRowIterator implements JoinIterator {
+
+ private final Stream<Feature> leftStream;
+ private final Iterator<Feature> leftIterator;
+ private Stream<Feature> rightStream;
+ private Iterator<Feature> rightIterator;
+ private Feature leftFeature;
+ private Feature combined;
+
+ JoinInnerRowIterator() throws DataStoreException {
+ leftStream = left.features(false);
+ leftIterator = leftStream.iterator();
+ }
+
+ @Override
+ public Feature next() {
+ try {
+ searchNext();
+ } catch (DataStoreException ex) {
+ throw new BackingStoreException(ex);
+ }
+ Feature f = combined;
+ combined = null;
+ return f;
+ }
+
+ @Override
+ public void close() {
+ leftStream.close();
+ if (rightStream != null) {
+ rightStream.close();
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ try {
+ searchNext();
+ } catch (DataStoreException ex) {
+ throw new BackingStoreException(ex);
+ }
+ return combined != null;
+ }
+
+ private void searchNext() throws DataStoreException {
+ if (combined != null) return;
+
+ final PropertyIsEqualTo equal = getJoinCondition();
+ final Expression leftProperty = equal.getExpression1();
+ final Expression rightProperty = equal.getExpression2();
+
+ //we might have several right features for one left
+ if (leftFeature != null && rightIterator != null) {
+ while (combined == null && rightIterator.hasNext()) {
+ final Feature rightRes = rightIterator.next();
+ combined = toFeature(leftFeature, rightRes);
+ }
+ }
+
+ if (rightIterator != null && !rightIterator.hasNext()){
+ //no more results in right iterator, close iterator
+ rightStream.close();
+ rightStream = null;
+ rightIterator = null;
+ }
+
+ while (combined == null && leftIterator.hasNext()) {
+ leftFeature = leftIterator.next();
+
+ final Object leftValue = leftProperty.evaluate(leftFeature);
+
+ if (rightIterator == null) {
+ final SimpleQuery rightQuery = new SimpleQuery();
+ rightQuery.setFilter(new
DefaultPropertyIsEqualTo(rightProperty, new DefaultLiteral<>(leftValue), true,
MatchAction.ALL));
+ rightStream = right.subset(rightQuery).features(false);
+ rightIterator = rightStream.iterator();
+ }
+
+ while (combined == null && rightIterator.hasNext()) {
+ final Feature rightRow = rightIterator.next();
+ combined = toFeature(leftFeature, rightRow);
+ }
+
+ if (combined == null) {
+ //no more results in right iterator, close iterator
+ rightStream.close();
+ rightStream = null;
+ rightIterator = null;
+ }
+ }
+
+ }
+
+ }
+
+ /**
+ * Iterate on both collections with an outer join condition.
+ */
+ private class JoinOuterRowIterator implements JoinIterator {
+
+ private final Stream<Feature> primeStream;
+ private final Iterator<Feature> primeIterator;
+ private Stream<Feature> secondStream;
+ private Iterator<Feature> secondIterator;
+
+ private final boolean leftJoint;
+ private Feature primeFeature;
+ private Feature nextFeature;
+
+ JoinOuterRowIterator(final boolean leftJoint) throws
DataStoreException{
+ this.leftJoint = leftJoint;
+ if (leftJoint) {
+ primeStream = left.features(false);
+ } else {
+ primeStream = right.features(false);
+ }
+ primeIterator = primeStream.iterator();
+
+ }
+
+ @Override
+ public Feature next() {
+ try {
+ searchNext();
+ } catch (DataStoreException ex) {
+ throw new BackingStoreException(ex);
+ }
+ Feature f = nextFeature;
+ nextFeature = null;
+ return f;
+ }
+
+ @Override
+ public void close() {
+ primeStream.close();
+ if (secondStream != null) {
+ secondStream.close();
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ try {
+ searchNext();
+ } catch (DataStoreException ex) {
+ throw new BackingStoreException(ex);
+ }
+ return nextFeature != null;
+ }
+
+ private void searchNext() throws DataStoreException {
+ if (nextFeature != null) return;
+
+ final PropertyIsEqualTo equal = getJoinCondition();
+ final Expression leftProperty = equal.getExpression1();
+ final Expression rightProperty = equal.getExpression2();
+
+ //we might have several right features for one left
+ if (primeFeature != null && secondIterator != null) {
+ while (nextFeature == null && secondIterator.hasNext()) {
+ final Feature secondCandidate = secondIterator.next();
+ nextFeature = checkValid(primeFeature, secondCandidate,
leftJoint);
+ }
+ }
+
+ while (nextFeature == null && primeIterator.hasNext()) {
+ primeFeature = primeIterator.next();
+
+ if (secondIterator != null) {
+ secondStream.close();
+ secondStream = null;
+ secondIterator = null;
+ }
+
+ final Object primeValue;
+ if (leftJoint) {
+ primeValue = leftProperty.evaluate(primeFeature);
+ } else {
+ primeValue = rightProperty.evaluate(primeFeature);
+ }
+
+ if (secondIterator == null) {
+ final SimpleQuery query = new SimpleQuery();
+ if (leftJoint) {
+ query.setFilter(new
DefaultPropertyIsEqualTo(rightProperty, new DefaultLiteral<>(primeValue), true,
MatchAction.ALL));
+ secondStream = right.subset(query).features(false);
+ secondIterator = secondStream.iterator();
+ } else {
+ query.setFilter(new
DefaultPropertyIsEqualTo(leftProperty, new DefaultLiteral<>(primeValue), true,
MatchAction.ALL));
+ secondStream = left.subset(query).features(false);
+ secondIterator = secondStream.iterator();
+ }
+ }
+
+ while (nextFeature == null && secondIterator.hasNext()) {
+ final Feature rightRow = secondIterator.next();
+ nextFeature = checkValid(primeFeature, rightRow,leftJoint);
+ }
+
+ if (nextFeature == null) {
+ //outer left effect, no right match but still we must
return the left side
+ if (leftJoint) {
+ nextFeature = toFeature(primeFeature,null);
+ } else {
+ nextFeature = toFeature(null,primeFeature);
+ }
+ }
+
+ }
+
+ }
+
+ private Feature checkValid(final Feature left, final Feature right,
final boolean leftJoin) throws DataStoreException{
+ final Feature candidate;
+ if (leftJoin) {
+ candidate = toFeature(left,right);
+ } else {
+ candidate = toFeature(right,left);
+ }
+
+ return candidate;
+ }
+
+ }
+
+}
Added:
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/JoinFeatureSetTest.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/JoinFeatureSetTest.java?rev=1830206&view=auto
==============================================================================
---
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/JoinFeatureSetTest.java
(added)
+++
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/JoinFeatureSetTest.java
Thu Apr 26 12:42:41 2018
@@ -0,0 +1,375 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.sis.internal.storage;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Stream;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.filter.DefaultFilterFactory;
+import org.apache.sis.internal.feature.AttributeConvention;
+import org.apache.sis.storage.FeatureSet;
+import org.apache.sis.test.TestCase;
+import org.apache.sis.util.iso.Names;
+import org.junit.Test;
+import org.opengis.feature.Feature;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.FilterFactory;
+import org.opengis.filter.PropertyIsEqualTo;
+import org.opengis.util.GenericName;
+import static org.junit.Assert.*;
+
+/**
+ * Tests {@link JoinFeatureSet}.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+public class JoinFeatureSetTest extends TestCase {
+
+ private static final FilterFactory FF = new DefaultFilterFactory();
+ private static final String ATT_ID =
AttributeConvention.IDENTIFIER_PROPERTY.toString();
+
+ private final FeatureSet featureSet1;
+ private final FeatureSet featureSet2;
+ private final GenericName name1;
+ private final GenericName name2;
+
+ private final String fid_1_0;
+ private final String fid_1_1;
+ private final String fid_1_2;
+ private final String fid_1_3;
+ private final String fid_1_4;
+ private final String fid_2_0;
+ private final String fid_2_1;
+ private final String fid_2_2;
+ private final String fid_2_3;
+ private final String fid_2_4;
+ private final String fid_2_5;
+
+
+ public JoinFeatureSetTest() throws Exception {
+ FeatureTypeBuilder builder = new FeatureTypeBuilder();
+
+
//----------------------------------------------------------------------
+ name1 = Names.createLocalName(null, null, "Type1");
+ builder.setName(name1);
+
builder.addAttribute(String.class).setName(AttributeConvention.IDENTIFIER_PROPERTY);
+ builder.addAttribute(String.class).setName("http://type1.com", "att1");
+ builder.addAttribute(Integer.class).setName("http://type1.com",
"att2");
+ final FeatureType sft1 = builder.build();
+
+ final List<Feature> features1 = new ArrayList<>();
+
+ Feature sf = sft1.newInstance();
+ sf.setPropertyValue(ATT_ID, "fid_1_0");
+ sf.setPropertyValue("att1", "str1");
+ sf.setPropertyValue("att2", 1);
+ fid_1_0 = getId(sf);
+ features1.add(sf);
+
+ sf = sft1.newInstance();
+ sf.setPropertyValue(ATT_ID, "fid_1_1");
+ sf.setPropertyValue("att1", "str2");
+ sf.setPropertyValue("att2", 2);
+ fid_1_1 = getId(sf);
+ features1.add(sf);
+
+ sf = sft1.newInstance();
+ sf.setPropertyValue(ATT_ID, "fid_1_2");
+ sf.setPropertyValue("att1", "str3");
+ sf.setPropertyValue("att2", 3);
+ fid_1_2 = getId(sf);
+ features1.add(sf);
+
+ sf = sft1.newInstance();
+ sf.setPropertyValue(ATT_ID, "fid_1_3");
+ sf.setPropertyValue("att1", "str50");
+ sf.setPropertyValue("att2", 50);
+ fid_1_3 = getId(sf);
+ features1.add(sf);
+
+ sf = sft1.newInstance();
+ sf.setPropertyValue(ATT_ID, "fid_1_4");
+ sf.setPropertyValue("att1", "str51");
+ sf.setPropertyValue("att2", 51);
+ fid_1_4 = getId(sf);
+ features1.add(sf);
+
+ featureSet1 = new ArrayFeatureSet(null, sft1, features1, null);
+
+
+
//----------------------------------------------------------------------
+ name2 = Names.createLocalName(null, null, "Type2");
+ builder = new FeatureTypeBuilder();
+ builder.setName(name2);
+
builder.addAttribute(String.class).setName(AttributeConvention.IDENTIFIER_PROPERTY);
+ builder.addAttribute(Integer.class).setName("http://type2.com",
"att3");
+ builder.addAttribute(Double.class).setName("http://type2.com", "att4");
+ final FeatureType sft2 = builder.build();
+
+ final List<Feature> features2 = new ArrayList<>();
+
+ sf = sft2.newInstance();
+ sf.setPropertyValue(ATT_ID, "fid_2_0");
+ sf.setPropertyValue("att3", 1);
+ sf.setPropertyValue("att4", 10d);
+ fid_2_0 = getId(sf);
+ features2.add(sf);
+
+ sf = sft2.newInstance();
+ sf.setPropertyValue(ATT_ID, "fid_2_1");
+ sf.setPropertyValue("att3", 2);
+ sf.setPropertyValue("att4", 20d);
+ fid_2_1 = getId(sf);
+ features2.add(sf);
+
+ sf = sft2.newInstance();
+ sf.setPropertyValue(ATT_ID, "fid_2_2");
+ sf.setPropertyValue("att3", 2);
+ sf.setPropertyValue("att4", 30d);
+ fid_2_2 = getId(sf);
+ features2.add(sf);
+
+ sf = sft2.newInstance();
+ sf.setPropertyValue(ATT_ID, "fid_2_3");
+ sf.setPropertyValue("att3", 3);
+ sf.setPropertyValue("att4", 40d);
+ fid_2_3 = getId(sf);
+ features2.add(sf);
+
+ sf = sft2.newInstance();
+ sf.setPropertyValue(ATT_ID, "fid_2_4");
+ sf.setPropertyValue("att3", 60);
+ sf.setPropertyValue("att4", 60d);
+ fid_2_4 = getId(sf);
+ features2.add(sf);
+
+ sf = sft2.newInstance();
+ sf.setPropertyValue(ATT_ID, "fid_2_5");
+ sf.setPropertyValue("att3", 61);
+ sf.setPropertyValue("att4", 61d);
+ fid_2_5 = getId(sf);
+ features2.add(sf);
+
+ featureSet2 = new ArrayFeatureSet(null, sft2, features2, null);
+
+ }
+
+ /**
+ * Test inner join feature set.
+ */
+ @Test
+ public void testInnerJoin() throws Exception{
+
+ final PropertyIsEqualTo condition = FF.equals(FF.property("att2"),
FF.property("att3"));
+ final FeatureSet col = new JoinFeatureSet(null, featureSet1, "s1",
featureSet2, "s2", JoinFeatureSet.Type.INNER, condition);
+
+ try (Stream<Feature> stream = col.features(false)) {
+ final Iterator<Feature> ite = stream.iterator();
+ Feature f = null;
+ Feature c1 = null;
+ Feature c2 = null;
+
+ int count = 0;
+ while (ite.hasNext()) {
+ count++;
+ f = ite.next();
+ if (getId(f).equals(fid_1_0 +" "+fid_2_0)) {
+ c1 = (Feature) f.getPropertyValue("s1");
+ c2 = (Feature) f.getPropertyValue("s2");
+ assertEquals("str1", c1.getProperty("att1").getValue());
+ assertEquals(1, c1.getProperty("att2").getValue());
+ assertEquals(1, c2.getProperty("att3").getValue());
+ assertEquals(10d, c2.getProperty("att4").getValue());
+ } else if(getId(f).equals(fid_1_1 +" "+fid_2_1)) {
+ c1 = (Feature) f.getPropertyValue("s1");
+ c2 = (Feature) f.getPropertyValue("s2");
+ assertEquals("str2", c1.getProperty("att1").getValue());
+ assertEquals(2, c1.getProperty("att2").getValue());
+ assertEquals(2, c2.getProperty("att3").getValue());
+ assertEquals(20d, c2.getProperty("att4").getValue());
+ } else if(getId(f).equals(fid_1_1 +" "+fid_2_2)) {
+ c1 = (Feature) f.getPropertyValue("s1");
+ c2 = (Feature) f.getPropertyValue("s2");
+ assertEquals("str2", c1.getProperty("att1").getValue());
+ assertEquals(2, c1.getProperty("att2").getValue());
+ assertEquals(2, c2.getProperty("att3").getValue());
+ assertEquals(30d, c2.getProperty("att4").getValue());
+ } else if(getId(f).equals(fid_1_2 +" "+fid_2_3)) {
+ c1 = (Feature) f.getPropertyValue("s1");
+ c2 = (Feature) f.getPropertyValue("s2");
+ assertEquals("str3", c1.getProperty("att1").getValue());
+ assertEquals(3, c1.getProperty("att2").getValue());
+ assertEquals(3, c2.getProperty("att3").getValue());
+ assertEquals(40d, c2.getProperty("att4").getValue());
+ } else {
+ fail("unexpected feature");
+ }
+ }
+
+ assertEquals("Was expecting 4 features.",4, count);
+ }
+ }
+
+ /**
+ * Test outer join feature set.
+ */
+ @Test
+ public void testOuterLeft() throws Exception{
+
+ final PropertyIsEqualTo condition = FF.equals(FF.property("att2"),
FF.property("att3"));
+ final FeatureSet col = new JoinFeatureSet(null, featureSet1, "s1",
featureSet2, "s2", JoinFeatureSet.Type.LEFT_OUTER, condition);
+
+ try (Stream<Feature> stream = col.features(false)) {
+ final Iterator<Feature> ite = stream.iterator();
+
+ boolean foundStr1 = false;
+ boolean foundStr20 = false;
+ boolean foundStr21 = false;
+ boolean foundStr3 = false;
+ boolean foundStr50 = false;
+ boolean foundStr51 = false;
+
+ int count = 0;
+ while (ite.hasNext()) {
+ final Feature f = ite.next();
+ final Feature c1 = (Feature) f.getPropertyValue("s1");
+ final Feature c2 = (Feature) f.getPropertyValue("s2");
+ final String att1 =
c1.getProperty("att1").getValue().toString();
+
+ if (att1.equals("str1")) {
+ foundStr1 = true;
+ assertEquals(c1.getProperty("att2").getValue(), 1);
+ assertEquals(c2.getProperty("att3").getValue(), 1);
+ assertEquals(c2.getProperty("att4").getValue(), 10d);
+ } else if(att1.equals("str2")) {
+ assertEquals(c1.getProperty("att2").getValue(), 2);
+ assertEquals(c2.getProperty("att3").getValue(), 2);
+ double att4 = (Double)c2.getProperty("att4").getValue();
+ if(att4 == 20d){
+ foundStr20 = true;
+ }else if(att4 == 30d){
+ foundStr21 = true;
+ }
+ } else if(att1.equals("str3")) {
+ foundStr3 = true;
+ assertEquals(c1.getProperty("att2").getValue(), 3);
+ assertEquals(c2.getProperty("att3").getValue(), 3);
+ assertEquals(c2.getProperty("att4").getValue(), 40d);
+ } else if(att1.equals("str50")) {
+ foundStr50 = true;
+ assertEquals(c1.getProperty("att2").getValue(), 50);
+ assertNull(c2);
+ } else if(att1.equals("str51")) {
+ foundStr51 = true;
+ assertEquals(c1.getProperty("att2").getValue(), 51);
+ assertNull(c2);
+ }
+ count++;
+ }
+
+ assertTrue(foundStr1);
+ assertTrue(foundStr20);
+ assertTrue(foundStr21);
+ assertTrue(foundStr3);
+ assertTrue(foundStr50);
+ assertTrue(foundStr51);
+ assertEquals(6, count);
+ }
+ }
+
+ /**
+ * Test outer join feature set.
+ */
+ @Test
+ public void testOuterRight() throws Exception{
+
+ final PropertyIsEqualTo condition = FF.equals(FF.property("att2"),
FF.property("att3"));
+ final FeatureSet col = new JoinFeatureSet(null, featureSet1, "s1",
featureSet2, "s2", JoinFeatureSet.Type.RIGHT_OUTER, condition);
+
+ try (Stream<Feature> stream = col.features(false)) {
+ final Iterator<Feature> ite = stream.iterator();
+
+ boolean foundStr1 = false;
+ boolean foundStr20 = false;
+ boolean foundStr21 = false;
+ boolean foundStr3 = false;
+ boolean foundStr60 = false;
+ boolean foundStr61 = false;
+
+ int count = 0;
+ while (ite.hasNext()) {
+ final Feature f = ite.next();
+ final Feature c1 = (Feature) f.getPropertyValue("s1");
+ final Feature c2 = (Feature) f.getPropertyValue("s2");
+
+ if (c1 != null) {
+ final Object att1 = c1.getProperty("att1").getValue();
+ if ("str1".equals(att1)) {
+ foundStr1 = true;
+ assertEquals(c1.getProperty("att2").getValue(), 1);
+ assertEquals(c2.getProperty("att3").getValue(), 1);
+ assertEquals(c2.getProperty("att4").getValue(), 10d);
+ } else if("str2".equals(att1)) {
+ assertEquals(c1.getProperty("att2").getValue(), 2);
+ assertEquals(c2.getProperty("att3").getValue(), 2);
+ double att4 =
(Double)c2.getProperty("att4").getValue();
+ if(att4 == 20d){
+ foundStr20 = true;
+ }else if(att4 == 30d){
+ foundStr21 = true;
+ }
+ } else if("str3".equals(att1)) {
+ foundStr3 = true;
+ assertEquals(c1.getProperty("att2").getValue(), 3);
+ assertEquals(c2.getProperty("att3").getValue(), 3);
+ assertEquals(c2.getProperty("att4").getValue(), 40d);
+ }
+ } else {
+ int att3 = (Integer)c2.getProperty("att3").getValue();
+
+ if (att3 == 60) {
+ assertEquals(c2.getProperty("att4").getValue(), 60d);
+ foundStr60 = true;
+ } else if (att3 == 61) {
+ assertEquals(c2.getProperty("att4").getValue(), 61d);
+ foundStr61 = true;
+ }
+ }
+
+ count++;
+ }
+
+ assertTrue(foundStr1);
+ assertTrue(foundStr20);
+ assertTrue(foundStr21);
+ assertTrue(foundStr3);
+ assertTrue(foundStr60);
+ assertTrue(foundStr61);
+ assertEquals(6, count);
+
+ }
+ }
+
+ private static String getId(Feature feature) {
+ return
String.valueOf(feature.getPropertyValue(AttributeConvention.IDENTIFIER_PROPERTY.toString()));
+ }
+}
Modified:
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java?rev=1830206&r1=1830205&r2=1830206&view=diff
==============================================================================
---
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
[UTF-8] (original)
+++
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
[UTF-8] Thu Apr 26 12:42:41 2018
@@ -51,6 +51,7 @@ import org.junit.BeforeClass;
org.apache.sis.internal.storage.csv.StoreTest.class,
org.apache.sis.internal.storage.folder.StoreTest.class,
org.apache.sis.internal.storage.query.SimpleQueryTest.class,
+ org.apache.sis.internal.storage.JoinFeatureSetTest.class,
org.apache.sis.storage.DataStoresTest.class
})
public final strictfp class StorageTestSuite extends TestSuite {