sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jso...@apache.org
Subject svn commit: r1830206 - in /sis/branches/JDK8/storage/sis-storage/src: main/java/org/apache/sis/internal/storage/JoinFeatureSet.java test/java/org/apache/sis/internal/storage/JoinFeatureSetTest.java test/java/org/apache/sis/test/suite/StorageTestSuite.java
Date Thu, 26 Apr 2018 12:42:41 GMT
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 {



Mime
View raw message