This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 8e338b7  Resolve links during the optimization phase of a filter 
expression. It allows SQL statements where we previously missed opportunities.
8e338b7 is described below

commit 8e338b7cf5d7e3737873770b769af0a2664bbbb7
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Sun Dec 26 21:51:30 2021 +0100

    Resolve links during the optimization phase of a filter expression.
    It allows SQL statements where we previously missed opportunities.
---
 .../java/org/apache/sis/filter/PropertyValue.java  | 63 ++++++++++++++++++++--
 .../sis/internal/sql/feature/FeatureStream.java    | 19 +++++--
 .../org/apache/sis/storage/sql/SQLStoreTest.java   | 21 ++++++++
 3 files changed, 96 insertions(+), 7 deletions(-)

diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/filter/PropertyValue.java 
b/core/sis-feature/src/main/java/org/apache/sis/filter/PropertyValue.java
index 594999a..8a20ddc 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/PropertyValue.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/PropertyValue.java
@@ -16,8 +16,10 @@
  */
 package org.apache.sis.filter;
 
+import java.util.Optional;
 import java.util.Collection;
 import java.util.Collections;
+import org.apache.sis.feature.Features;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ObjectConverter;
 import org.apache.sis.util.ObjectConverters;
@@ -45,7 +47,7 @@ import org.opengis.filter.ValueReference;
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
  *
  * @param  <V>  the type of value computed by the expression.
  *
@@ -136,7 +138,9 @@ abstract class PropertyValue<V> extends 
LeafExpression<Feature,V> implements Val
      * An expression fetching property values as {@code Object}.
      * This expression does not need to apply any type conversion.
      */
-    private static final class AsObject extends PropertyValue<Object> {
+    private static final class AsObject extends PropertyValue<Object>
+            implements Optimization.OnExpression<Feature,Object>
+    {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = 2854731969723006038L;
 
@@ -161,6 +165,21 @@ abstract class PropertyValue<V> extends 
LeafExpression<Feature,V> implements Val
             }
             return null;
         }
+
+        /**
+         * If the evaluated property is a link, replaces this expression
+         * by a more direct reference to the target property.
+         */
+        @Override
+        public Expression<Feature,?> optimize(final Optimization optimization) 
{
+            final FeatureType type = optimization.getFeatureType();
+            if (type != null) try {
+                return 
Features.getLinkTarget(type.getProperty(name)).map(AsObject::new).orElse(this);
+            } catch (PropertyNotFoundException e) {
+                warning(e, true);
+            }
+            return this;
+        }
     }
 
 
@@ -189,6 +208,14 @@ abstract class PropertyValue<V> extends 
LeafExpression<Feature,V> implements Val
         }
 
         /**
+         * Creates a new {@code Converted} fetching values for a property of 
different name.
+         * The given name should be the target of a link that the caller has 
resolved.
+         */
+        protected Converted<V> rename(final String target) {
+            return new Converted<>(type, target);
+        }
+
+        /**
          * Returns the type of values computed by this expression.
          */
         @Override
@@ -222,13 +249,25 @@ abstract class PropertyValue<V> extends 
LeafExpression<Feature,V> implements Val
         public final Expression<Feature, ? extends V> optimize(final 
Optimization optimization) {
             final FeatureType featureType = optimization.getFeatureType();
             if (featureType != null) try {
-                final PropertyType property = featureType.getProperty(name);
+                String targetName = name;
+                PropertyType property = featureType.getProperty(targetName);
+                Optional<String> target = Features.getLinkTarget(property);
+                if (target.isPresent()) try {
+                    targetName = target.get();
+                    property = featureType.getProperty(targetName);
+                } catch (PropertyNotFoundException e) {
+                    targetName = name;
+                    warning(e, true);
+                }
                 if (property instanceof AttributeType<?>) {
                     final Class<?> source = ((AttributeType<?>) 
property).getValueClass();
                     if (source != null && source != Object.class && 
!source.isAssignableFrom(getSourceClass())) {
-                        return new CastedAndConverted<>(source, type, name);
+                        return new CastedAndConverted<>(source, type, 
targetName);
                     }
                 }
+                if (!targetName.equals(name)) {
+                    return rename(targetName);
+                }
             } catch (PropertyNotFoundException e) {
                 warning(e, true);
             }
@@ -301,6 +340,22 @@ abstract class PropertyValue<V> extends 
LeafExpression<Feature,V> implements Val
             converter = ObjectConverters.find(source, type);
         }
 
