RYA-392-Datetime-Within. Closes #237.

Project: http://git-wip-us.apache.org/repos/asf/incubator-rya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-rya/commit/29a8e6b7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-rya/tree/29a8e6b7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-rya/diff/29a8e6b7

Branch: refs/heads/master
Commit: 29a8e6b75c0ed903df2da8112f30f4941448582a
Parents: a3b2042
Author: Caleb Meier <[email protected]>
Authored: Wed Oct 4 12:06:55 2017 -0700
Committer: jdasch <[email protected]>
Committed: Thu Oct 12 12:56:26 2017 -0400

----------------------------------------------------------------------
 .../rya/api/functions/DateTimeWithinPeriod.java | 130 ++++++++++++++
 .../org/apache/rya/api/functions/OWLTime.java   | 111 ++++++++++++
 ...f.query.algebra.evaluation.function.Function |  17 ++
 .../api/functions/DateTimeWithinPeriodTest.java | 180 +++++++++++++++++++
 .../pcj/fluo/app/FilterResultUpdater.java       |  14 +-
 .../pcj/fluo/app/util/FilterSerializer.java     |  27 ++-
 .../pcj/fluo/app/util/FilterSerializerTest.java |  48 +++++
 .../indexing/pcj/fluo/integration/QueryIT.java  |  89 +++++++++
 8 files changed, 611 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/29a8e6b7/common/rya.api/src/main/java/org/apache/rya/api/functions/DateTimeWithinPeriod.java
----------------------------------------------------------------------
diff --git 
a/common/rya.api/src/main/java/org/apache/rya/api/functions/DateTimeWithinPeriod.java
 