+        /** Creates a new expression derived from an existing one except for 
the target name. */
+        private CastedAndConverted(final CastedAndConverted<S,V> other, final 
String name) {
+            super(other.type, name);
+            source = other.source;
+            converter = other.converter;
+        }
+
+        /**
+         * Creates a new {@code CastedAndConverted} fetching values for a 
property of different name.
+         * The given name should be the target of a link that the caller has 
resolved.
+         */
+        @Override
+        protected Converted<V> rename(final String target) {
+            return new CastedAndConverted<>(this, target);
+        }
+
         /**
          * Returns the type of values fetched from {@link Feature} instance.
          */
diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureStream.java
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureStream.java
index 2b4ab6b..2660249 100644
--- 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureStream.java
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureStream.java
@@ -32,6 +32,7 @@ import org.apache.sis.internal.metadata.sql.SQLBuilder;
 import org.apache.sis.internal.stream.DeferredStream;
 import org.apache.sis.internal.stream.PaginedStream;
 import org.apache.sis.internal.filter.SortByComparator;
+import org.apache.sis.internal.util.Strings;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.util.collection.BackingStoreException;
 import org.apache.sis.util.ArgumentChecks;
@@ -75,7 +76,7 @@ final class FeatureStream extends DeferredStream<Feature> {
     /**
      * The SQL fragment on the right side of the {@code WHERE} keyword.
      * This buffer does not including the {@code WHERE} keyword.
-     * IT is created when first needed.
+     * It is created when first needed and discarded after the iterator is 
created.
      */
     private SelectionClause selection;
 
@@ -392,8 +393,7 @@ final class FeatureStream extends DeferredStream<Feature> {
     @Override
     protected Spliterator<Feature> createSourceIterator() throws Exception {
         final String filter = (selection != null && !selection.isEmpty()) ? 
selection.toString() : null;
-        selection   = null;     // Let the garbage collector do its work.
-        filterToSQL = null;
+        selection = null;             // Let the garbage collector do its work.
 
         final Connection connection = getConnection();
         setCloseHandler(connection);  // Executed only if `FeatureIterator` 
creation fails, discarded later otherwise.
@@ -402,4 +402,17 @@ final class FeatureStream extends DeferredStream<Feature> {
         setCloseHandler(features);
         return features;
     }
+
+    /**
+     * Returns a string representation of this stream for debugging purposes.
+     */
+    @Override
+    public String toString() {
+        return Strings.toString(getClass(), "table", table.name.table,
+                "predicates", hasPredicates ? (filterToSQL != null ? "mixed" : 
"Java") : (filterToSQL != null ? "SQL" : null),
+                "comparator", hasComparator ? (sort != null ? "mixed" : 
"Java") : (sort != null ? "SQL" : null),
+                "distinct",   distinct ? Boolean.TRUE : null,
+                "offset",     offset != 0 ? offset : null,
+                "count",      count  != 0 ? count  : null);
+    }
 }
diff --git 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/storage/sql/SQLStoreTest.java
 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/storage/sql/SQLStoreTest.java
index b4111b1..a1ee95e 100644
--- 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/storage/sql/SQLStoreTest.java
+++ 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/storage/sql/SQLStoreTest.java
@@ -207,6 +207,7 @@ public final strictfp class SQLStoreTest extends TestCase {
             verifySimpleQuerySorting(store);
             verifySimpleQueryWithLimit(store);
             verifySimpleWhere(store);
+            verifyWhereOnLink(store);
             verifyStreamOperations(store.findResource("Cities"));
         }
         canada = null;
@@ -432,6 +433,26 @@ public final strictfp class SQLStoreTest extends TestCase {
     }
 
     /**
+     * Requests a new set of features filtered by a condition on the 
"sis:identifier" property.
+     * The optimizer should replace that link by a condition on the actual 
column.
+     *
+     * @param  dataset  the store on which to query the features.
+     * @throws DataStoreException if an error occurred during query execution.
+     */
+    private void verifyWhereOnLink(SQLStore dataset) throws Exception {
+        final String   desiredProperty = "native_name";
+        final String[] expectedValues  = {"Canada"};
+        final FeatureSet   countries   = dataset.findResource("Countries");
+        final FeatureQuery query       = new FeatureQuery();
+        query.setSelection(FF.equal(FF.property("sis:identifier"), 
FF.literal("CAN")));
+        final Object[] names;
+        try (Stream<Feature> features = 
countries.subset(query).features(false)) {
+            names = features.map(f -> 
f.getPropertyValue(desiredProperty)).toArray();
+        }
+        assertArrayEquals(expectedValues, names);
+    }
+
+    /**
      * Checks that operations stacked on feature stream are well executed.
      * This test focuses on mapping and peeking actions overloaded by SQL 
streams.
      * Operations used here are meaningless; we just want to ensure that the 
pipeline does not skip any operation.

Reply via email to