b/common/rya.api/src/main/java/org/apache/rya/api/functions/DateTimeWithinPeriod.java
new file mode 100644
index 0000000..aedeea7
--- /dev/null
+++ 
b/common/rya.api/src/main/java/org/apache/rya/api/functions/DateTimeWithinPeriod.java
@@ -0,0 +1,130 @@
+/*
+ * 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.rya.api.functions;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.time.Duration;
+import java.time.Instant;
+
+import org.openrdf.model.Literal;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+import org.openrdf.model.ValueFactory;
+import org.openrdf.model.datatypes.XMLDatatypeUtil;
+import org.openrdf.model.vocabulary.FN;
+import org.openrdf.model.vocabulary.XMLSchema;
+import org.openrdf.query.algebra.evaluation.ValueExprEvaluationException;
+import org.openrdf.query.algebra.evaluation.function.Function;
+
+/**
+ * This {@link Function} determines whether two {@link XMLSchema#DATETIME}s 
occur within a specified period of time of
+ * one another. The method {@link Function#evaluate(ValueFactory, Value...)} 
expects four values, where the first two
+ * values are the datetimes, the third value is an integer indicating the 
period, and the fourth value is a URI
+ * indicating the time unit of the period. The URI must be of Type 
DurationDescription in the OWL-Time ontology (see
+ * <a href 
="https://www.w3.org/TR/owl-time/";>https://www.w3.org/TR/owl-time/</a>). 
Examples of valid time unit URIs can
+ * be found in the class {@link OWLTime} and below
+ * <ul>
+ * <li>http://www.w3.org/2006/time#days</li>
+ * <li>http://www.w3.org/2006/time#hours</li>
+ * <li>http://www.w3.org/2006/time#minutes</li>
+ * <li>http://www.w3.org/2006/time#seconds</li>
+ * </ul>
+ *
+ */
+public class DateTimeWithinPeriod implements Function {
+
+    private static final String FUNCTION_URI = FN.NAMESPACE + "dateTimeWithin";
+
+    @Override
+    public String getURI() {
+        return FUNCTION_URI;
+    }
+
+    /**
+     * Determines whether two datetimes occur within a specified period of 
time of one another. This method expects four
+     * values, where the first two values are the datetimes, the third value 
is an integer indicating the period, and
+     * the fourth value is a URI indicating the time unit of the period. The 
URI must be of Type DurationDescription in
+     * the OWL-Time ontology (see <a href 
="https://www.w3.org/TR/owl-time/";>https://www.w3.org/TR/owl-time/</a>).
+     * Examples of valid time unit URIs can be found in the class {@link 
OWLTime} and below
+     * <ul>
+     * <li>http://www.w3.org/2006/time#days</li>
+     * <li>http://www.w3.org/2006/time#hours</li>
+     * <li>http://www.w3.org/2006/time#minutes</li>
+     * <li>http://www.w3.org/2006/time#seconds</li>
+     * </ul>
+     *
+     * @param valueFactory - factory for creating values (not null)
+     * @param values - array of Value arguments for this Function (not null).
+     */
+    @Override
+    public Value evaluate(ValueFactory valueFactory, Value... values) throws 
ValueExprEvaluationException {
+        checkNotNull(valueFactory);
+        checkNotNull(values);
+        try {
+            // general validation of input
+            checkArgument(values.length == 4);
+            checkArgument(values[0] instanceof Literal);
+            checkArgument(values[1] instanceof Literal);
+            checkArgument(values[2] instanceof Literal);
+            checkArgument(values[3] instanceof URI);
+
+            Instant dateTime1 = convertToInstant((Literal) values[0]);
+            Instant dateTime2 = convertToInstant((Literal) values[1]);
+            long periodMillis = convertPeriodToMillis((Literal) values[2], 
(URI) values[3]);
+            long timeBetween = Math.abs(Duration.between(dateTime1, 
dateTime2).toMillis());
+
+            return valueFactory.createLiteral(timeBetween < periodMillis);
+        } catch (Exception e) {
+            throw new ValueExprEvaluationException(e);
+        }
+    }
+
+    private Instant convertToInstant(Literal literal) {
+        String stringVal = literal.getLabel();
+        URI dataType = literal.getDatatype();
+        checkArgument(dataType.equals(XMLSchema.DATETIME) || 
dataType.equals(XMLSchema.DATE),
+                String.format("Invalid data type for date time. Data Type must 
be of type %s or %s .", XMLSchema.DATETIME, XMLSchema.DATE));
+        checkArgument(XMLDatatypeUtil.isValidDateTime(stringVal) || 
XMLDatatypeUtil.isValidDate(stringVal), "Invalid date time value.");
+        return literal.calendarValue().toGregorianCalendar().toInstant();
+    }
+
+    private long convertPeriodToMillis(Literal literal, URI unit) {
+        String stringVal = literal.getLabel();
+        URI dataType = literal.getDatatype();
+        checkArgument(dataType.equals(XMLSchema.INTEGER) || 
dataType.equals(XMLSchema.INT), String
+                .format("Invalid data type for period duration. Data Type must 
be of type %s or %s .", XMLSchema.INTEGER, XMLSchema.INT));
+        checkArgument(XMLDatatypeUtil.isValidInteger(stringVal) || 
XMLDatatypeUtil.isValidInt(stringVal), "Invalid duration value.");
+        return convertToMillis(Integer.parseInt(stringVal), unit);
+    }
+
+    /**
+     * Converts the period duration to milliseconds.
+     *
+     * @param duration - duration of temporal period
+     * @param unit - URI indicating the time unit (URI must be of type 
DurationDescription in the OWL-Time ontology
+     *            indicated by the namespace <http://www.w3.org/2006/time#>)
+     * @return - duration in milliseconds
+     */
+    private long convertToMillis(int duration, URI unit) {
+        checkArgument(duration > 0);
+        return OWLTime.getMillis(duration, unit);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/29a8e6b7/common/rya.api/src/main/java/org/apache/rya/api/functions/OWLTime.java
----------------------------------------------------------------------
diff --git 
a/common/rya.api/src/main/java/org/apache/rya/api/functions/OWLTime.java 
b/common/rya.api/src/main/java/org/apache/rya/api/functions/OWLTime.java
new file mode 100644
index 0000000..5ffc4ee
--- /dev/null
+++ b/common/rya.api/src/main/java/org/apache/rya/api/functions/OWLTime.java
@@ -0,0 +1,111 @@
+/*
+ * 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.rya.api.functions;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.time.temporal.ChronoUnit;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import org.openrdf.model.URI;
+import org.openrdf.model.ValueFactory;
+import org.openrdf.model.impl.ValueFactoryImpl;
+
+/**
+ * Constants for OWL-Time primitives in the OWL-Time namespace.
+ *
+ */
+public class OWLTime {
+
+    private static final ValueFactory FACTORY = ValueFactoryImpl.getInstance();
+
+    /**
+     * Indicates namespace of OWL-Time ontology
+     */
+    public static final String NAMESPACE = "http://www.w3.org/2006/time#";;
+    /**
+     * Seconds class of type DurationDescription in OWL-Time ontology
+     */
+    public static final URI SECONDS_URI = FACTORY.createURI(NAMESPACE, 
"seconds");
+    /**
+     * Minutes class of type DurationDescription in OWL-Time ontology
+     */
+    public static final URI MINUTES_URI = FACTORY.createURI(NAMESPACE, 
"minutes");
+    /**
+     * Hours class of type DurationDescription in OWL-Time ontology
+     */
+    public static final URI HOURS_URI = FACTORY.createURI(NAMESPACE, "hours");
+    /**
+     * Days class of type DurationDescription in OWL-Time ontology
+     */
+    public static final URI DAYS_URI = FACTORY.createURI(NAMESPACE, "days");
+    /**
+     * Weeks class of type DurationDescription in OWL-Time ontology
+     */
+    public static final URI WEEKS_URI = FACTORY.createURI(NAMESPACE, "weeks");
+
+    private static final Map<URI, ChronoUnit> DURATION_MAP = new HashMap<>();
+
+    static {
+        DURATION_MAP.put(SECONDS_URI, ChronoUnit.SECONDS);
+        DURATION_MAP.put(MINUTES_URI, ChronoUnit.MINUTES);
+        DURATION_MAP.put(HOURS_URI, ChronoUnit.HOURS);
+        DURATION_MAP.put(DAYS_URI, ChronoUnit.DAYS);
+        DURATION_MAP.put(WEEKS_URI, ChronoUnit.WEEKS);
+    }
+
+    /**
+     * Verifies whether URI is a valid OWL-Time URI that is supported by this 
class.
+     * @param durationURI - OWLTime URI indicating the time unit (not null)
+     * @return - {@code true} if this URI indicates a supported OWLTime time 
unit
+     */
+    public static boolean isValidDurationType(URI durationURI) {
+        checkNotNull(durationURI);
+        return DURATION_MAP.containsKey(durationURI);
+    }
+
+    /**
+     * Returns the duration in milliseconds
+     *
+     * @param duration - amount of time in the units indicated by the provided 
{@link OWLTime} URI
+     * @param uri - OWLTime URI indicating the time unit of duration (not null)
+     * @return - the amount of time in milliseconds
+     * @throws IllegalArgumentException if provided {@link URI} is not a 
valid, supported OWL-Time time unit.
+     */
+    public static long getMillis(int duration, URI uri) throws 
IllegalArgumentException {
+        Optional<ChronoUnit> unit = getChronoUnitFromURI(uri);
+        checkArgument(unit.isPresent(),
+                String.format("URI %s does not indicate a valid OWLTime time 
unit.  URI must of be of type %s, %s, %s, %s, or %s .", uri,
+                        SECONDS_URI, MINUTES_URI, HOURS_URI, DAYS_URI, 
WEEKS_URI));
+        return duration * unit.get().getDuration().toMillis();
+    }
+
+    /**
+     * Converts the {@link OWLTime} URI time unit to a {@link ChronoUnit} time 
unit
+     *
+     * @param durationURI - OWLTime time unit URI (not null)
+     * @return - corresponding ChronoUnit time unit
+     */
+    public static Optional<ChronoUnit> getChronoUnitFromURI(URI durationURI) {
+        return Optional.ofNullable(DURATION_MAP.get(durationURI));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/29a8e6b7/common/rya.api/src/main/resources/META-INF/services/org.openrdf.query.algebra.evaluation.function.Function
----------------------------------------------------------------------
diff --git 
a/common/rya.api/src/main/resources/META-INF/services/org.openrdf.query.algebra.evaluation.function.Function
 
b/common/rya.api/src/main/resources/META-INF/services/org.openrdf.query.algebra.evaluation.function.Function
new file mode 100644
index 0000000..104a13c
--- /dev/null
+++ 
b/common/rya.api/src/main/resources/META-INF/services/org.openrdf.query.algebra.evaluation.function.Function
@@ -0,0 +1,17 @@
+ # 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.
+org.apache.rya.api.functions.DateTimeWithinPeriod
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/29a8e6b7/common/rya.api/src/test/java/org/apache/rya/api/functions/DateTimeWithinPeriodTest.java
----------------------------------------------------------------------
diff --git 
a/common/rya.api/src/test/java/org/apache/rya/api/functions/DateTimeWithinPeriodTest.java
 
b/common/rya.api/src/test/java/org/apache/rya/api/functions/DateTimeWithinPeriodTest.java
new file mode 100644
index 0000000..0fb0f2a
--- /dev/null
+++ 
b/common/rya.api/src/test/java/org/apache/rya/api/functions/DateTimeWithinPeriodTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.rya.api.functions;
+
+import static org.junit.Assert.assertEquals;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+
+import org.junit.Test;
+import org.openrdf.model.Literal;
+import org.openrdf.model.ValueFactory;
+import org.openrdf.model.impl.ValueFactoryImpl;
+import org.openrdf.query.algebra.evaluation.ValueExprEvaluationException;
+
+public class DateTimeWithinPeriodTest {
+
+    private static final ValueFactory vf = new ValueFactoryImpl();
+    private static final Literal TRUE = vf.createLiteral(true);
+    private static final Literal FALSE = vf.createLiteral(false);
+
+    @Test
+    public void testSeconds() throws DatatypeConfigurationException, 
ValueExprEvaluationException {
+        DatatypeFactory dtf = DatatypeFactory.newInstance();
+
+        ZonedDateTime zTime = ZonedDateTime.now();
+        String time = zTime.format(DateTimeFormatter.ISO_INSTANT);
+
+        ZonedDateTime zTime1 = zTime.minusSeconds(1);
+        String time1 = zTime1.format(DateTimeFormatter.ISO_INSTANT);
+
+        Literal now = vf.createLiteral(dtf.newXMLGregorianCalendar(time));
+        Literal nowMinusOne = 
vf.createLiteral(dtf.newXMLGregorianCalendar(time1));
+
+        DateTimeWithinPeriod func = new DateTimeWithinPeriod();
+
+        assertEquals(TRUE, func.evaluate(vf, now, now, vf.createLiteral(1), 
OWLTime.SECONDS_URI));
+        assertEquals(FALSE, func.evaluate(vf, now, 
nowMinusOne,vf.createLiteral(1), OWLTime.SECONDS_URI));
+        assertEquals(TRUE, func.evaluate(vf, now, 
nowMinusOne,vf.createLiteral(2), OWLTime.SECONDS_URI));
+    }
+
+    @Test
+    public void testMinutes() throws DatatypeConfigurationException, 
ValueExprEvaluationException {
+
+        DatatypeFactory dtf = DatatypeFactory.newInstance();
+
+        ZonedDateTime zTime = ZonedDateTime.now();
+        String time = zTime.format(DateTimeFormatter.ISO_INSTANT);
+
+        ZonedDateTime zTime1 = zTime.minusMinutes(1);
+        String time1 = zTime1.format(DateTimeFormatter.ISO_INSTANT);
+
+        Literal now = vf.createLiteral(dtf.newXMLGregorianCalendar(time));
+        Literal nowMinusOne = 
vf.createLiteral(dtf.newXMLGregorianCalendar(time1));
+
+        DateTimeWithinPeriod func = new DateTimeWithinPeriod();
+
+        assertEquals(TRUE, func.evaluate(vf, now, 
now,vf.createLiteral(1),OWLTime.MINUTES_URI));
+        assertEquals(FALSE, func.evaluate(vf, now, 
nowMinusOne,vf.createLiteral(1),OWLTime.MINUTES_URI));
+        assertEquals(TRUE, func.evaluate(vf, now, 
nowMinusOne,vf.createLiteral(2),OWLTime.MINUTES_URI));
+    }
+
+
+    @Test
+    public void testHours() throws DatatypeConfigurationException, 
ValueExprEvaluationException {
+        DatatypeFactory dtf = DatatypeFactory.newInstance();
+
+        ZonedDateTime zTime = ZonedDateTime.now();
+        String time = zTime.format(DateTimeFormatter.ISO_INSTANT);
+
+        ZonedDateTime zTime1 = zTime.minusHours(1);
+        String time1 = zTime1.format(DateTimeFormatter.ISO_INSTANT);
+
+        Literal now = vf.createLiteral(dtf.newXMLGregorianCalendar(time));
+        Literal nowMinusOne = 
vf.createLiteral(dtf.newXMLGregorianCalendar(time1));
+
+        DateTimeWithinPeriod func = new DateTimeWithinPeriod();
+
+        assertEquals(TRUE, func.evaluate(vf, now, 
now,vf.createLiteral(1),OWLTime.HOURS_URI));
+        assertEquals(FALSE, func.evaluate(vf, now, 
nowMinusOne,vf.createLiteral(1),OWLTime.HOURS_URI));
+        assertEquals(TRUE, func.evaluate(vf, now, 
nowMinusOne,vf.createLiteral(2),OWLTime.HOURS_URI));
+    }
+
+
+    @Test
+    public void testDays() throws DatatypeConfigurationException, 
ValueExprEvaluationException {
+        DatatypeFactory dtf = DatatypeFactory.newInstance();
+
+        ZonedDateTime zTime = ZonedDateTime.now();
+        String time = zTime.format(DateTimeFormatter.ISO_INSTANT);
+
+        ZonedDateTime zTime1 = zTime.minusDays(1);
+        String time1 = zTime1.format(DateTimeFormatter.ISO_INSTANT);
+
+        Literal now = vf.createLiteral(dtf.newXMLGregorianCalendar(time));
+        Literal nowMinusOne = 
vf.createLiteral(dtf.newXMLGregorianCalendar(time1));
+
+        DateTimeWithinPeriod func = new DateTimeWithinPeriod();
+
+        assertEquals(TRUE, func.evaluate(vf, now, now, vf.createLiteral(1), 
OWLTime.DAYS_URI));
+        assertEquals(FALSE, func.evaluate(vf, now, nowMinusOne, 
vf.createLiteral(1), OWLTime.DAYS_URI));
+        assertEquals(TRUE, func.evaluate(vf, now, nowMinusOne, 
vf.createLiteral(2), OWLTime.DAYS_URI));
+    }
+
+    @Test
+    public void testWeeks() throws DatatypeConfigurationException, 
ValueExprEvaluationException {
+        DatatypeFactory dtf = DatatypeFactory.newInstance();
+
+        ZonedDateTime zTime = ZonedDateTime.now();
+        String time = zTime.format(DateTimeFormatter.ISO_INSTANT);
+
+        ZonedDateTime zTime1 = zTime.minusWeeks(1);
+        String time1 = zTime1.format(DateTimeFormatter.ISO_INSTANT);
+
+        ZonedDateTime zTime2 = zTime.minusWeeks(7);
+        String time2 = zTime2.format(DateTimeFormatter.ISO_INSTANT);
+
+        Literal now = vf.createLiteral(dtf.newXMLGregorianCalendar(time));
+        Literal nowMinusOne = 
vf.createLiteral(dtf.newXMLGregorianCalendar(time1));
+        Literal nowMinusSeven = 
vf.createLiteral(dtf.newXMLGregorianCalendar(time2));
+
+        DateTimeWithinPeriod func = new DateTimeWithinPeriod();
+
+        assertEquals(TRUE, func.evaluate(vf, now, now, vf.createLiteral(1), 
OWLTime.WEEKS_URI));
+        assertEquals(FALSE, func.evaluate(vf, now, nowMinusOne, 
vf.createLiteral(1), OWLTime.WEEKS_URI));
+        assertEquals(TRUE, func.evaluate(vf, now, nowMinusOne, 
vf.createLiteral(2), OWLTime.WEEKS_URI));
+        assertEquals(FALSE, func.evaluate(vf, now, nowMinusSeven, 
vf.createLiteral(7), OWLTime.WEEKS_URI));
+    }
+
+    @Test
+    public void testTimeZone() throws DatatypeConfigurationException, 
ValueExprEvaluationException {
+        DatatypeFactory dtf = DatatypeFactory.newInstance();
+
+        ZonedDateTime now = ZonedDateTime.now();
+        String time = now.format(DateTimeFormatter.ISO_INSTANT);
+
+        ZonedDateTime zTime1 = 
now.withZoneSameInstant(ZoneId.of("Europe/London"));
+        String time1 = zTime1.format(DateTimeFormatter.ISO_INSTANT);
+
+        ZonedDateTime zTime2 = 
now.withZoneSameInstant(ZoneId.of("Australia/Sydney"));
+        String time2 = zTime2.format(DateTimeFormatter.ISO_INSTANT);
+
+        ZonedDateTime zTime3 = 
now.minusDays(1).withZoneSameInstant(ZoneId.of("Asia/Seoul"));
+        String time3 = zTime3.format(DateTimeFormatter.ISO_INSTANT);
+
+        Literal nowLocal = vf.createLiteral(dtf.newXMLGregorianCalendar(time));
+        Literal nowEuropeTZ = 
vf.createLiteral(dtf.newXMLGregorianCalendar(time1));
+        Literal nowAustraliaTZ = 
vf.createLiteral(dtf.newXMLGregorianCalendar(time2));
+        Literal nowAsiaTZMinusOne = 
vf.createLiteral(dtf.newXMLGregorianCalendar(time3));
+
+        DateTimeWithinPeriod func = new DateTimeWithinPeriod();
+
+        assertEquals(TRUE, func.evaluate(vf, nowLocal, nowEuropeTZ, 
vf.createLiteral(1), OWLTime.SECONDS_URI));
+        assertEquals(TRUE, func.evaluate(vf, nowLocal, nowAustraliaTZ, 
vf.createLiteral(1), OWLTime.SECONDS_URI));
+        assertEquals(FALSE, func.evaluate(vf, nowLocal, nowAsiaTZMinusOne, 
vf.createLiteral(1), OWLTime.DAYS_URI));
+        assertEquals(TRUE, func.evaluate(vf, nowLocal, nowAsiaTZMinusOne, 
vf.createLiteral(2), OWLTime.DAYS_URI));
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/29a8e6b7/extras/rya.pcj.fluo/pcj.fluo.app/src/main/java/org/apache/rya/indexing/pcj/fluo/app/FilterResultUpdater.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.pcj.fluo/pcj.fluo.app/src/main/java/org/apache/rya/indexing/pcj/fluo/app/FilterResultUpdater.java
 
b/extras/rya.pcj.fluo/pcj.fluo.app/src/main/java/org/apache/rya/indexing/pcj/fluo/app/FilterResultUpdater.java
index 7cfa216..17ed158 100644
--- 
a/extras/rya.pcj.fluo/pcj.fluo.app/src/main/java/org/apache/rya/indexing/pcj/fluo/app/FilterResultUpdater.java
+++ 
b/extras/rya.pcj.fluo/pcj.fluo.app/src/main/java/org/apache/rya/indexing/pcj/fluo/app/FilterResultUpdater.java
@@ -39,9 +39,11 @@ import org.openrdf.model.impl.ValueFactoryImpl;
 import org.openrdf.query.BindingSet;
 import org.openrdf.query.QueryEvaluationException;
 import org.openrdf.query.algebra.Filter;
+import org.openrdf.query.algebra.FunctionCall;
 import org.openrdf.query.algebra.ValueExpr;
 import org.openrdf.query.algebra.evaluation.TripleSource;
 import org.openrdf.query.algebra.evaluation.ValueExprEvaluationException;
+import org.openrdf.query.algebra.evaluation.function.FunctionRegistry;
 import org.openrdf.query.algebra.evaluation.impl.EvaluationStrategyImpl;
 import org.openrdf.query.algebra.evaluation.util.QueryEvaluationUtil;
 
@@ -130,16 +132,20 @@ public class FilterResultUpdater {
      * @param condition - The filter condition. (not null)
      * @param bindings - The binding set to evaluate. (not null)
      * @return {@code true} if the binding set is accepted by the filter; 
otherwise {@code false}.
-     * @throws QueryEvaluationException The condition couldn't be evaluated.
+     * @throws QueryEvaluationException The condition couldn't be evaluated. 
In the case that the ValueExpr is a
+     *             {@link FunctionCall}, this Exception is thrown because the 
Function could not be found in the
+     *             {@link FunctionRegistry}.
      */
     private static boolean isTrue(final ValueExpr condition, final BindingSet 
bindings) throws QueryEvaluationException {
         try {
             final Value value = evaluator.evaluate(condition, bindings);
             return QueryEvaluationUtil.getEffectiveBooleanValue(value);
         } catch (final ValueExprEvaluationException e) {
-            // XXX Hack: If filtering a statement that does not have the right 
bindings, return true.
-            //           When would this ever come up? Should we actually 
return true?
-            return true;
+            //False returned because for whatever reason, the ValueExpr could 
not be evaluated.
+            //In the event that the ValueExpr is a FunctionCall, this 
Exception will be generated if
+            //the Function URI is a valid URI that was found in the 
FunctionRegistry, but the arguments
+            //for that Function could not be parsed.
+            return false;
         }
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/29a8e6b7/extras/rya.pcj.fluo/pcj.fluo.app/src/main/java/org/apache/rya/indexing/pcj/fluo/app/util/FilterSerializer.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.pcj.fluo/pcj.fluo.app/src/main/java/org/apache/rya/indexing/pcj/fluo/app/util/FilterSerializer.java
 
b/extras/rya.pcj.fluo/pcj.fluo.app/src/main/java/org/apache/rya/indexing/pcj/fluo/app/util/FilterSerializer.java
index 73f3447..6c99809 100644
--- 
a/extras/rya.pcj.fluo/pcj.fluo.app/src/main/java/org/apache/rya/indexing/pcj/fluo/app/util/FilterSerializer.java
+++ 
b/extras/rya.pcj.fluo/pcj.fluo.app/src/main/java/org/apache/rya/indexing/pcj/fluo/app/util/FilterSerializer.java
@@ -23,6 +23,8 @@ import java.util.Set;
 
 import org.openrdf.query.algebra.Filter;
 import org.openrdf.query.algebra.SingletonSet;
+import org.openrdf.query.algebra.evaluation.function.Function;
+import org.openrdf.query.algebra.evaluation.function.FunctionRegistry;
 import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
 import org.openrdf.query.parser.ParsedQuery;
 import org.openrdf.query.parser.ParsedTupleQuery;
@@ -52,7 +54,7 @@ public class FilterSerializer {
         Filter clone = filter.clone();
         clone.setArg(new SingletonSet());
         try {
-            return renderer.render(new ParsedTupleQuery(clone));
+            return 
removeAngularBracketsFromNonUriFunctions(renderer.render(new 
ParsedTupleQuery(clone)));
         } catch (Exception e) {
             throw new FilterParseException("Unable to parse Filter.", e);
         }
@@ -98,6 +100,29 @@ public class FilterSerializer {
         public void meet(Filter node) {
             filters.add(node);
         }
+        
+    }
+    
+    /**
+     * There are a number of Functions in the FunctionRegistry whose getURI() 
method does not return a valid URI (NOW()
+     * is one such method). The SPARQLQueryRender adds angular brackets to the 
result returned by
+     * {@link Function#getURI()} by default, which leads to a 
MalformedQueryException when the SPARQLParser attempts to
+     * parse the SPARQL created by the renderer. Therefore, a call to 
serialize and then deserialize for a Filter
+     * containing a Function that returns an invalid URI will generate an 
exception. This method removes the angular
+     * brackets from the result returned by {@link Function#getURI()} if it is 
not a valid URI so that the parser will
+     * parse it.
+     * 
+     * @param query - query generated by query renderer
+     * @return - String with angular brackets removed from around all invalid 
Function URI
+     */
+    private static String removeAngularBracketsFromNonUriFunctions(String 
query) {
+        FunctionRegistry registry = FunctionRegistry.getInstance();
+        for(String key: registry.getKeys()) {
+            if (key.indexOf(':') < 0) {
+                query = query.replace("<"+key.trim()+">", key);
+            }
+        }
+        return query;
     }
     
     public static class FilterParseException extends Exception {

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/29a8e6b7/extras/rya.pcj.fluo/pcj.fluo.app/src/test/java/org/apache/rya/indexing/pcj/fluo/app/util/FilterSerializerTest.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.pcj.fluo/pcj.fluo.app/src/test/java/org/apache/rya/indexing/pcj/fluo/app/util/FilterSerializerTest.java
 
b/extras/rya.pcj.fluo/pcj.fluo.app/src/test/java/org/apache/rya/indexing/pcj/fluo/app/util/FilterSerializerTest.java
new file mode 100644
index 0000000..b2efa96
--- /dev/null
+++ 
b/extras/rya.pcj.fluo/pcj.fluo.app/src/test/java/org/apache/rya/indexing/pcj/fluo/app/util/FilterSerializerTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.rya.indexing.pcj.fluo.app.util;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.openrdf.query.algebra.Filter;
+import org.openrdf.query.algebra.Projection;
+import org.openrdf.query.parser.ParsedQuery;
+import org.openrdf.query.parser.sparql.SPARQLParser;
+
+public class FilterSerializerTest {
+
+    @Test
+    public void nowTest() throws Exception {
+
+        //tests to see if NOW function is correctly serialized and deserialized
+        //by FilterSerializer
+        String query = "select * {Filter(NOW())}";
+        SPARQLParser parser = new SPARQLParser();
+        ParsedQuery pq = parser.parseQuery(query, null);
+        Filter filter = (Filter) ((Projection) pq.getTupleExpr()).getArg();
+        String filterString = FilterSerializer.serialize(filter);
+        Filter deserializedFilter = FilterSerializer.deserialize(filterString);
+        
+        assertEquals(filter, deserializedFilter);
+
+    }
+    
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/29a8e6b7/extras/rya.pcj.fluo/pcj.fluo.integration/src/test/java/org/apache/rya/indexing/pcj/fluo/integration/QueryIT.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.pcj.fluo/pcj.fluo.integration/src/test/java/org/apache/rya/indexing/pcj/fluo/integration/QueryIT.java
 
b/extras/rya.pcj.fluo/pcj.fluo.integration/src/test/java/org/apache/rya/indexing/pcj/fluo/integration/QueryIT.java
index 4974aee..beaef32 100644
--- 
a/extras/rya.pcj.fluo/pcj.fluo.integration/src/test/java/org/apache/rya/indexing/pcj/fluo/integration/QueryIT.java
+++ 
b/extras/rya.pcj.fluo/pcj.fluo.integration/src/test/java/org/apache/rya/indexing/pcj/fluo/integration/QueryIT.java
@@ -37,6 +37,8 @@ import org.apache.fluo.core.client.FluoClientImpl;
 import org.apache.rya.api.client.CreatePCJ.ExportStrategy;
 import org.apache.rya.api.client.RyaClient;
 import org.apache.rya.api.client.accumulo.AccumuloRyaClientFactory;
+import org.apache.rya.api.functions.DateTimeWithinPeriod;
+import org.apache.rya.api.functions.OWLTime;
 import org.apache.rya.indexing.pcj.fluo.api.CreateFluoPcj;
 import org.apache.rya.indexing.pcj.fluo.app.query.UnsupportedQueryException;
 import org.apache.rya.indexing.pcj.storage.PeriodicQueryResultStorage;
@@ -54,6 +56,7 @@ import org.openrdf.model.ValueFactory;
 import org.openrdf.model.datatypes.XMLDatatypeUtil;
 import org.openrdf.model.impl.BooleanLiteralImpl;
 import org.openrdf.model.impl.ValueFactoryImpl;
+import org.openrdf.model.vocabulary.FN;
 import org.openrdf.model.vocabulary.XMLSchema;
 import org.openrdf.query.BindingSet;
 import org.openrdf.query.algebra.evaluation.ValueExprEvaluationException;
@@ -423,6 +426,92 @@ public class QueryIT extends RyaExportITBase {
         runTest(sparql, statements, expectedResults, ExportStrategy.RYA);
     }
 
+    
+    @Test
+    public void dateTimeWithin() throws Exception {
+        
+        final ValueFactory vf = new ValueFactoryImpl();
+        DatatypeFactory dtf = DatatypeFactory.newInstance();
+        FunctionRegistry.getInstance().add(new DateTimeWithinPeriod());
+
+        final String sparql = "PREFIX fn: <" + FN.NAMESPACE +">"
+                + "SELECT ?event ?startTime ?endTime WHERE { ?event 
<uri:startTime> ?startTime; <uri:endTime> ?endTime. "
+                + "FILTER(fn:dateTimeWithin(?startTime, ?endTime, 2,<" + 
OWLTime.HOURS_URI + "> ))}";
+        
+        ZonedDateTime zTime = ZonedDateTime.now();
+        String time = zTime.format(DateTimeFormatter.ISO_INSTANT);
+
+        ZonedDateTime zTime1 = zTime.minusHours(1);
+        String time1 = zTime1.format(DateTimeFormatter.ISO_INSTANT);
+        
+        ZonedDateTime zTime2 = zTime.minusHours(2);
+        String time2 = zTime2.format(DateTimeFormatter.ISO_INSTANT);
+
+        Literal lit = vf.createLiteral(dtf.newXMLGregorianCalendar(time));
+        Literal lit1 = vf.createLiteral(dtf.newXMLGregorianCalendar(time1));
+        Literal lit2 = vf.createLiteral(dtf.newXMLGregorianCalendar(time2));
+
+        // Create the Statements that will be loaded into Rya.
+        final Collection<Statement> statements = Sets.newHashSet(
+                vf.createStatement(vf.createURI("uri:event1"), 
vf.createURI("uri:startTime"), lit),
+                vf.createStatement(vf.createURI("uri:event1"), 
vf.createURI("uri:endTime"), lit1),
+                vf.createStatement(vf.createURI("uri:event2"), 
vf.createURI("uri:startTime"), lit),
+                vf.createStatement(vf.createURI("uri:event2"), 
vf.createURI("uri:endTime"), lit2)
+               );
+
+        // Create the expected results of the SPARQL query once the PCJ has 
been computed.
+        final Set<BindingSet> expectedResults = new HashSet<>();
+
+        MapBindingSet bs = new MapBindingSet();
+        bs.addBinding("event", vf.createURI("uri:event1"));
+        bs.addBinding("startTime", lit);
+        bs.addBinding("endTime", lit1);
+        expectedResults.add(bs);
+
+        // Verify the end results of the query match the expected results.
+        runTest(sparql, statements, expectedResults, ExportStrategy.RYA);
+    }
+    
+    @Test
+    public void dateTimeWithinNow() throws Exception {
+        
+        final ValueFactory vf = new ValueFactoryImpl();
+        DatatypeFactory dtf = DatatypeFactory.newInstance();
+        FunctionRegistry.getInstance().add(new DateTimeWithinPeriod());
+
+        final String sparql = "PREFIX fn: <" + FN.NAMESPACE +">"
+                + "SELECT ?event ?startTime WHERE { ?event <uri:startTime> 
?startTime. "
+                + "FILTER(fn:dateTimeWithin(?startTime, NOW(), 15, <" + 
OWLTime.SECONDS_URI + "> ))}";
+        
+        ZonedDateTime zTime = ZonedDateTime.now();
+        String time = zTime.format(DateTimeFormatter.ISO_INSTANT);
+
+        ZonedDateTime zTime1 = zTime.minusSeconds(30);
+        String time1 = zTime1.format(DateTimeFormatter.ISO_INSTANT);
+        
+        Literal lit = vf.createLiteral(dtf.newXMLGregorianCalendar(time));
+        Literal lit1 = vf.createLiteral(dtf.newXMLGregorianCalendar(time1));
+
+        // Create the Statements that will be loaded into Rya.
+        final Collection<Statement> statements = Sets.newHashSet(
+                vf.createStatement(vf.createURI("uri:event1"), 
vf.createURI("uri:startTime"), lit),
+                vf.createStatement(vf.createURI("uri:event2"), 
vf.createURI("uri:startTime"), lit1)
+               );
+
+        // Create the expected results of the SPARQL query once the PCJ has 
been computed.
+        final Set<BindingSet> expectedResults = new HashSet<>();
+
+        MapBindingSet bs = new MapBindingSet();
+        bs.addBinding("event", vf.createURI("uri:event1"));
+        bs.addBinding("startTime", lit);
+        expectedResults.add(bs);
+
+        // Verify the end results of the query match the expected results.
+        runTest(sparql, statements, expectedResults, ExportStrategy.RYA);
+    }
+
+
+    
     @Test
     public void periodicQueryTestWithoutAggregation() throws Exception {
         String query = "prefix function: <http://org.apache.rya/function#> " 
// n


Reply via email to