Repository: incubator-rya
Updated Branches:
  refs/heads/master f8d02eb3d -> 646d21b4e


RYA-240 GeoTemporal framework

Interfaces and integration test for geotemporal indexing.

The Integration test is incomplete as the expected is
currently unknown.


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

Branch: refs/heads/master
Commit: af5964aa72ebb3436666929a57a5ee7469732093
Parents: f8d02eb
Author: me@devbox.(none) <me@devbox.(none)>
Authored: Wed Jan 18 12:48:11 2017 -0500
Committer: Aaron Mihalik <[email protected]>
Committed: Wed Jun 14 13:27:42 2017 -0400

----------------------------------------------------------------------
 .../GeoTemporalExternalSetMatcherFactory.java   |  44 +++
 .../geotemporal/GeoTemporalIndexException.java  |  57 ++++
 .../GeoTemporalIndexSetProvider.java            | 251 ++++++++++++++++
 .../geotemporal/GeoTemporalIndexer.java         | 142 ++++++++++
 .../geotemporal/GeoTemporalIndexerFactory.java  |  53 ++++
 .../geotemporal/GeoTemporalOptimizer.java       |  69 +++++
 .../GeoTemporalToSegmentConverter.java          |  51 ++++
 .../rya/indexing/geotemporal/model/Event.java   | 218 ++++++++++++++
 .../geotemporal/model/EventQueryNode.java       | 283 +++++++++++++++++++
 .../geotemporal/storage/EventStorage.java       | 130 +++++++++
 10 files changed, 1298 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/af5964aa/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalExternalSetMatcherFactory.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalExternalSetMatcherFactory.java
 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalExternalSetMatcherFactory.java
new file mode 100644
index 0000000..c4a287e
--- /dev/null
+++ 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalExternalSetMatcherFactory.java
@@ -0,0 +1,44 @@
+/**
+ * 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.geotemporal;
+
+import 
org.apache.rya.indexing.external.matching.AbstractExternalSetMatcherFactory;
+import org.apache.rya.indexing.external.matching.ExternalSetMatcher;
+import org.apache.rya.indexing.external.matching.JoinSegment;
+import org.apache.rya.indexing.external.matching.JoinSegmentMatcher;
+import org.apache.rya.indexing.external.matching.OptionalJoinSegment;
+import org.apache.rya.indexing.external.matching.OptionalJoinSegmentMatcher;
+import org.apache.rya.indexing.geotemporal.model.EventQueryNode;
+
+/**
+ * Factory used to build {@link EntityQueryNodeMatcher}s for the {@link 
GeoTemporalIndexOptimizer}.
+ *
+ */
+public class GeoTemporalExternalSetMatcherFactory extends 
AbstractExternalSetMatcherFactory<EventQueryNode> {
+
+    @Override
+    protected ExternalSetMatcher<EventQueryNode> getJoinSegmentMatcher(final 
JoinSegment<EventQueryNode> segment) {
+        return new JoinSegmentMatcher<EventQueryNode>(segment, new 
GeoTemporalToSegmentConverter());
+    }
+
+    @Override
+    protected ExternalSetMatcher<EventQueryNode> 
getOptionalJoinSegmentMatcher(final OptionalJoinSegment<EventQueryNode> 
segment) {
+        return new OptionalJoinSegmentMatcher<EventQueryNode>(segment, new 
GeoTemporalToSegmentConverter());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/af5964aa/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexException.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexException.java
 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexException.java
new file mode 100644
index 0000000..b2d4de5
--- /dev/null
+++ 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexException.java
@@ -0,0 +1,57 @@
+/**
+ * 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.geotemporal;
+
+import org.apache.rya.indexing.entity.model.TypedEntity;
+
+/**
+ * An operation over the {@link TypedEntity} index failed to complete.
+ */
+public class GeoTemporalIndexException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Constructs a new exception with the specified detail message.  The
+     * cause is not initialized, and may subsequently be initialized by
+     * a call to {@link #initCause}.
+     *
+     * @param   message   the detail message. The detail message is saved for
+     *          later retrieval by the {@link #getMessage()} method.
+     */
+    public GeoTemporalIndexException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message and
+     * cause.  <p>Note that the detail message associated with
+     * {@code cause} is <i>not</i> automatically incorporated in
+     * this exception's detail message.
+     *
+     * @param  message the detail message (which is saved for later retrieval
+     *         by the {@link #getMessage()} method).
+     * @param  cause the cause (which is saved for later retrieval by the
+     *         {@link #getCause()} method).  (A <tt>null</tt> value is
+     *         permitted, and indicates that the cause is nonexistent or
+     *         unknown.)
+     */
+    public GeoTemporalIndexException(final String message, final Throwable 
cause) {
+        super(message, cause);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/af5964aa/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexSetProvider.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexSetProvider.java
 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexSetProvider.java
new file mode 100644
index 0000000..38790c4
--- /dev/null
+++ 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexSetProvider.java
@@ -0,0 +1,251 @@
+/**
+ * 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.geotemporal;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.rya.indexing.IndexingExpr;
+import org.apache.rya.indexing.IndexingFunctionRegistry;
+import org.apache.rya.indexing.IndexingFunctionRegistry.FUNCTION_TYPE;
+import org.apache.rya.indexing.accumulo.geo.GeoTupleSet;
+import org.apache.rya.indexing.external.matching.ExternalSetProvider;
+import org.apache.rya.indexing.external.matching.QuerySegment;
+import org.apache.rya.indexing.geotemporal.model.EventQueryNode;
+import org.apache.rya.indexing.geotemporal.storage.EventStorage;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+import org.openrdf.model.impl.URIImpl;
+import org.openrdf.query.algebra.FunctionCall;
+import org.openrdf.query.algebra.QueryModelNode;
+import org.openrdf.query.algebra.StatementPattern;
+import org.openrdf.query.algebra.ValueConstant;
+import org.openrdf.query.algebra.ValueExpr;
+import org.openrdf.query.algebra.Var;
+import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
+/**
+ * Provides {@link GeoTupleSet}s.
+ */
+public class GeoTemporalIndexSetProvider implements 
ExternalSetProvider<EventQueryNode> {
+    //organzied by object var.  Each object is a filter, or set of filters
+    private Multimap<Var, IndexingExpr> filterMap;
+
+    //organzied by subject var.  Each subject is a GeoTemporalTupleSet
+    private Multimap<Var, StatementPattern> patternMap;
+
+    //filters that have not been constrained by statement patterns into 
indexing expressions yet.
+    private Multimap<Var, FunctionCall> unmatchedFilters;
+    //filters that have been used, to be used by the matcher later.
+    private Multimap<Var, FunctionCall> matchedFilters;
+
+    //organzied by object var.  Used to find matches between unmatch filters 
and patterns
+    private Map<Var, StatementPattern> objectPatterns;
+
+
+    private static URI filterURI;
+
+    private final EventStorage eventStorage;
+
+    public GeoTemporalIndexSetProvider(final EventStorage eventStorage) {
+        this.eventStorage = requireNonNull(eventStorage);
+    }
+
+    @Override
+    public List<EventQueryNode> getExternalSets(final 
QuerySegment<EventQueryNode> node) {
+        filterMap = HashMultimap.create();
+        patternMap = HashMultimap.create();
+        unmatchedFilters = HashMultimap.create();
+        matchedFilters = HashMultimap.create();
+
+        objectPatterns = new HashMap<>();
+        //discover entities
+        buildMaps(node);
+        final List<EventQueryNode> nodes = createNodes();
+
+        return nodes;
+    }
+
+    private List<EventQueryNode> createNodes() {
+        final List<EventQueryNode> nodes = new ArrayList<>();
+        for(final Var subj : patternMap.keySet()) {
+            final EventQueryNode node = getGeoTemporalNode(subj);
+            if(node != null) {
+                nodes.add(node);
+            }
+        }
+        return nodes;
+    }
+
+    private EventQueryNode getGeoTemporalNode(final Var subj) {
+        final Collection<StatementPattern> patterns = patternMap.get(subj);
+        final Collection<FunctionCall> usedFilters = new ArrayList<>();
+        Optional<StatementPattern> geoPattern = Optional.empty();
+        Optional<StatementPattern> temporalPattern = Optional.empty();
+        Optional<Collection<IndexingExpr>> geoFilters = Optional.empty();
+        Optional<Collection<IndexingExpr>> temporalFilters = Optional.empty();
+
+        //should only be 2 patterns.
+        for(final StatementPattern sp : patterns) {
+            final Var obj = sp.getObjectVar();
+
+            ///filter map does not have -const-
+
+
+            if(filterMap.containsKey(obj)) {
+                final Collection<IndexingExpr> filters = filterMap.get(obj);
+                final IndexingFunctionRegistry.FUNCTION_TYPE type = 
ensureSameType(filters);
+                if(type != null && type == FUNCTION_TYPE.GEO) {
+                    geoPattern = Optional.of(sp);
+                    geoFilters = Optional.of(filters);
+                    usedFilters.addAll(matchedFilters.get(obj));
+                } else if(type != null && type == FUNCTION_TYPE.TEMPORAL) {
+                    temporalPattern = Optional.of(sp);
+                    temporalFilters = Optional.of(filters);
+                    usedFilters.addAll(matchedFilters.get(obj));
+                } else {
+                    return null;
+                }
+            } else {
+                return null;
+            }
+        }
+
+        if(geoFilters.isPresent() && temporalFilters.isPresent() && 
geoPattern.isPresent() && temporalPattern.isPresent()) {
+            return new EventQueryNode(eventStorage, geoPattern.get(), 
temporalPattern.get(), geoFilters.get(), temporalFilters.get(), usedFilters);
+        } else {
+            return null;
+        }
+    }
+
+    private FUNCTION_TYPE ensureSameType(final Collection<IndexingExpr> 
filters) {
+        FUNCTION_TYPE type = null;
+        for(final IndexingExpr filter : filters) {
+            if(type == null) {
+                type = 
IndexingFunctionRegistry.getFunctionType(filter.getFunction());
+            } else {
+                
if(IndexingFunctionRegistry.getFunctionType(filter.getFunction()) != type) {
+                    return null;
+                }
+            }
+        }
+        return type;
+    }
+
+    private void buildMaps(final QuerySegment<EventQueryNode> node) {
+        final List<QueryModelNode> unused = new ArrayList<>();
+        for (final QueryModelNode pattern : node.getOrderedNodes()) {
+            if(pattern instanceof FunctionCall) {
+                discoverFilter((FunctionCall) pattern, unused);
+            }
+            if(pattern instanceof StatementPattern) {
+                discoverPatterns((StatementPattern) pattern, unused);
+            }
+        }
+    }
+
+    private void discoverFilter(final FunctionCall filter, final 
List<QueryModelNode> unmatched) {
+        try {
+            filter.visit(new FilterVisitor());
+        } catch (final Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void discoverPatterns(final StatementPattern pattern, final 
List<QueryModelNode> unmatched) {
+        final Var subj = pattern.getSubjectVar();
+        final Var objVar = pattern.getObjectVar();
+
+        patternMap.put(subj, pattern);
+        objectPatterns.put(objVar, pattern);
+        //check for existing filters.
+        if(unmatchedFilters.containsKey(objVar)) {
+            final Collection<FunctionCall> calls = 
unmatchedFilters.removeAll(objVar);
+            for(final FunctionCall call : calls) {
+                addFilter(call);
+                matchedFilters.put(objVar, call);
+            }
+        }
+    }
+
+    @Override
+    public Iterator<List<EventQueryNode>> getExternalSetCombos(final 
QuerySegment<EventQueryNode> segment) {
+        final List<List<EventQueryNode>> comboList = new ArrayList<>();
+        comboList.add(getExternalSets(segment));
+        return comboList.iterator();
+    }
+
+    private void addFilter(final FunctionCall call) {
+        filterURI = new URIImpl(call.getURI());
+        final Var objVar = 
IndexingFunctionRegistry.getResultVarFromFunctionCall(filterURI, 
call.getArgs());
+        filterMap.put(objVar, new IndexingExpr(filterURI, 
objectPatterns.get(objVar), extractArguments(objVar.getName(), call)));
+    }
+
+    private Value[] extractArguments(final String matchName, final 
FunctionCall call) {
+        final Value args[] = new Value[call.getArgs().size() - 1];
+        int argI = 0;
+        for (int i = 0; i != call.getArgs().size(); ++i) {
+            final ValueExpr arg = call.getArgs().get(i);
+            if (argI == i && arg instanceof Var && 
matchName.equals(((Var)arg).getName())) {
+                continue;
+            }
+            if (arg instanceof ValueConstant) {
+                args[argI] = ((ValueConstant)arg).getValue();
+            } else if (arg instanceof Var && ((Var)arg).hasValue()) {
+                args[argI] = ((Var)arg).getValue();
+            } else {
+                throw new IllegalArgumentException("Query error: Found " + arg 
+ ", expected a Literal, BNode or URI");
+            }
+            ++argI;
+        }
+        return args;
+    }
+
+    /**
+     * Finds the object/function in a Filter.  If the associated statement 
pattern
+     * has been found, creates the {@link IndexingExpr} and adds it to the map.
+     */
+    private class FilterVisitor extends QueryModelVisitorBase<Exception> {
+        @Override
+        public void meet(final FunctionCall call) throws Exception {
+
+            filterURI = new URIImpl(call.getURI());
+            final FUNCTION_TYPE type = 
IndexingFunctionRegistry.getFunctionType(filterURI);
+            if(type == FUNCTION_TYPE.GEO || type == FUNCTION_TYPE.TEMPORAL) {
+                final Var objVar = 
IndexingFunctionRegistry.getResultVarFromFunctionCall(filterURI, 
call.getArgs());
+                if(objectPatterns.containsKey(objVar)) {
+                    filterMap.put(objVar, new IndexingExpr(filterURI, 
objectPatterns.get(objVar), extractArguments(objVar.getName(), call)));
+                    matchedFilters.put(objVar, call);
+                } else {
+                    unmatchedFilters.put(objVar, call);
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/af5964aa/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexer.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexer.java
 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexer.java
new file mode 100644
index 0000000..01b254b
--- /dev/null
+++ 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexer.java
@@ -0,0 +1,142 @@
+/**
+ * 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.geotemporal;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.rya.api.persist.index.RyaSecondaryIndexer;
+import org.apache.rya.indexing.GeoConstants;
+import org.apache.rya.indexing.geotemporal.storage.EventStorage;
+import org.openrdf.model.Statement;
+import org.openrdf.model.URI;
+import org.openrdf.model.impl.URIImpl;
+
+/**
+ * A repository to store, index, and retrieve {@link Statement}s based on 
geotemporal features.
+ */
+public interface GeoTemporalIndexer extends RyaSecondaryIndexer {
+
+    /**
+     * Creates the {@link Eventtorage} that will be used by the indexer.
+     *
+     * @param conf - Indicates how the {@link EventStorage} is initialized. 
(not null)
+     * @return The {@link EventStorage} that will be used by this indexer.
+     */
+    public abstract EventStorage getEventStorage(final Configuration conf);
+
+    public enum GeoPolicy {
+        EQUALS(GeoConstants.GEO_SF_EQUALS),
+        DISJOINT(GeoConstants.GEO_SF_DISJOINT),
+        INTERSECTS(GeoConstants.GEO_SF_INTERSECTS),
+        TOUCHES(GeoConstants.GEO_SF_TOUCHES),
+        CROSSES(GeoConstants.GEO_SF_CROSSES),
+        WITHIN(GeoConstants.GEO_SF_WITHIN),
+        CONTAINS(GeoConstants.GEO_SF_CONTAINS),
+        OVERLAPS(GeoConstants.GEO_SF_OVERLAPS);
+
+        private final URI uri;
+
+        private GeoPolicy(final URI uri) {
+            this.uri = uri;
+        }
+
+        public URI getURI() {
+            return uri;
+        }
+
+        public static GeoPolicy fromURI(final URI uri) {
+            for(final GeoPolicy policy : GeoPolicy.values()) {
+                if(policy.getURI().equals(uri)) {
+                    return policy;
+                }
+            }
+            return null;
+        }
+    }
+
+    String TEMPORAL_NS = "tag:rya-rdf.org,2015:temporal#";
+    /**
+     * All of the filter functions that can be used in a temporal based query.
+     * <p>
+     */
+    public enum TemporalPolicy {
+        /**
+         * The provided instant in time equals the instant the event took 
place.
+         */
+        INSTANT_EQUALS_INSTANT(true, new URIImpl(TEMPORAL_NS+"equals")),
+
+        /**
+         * The provided instant in time was before when the event took place.
+         */
+        INSTANT_BEFORE_INSTANT(true, new URIImpl(TEMPORAL_NS+"before")),
+
+        /**
+         * The provided instant in time was after when the event took place.
+         */
+        INSTANT_AFTER_INSTANT(true, new URIImpl(TEMPORAL_NS+"after")),
+
+        /**
+         * The provided instant in time was before a time period.
+         */
+        INSTANT_BEFORE_INTERVAL(false, new 
URIImpl(TEMPORAL_NS+"beforeInterval")),
+
+        /**
+         * The provided instant in time took place within a set of time.
+         */
+        INSTANT_IN_INTERVAL(false, new URIImpl(TEMPORAL_NS+"insideInterval")),
+
+        /**
+         * The provided instant in time took place after a time period.
+         */
+        INSTANT_AFTER_INTERVAL(false, new 
URIImpl(TEMPORAL_NS+"afterInterval")),
+
+        /**
+         * The provided instant in time equals the instant the event took 
place.
+         */
+        INSTANT_START_INTERVAL(false, new 
URIImpl(TEMPORAL_NS+"hasBeginningInterval")),
+        INSTANT_END_INTERVAL(false, new URIImpl(TEMPORAL_NS+"hasEndInterval")),
+        INTERVAL_EQUALS(false, new URIImpl(TEMPORAL_NS+"intervalEquals")),
+        INTERVAL_BEFORE(false, new URIImpl(TEMPORAL_NS+"intervalBefore")),
+        INTERVAL_AFTER(false, new URIImpl(TEMPORAL_NS+"intervalAfter"));
+
+        private final boolean isInstant;
+        private final URI uri;
+
+        TemporalPolicy(final boolean isInstant, final URI uri) {
+            this.isInstant = isInstant;
+            this.uri = uri;
+        }
+
+        public boolean isInstant(){
+            return isInstant;
+        }
+
+        public URI getURI() {
+            return uri;
+        }
+
+        public static TemporalPolicy fromURI(final URI uri) {
+            for(final TemporalPolicy policy : TemporalPolicy.values()) {
+                if(policy.getURI().equals(uri)) {
+                    return policy;
+                }
+            }
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/af5964aa/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexerFactory.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexerFactory.java
 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexerFactory.java
new file mode 100644
index 0000000..a7ba8fa
--- /dev/null
+++ 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalIndexerFactory.java
@@ -0,0 +1,53 @@
+/**
+ * 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.geotemporal;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.rya.indexing.accumulo.ConfigUtils;
+import org.apache.rya.indexing.geotemporal.mongo.MongoGeoTemporalIndexer;
+import org.apache.rya.mongodb.MongoDBRdfConfiguration;
+import org.apache.rya.mongodb.MongoSecondaryIndex;
+
+/**
+ * Factory for retrieving a {@link GeoTemporalIndexer} based on a provided 
{@link Configuration}.
+ */
+public class GeoTemporalIndexerFactory {
+    /**
+     * Creates and returns a {@link GeoTemporalIndexer}.
+     * @param conf - The {@link Configuration} to base the {@link 
GeoTemporalIndexer} on.
+     * @return The created {@link GeoTemporalIndexer}.
+     */
+    public GeoTemporalIndexer getIndexer(final Configuration conf) {
+        if(ConfigUtils.getUseMongo(conf)) {
+            final MongoDBRdfConfiguration config = new 
MongoDBRdfConfiguration(conf);
+            for(final MongoSecondaryIndex index : 
config.getAdditionalIndexers()) {
+                if(index instanceof GeoTemporalIndexer) {
+                    return (GeoTemporalIndexer) index;
+                }
+            }
+            final MongoGeoTemporalIndexer index = new 
MongoGeoTemporalIndexer();
+            index.setConf(conf);
+            index.init();
+            return index;
+        } else {
+            //TODO: add Accumulo here.
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/af5964aa/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalOptimizer.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalOptimizer.java
 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalOptimizer.java
new file mode 100644
index 0000000..d626adc
--- /dev/null
+++ 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalOptimizer.java
@@ -0,0 +1,69 @@
+/*
+ * 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.geotemporal;
+
+import org.apache.hadoop.conf.Configurable;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.rya.indexing.external.matching.AbstractExternalSetOptimizer;
+import org.apache.rya.indexing.external.matching.ExternalSetMatcher;
+import org.apache.rya.indexing.external.matching.ExternalSetProvider;
+import org.apache.rya.indexing.external.matching.QueryNodeListRater;
+import org.apache.rya.indexing.external.matching.QuerySegment;
+import org.apache.rya.indexing.geotemporal.model.EventQueryNode;
+
+import com.google.common.base.Optional;
+
+
+public class GeoTemporalOptimizer extends 
AbstractExternalSetOptimizer<EventQueryNode> implements Configurable {
+    private static final GeoTemporalExternalSetMatcherFactory MATCHER_FACTORY 
= new GeoTemporalExternalSetMatcherFactory();
+
+    private GeoTemporalIndexer indexer;
+    private GeoTemporalIndexSetProvider provider;
+    private Configuration conf;
+
+    @Override
+    public void setConf(final Configuration conf) {
+        this.conf = conf;
+        final GeoTemporalIndexerFactory factory = new 
GeoTemporalIndexerFactory();
+        indexer = factory.getIndexer(conf);
+
+        //conf here does not matter since EventStorage has already been set in 
the indexer.
+        provider = new 
GeoTemporalIndexSetProvider(indexer.getEventStorage(conf));
+    }
+
+    @Override
+    public Configuration getConf() {
+        return conf;
+    }
+
+    @Override
+    protected ExternalSetMatcher<EventQueryNode> getMatcher(final 
QuerySegment<EventQueryNode> segment) {
+        return MATCHER_FACTORY.getMatcher(segment);
+    }
+
+    @Override
+    protected ExternalSetProvider<EventQueryNode> getProvider() {
+        return provider;
+    }
+
+    @Override
+    protected Optional<QueryNodeListRater> getNodeListRater(final 
QuerySegment<EventQueryNode> segment) {
+        return null;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/af5964aa/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalToSegmentConverter.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalToSegmentConverter.java
 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalToSegmentConverter.java
new file mode 100644
index 0000000..22bfdb1
--- /dev/null
+++ 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/GeoTemporalToSegmentConverter.java
@@ -0,0 +1,51 @@
+/**
+ * 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.geotemporal;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.rya.indexing.external.matching.ExternalSetConverter;
+import org.apache.rya.indexing.external.matching.JoinSegment;
+import org.apache.rya.indexing.external.matching.QuerySegment;
+import org.apache.rya.indexing.geotemporal.model.EventQueryNode;
+import org.openrdf.query.algebra.Filter;
+import org.openrdf.query.algebra.QueryModelNode;
+import org.openrdf.query.algebra.ValueExpr;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Implementation of {@link ExternalSetConverter} to convert {@link 
EventQueryNode}s
+ * to {@link QuerySegment}s.
+ *
+ */
+public class GeoTemporalToSegmentConverter implements 
ExternalSetConverter<EventQueryNode> {
+    @Override
+    public QuerySegment<EventQueryNode> setToSegment(final EventQueryNode set) 
{
+        Preconditions.checkNotNull(set);
+        final Set<QueryModelNode> matched = new HashSet<>(set.getPatterns());
+        matched.addAll(set.getFilters());
+        final List<QueryModelNode> unmatched = new 
ArrayList<>(set.getPatterns());
+        return new JoinSegment<EventQueryNode>(matched, unmatched, new 
HashMap<ValueExpr, Filter>());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/af5964aa/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/model/Event.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/model/Event.java
 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/model/Event.java
new file mode 100644
index 0000000..4c50bfb
--- /dev/null
+++ 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/model/Event.java
@@ -0,0 +1,218 @@
+/**
+ * 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.geotemporal.model;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Objects;
+import java.util.Optional;
+
+import org.apache.rya.api.domain.RyaURI;
+import org.apache.rya.indexing.TemporalInstant;
+import org.apache.rya.indexing.TemporalInterval;
+import org.apache.rya.indexing.geotemporal.GeoTemporalIndexer;
+
+import com.vividsolutions.jts.geom.Geometry;
+
+import edu.umd.cs.findbugs.annotations.DefaultAnnotation;
+import edu.umd.cs.findbugs.annotations.NonNull;
+
+/**
+ * Query object for a {@link GeoTemporalIndexer}.
+ * Defines a {@link Geometry}, either a {@link TemporalInstant} or
+ * {@link TemporalInterval}, and a triple Subject.
+ */
+public class Event {
+    private final Optional<Geometry> geometry;
+    private final Optional<TemporalInstant> instant;
+    private final Optional<TemporalInterval> interval;
+    private final RyaURI subject;
+
+    private final boolean isInstant;
+
+    /**
+     * Creates a new {@link Event} query object with a {@link TemporalInstant}.
+     * @param geo - The {@link Geometry} to use when querying.
+     * @param instant - The {@link TemporalInstant} to use when querying.
+     * @param subject - The Subject that both statements must have when 
querying.
+     */
+    private Event(final Geometry geo, final TemporalInstant instant, final 
RyaURI subject) {
+        this.subject = requireNonNull(subject);
+
+        //these fields are nullable since they are filled field by field.
+        this.instant = Optional.ofNullable(instant);
+        geometry = Optional.ofNullable(geo);
+        isInstant = true;
+        interval = Optional.empty();
+    }
+
+    /**
+     * Creates a new {@link Event} query object with a {@link 
TemporalInterval}.
+     * @param geo - The {@link Geometry} to use when querying.
+     * @param interval - The {@link TemporalInterval} to use when querying.
+     * @param subject - The Subject that both statements must have when 
querying.
+     */
+    private Event(final Geometry geo, final TemporalInterval interval, final 
RyaURI subject) {
+        this.subject = requireNonNull(subject);
+
+        //these fields are nullable since they are filled field by field.
+        this.interval = Optional.ofNullable(interval);
+        geometry = Optional.ofNullable(geo);
+        isInstant = false;
+        instant = Optional.empty();
+    }
+
+    /**
+     * @return Whether or not the query object uses a {@link TemporalInstant}.
+     */
+    public boolean isInstant() {
+        return isInstant;
+    }
+
+    /**
+     * @return The {@link Geometry} to use when querying.
+     */
+    public Optional<Geometry> getGeometry() {
+        return geometry;
+    }
+
+    /**
+     * @return The {@link TemporalInstant} to use when querying.
+     */
+    public Optional<TemporalInstant> getInstant() {
+        return instant;
+    }
+
+    /**
+     * @return The {@link TemporalInterval} to use when querying.
+     */
+    public Optional<TemporalInterval> getInterval() {
+        return interval;
+    }
+
+    /**
+     * @return The statement subject.
+     */
+    public RyaURI getSubject() {
+        return subject;
+    }
+
+    @Override
+    public int hashCode() {
+        if(isInstant) {
+            return Objects.hash(subject, geometry, instant);
+        } else {
+            return Objects.hash(subject, geometry, interval);
+        }
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if(this == o) {
+            return true;
+        }
+        if(o instanceof Event) {
+            final Event event = (Event) o;
+            return Objects.equals(subject, event.subject) &&
+                    Objects.equals(isInstant, event.isInstant) &&
+                    (isInstant ? Objects.equals(instant, event.instant) : 
Objects.equals(interval, event.interval));
+        }
+        return false;
+    }
+
+    public static Builder builder(final Event event) {
+        final Builder builder = new Builder()
+            .setSubject(event.getSubject());
+        if(event.getGeometry().isPresent()) {
+            builder.setGeometry(event.getGeometry().get());
+        }
+        if(event.isInstant()) {
+            if(event.getInstant().isPresent()) {
+                builder.setTemporalInstant(event.getInstant().get());
+            }
+        } else {
+            if(event.getInterval().isPresent()) {
+                builder.setTemporalInterval(event.getInterval().get());
+            }
+        }
+        return builder;
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builds instances of {@link Event}.
+     */
+    @DefaultAnnotation(NonNull.class)
+    public static class Builder {
+        private RyaURI subject;
+        private Geometry geo;
+        private TemporalInstant instant;
+        private TemporalInterval interval;
+
+        /**
+         * Sets the {@link RyaURI} subject.
+         * @param subject - The subject to key on the event.
+         */
+        public Builder setSubject(final RyaURI subject) {
+            this.subject = subject;
+            return this;
+        }
+
+        /**
+         * Sets the {@link Geometry}.
+         * @param geo - The geometry.
+         */
+        public Builder setGeometry(final Geometry geo) {
+            this.geo = geo;
+            return this;
+        }
+
+        /**
+         * Sets the {@link TemporalInterval}.
+         * @param interval - The interval.
+         */
+        public Builder setTemporalInterval(final TemporalInterval interval) {
+            this.interval = interval;
+            return this;
+        }
+
+        /**
+         * Sets the {@link TemporalInstant}.
+         * @param instant - The instant.
+         */
+        public Builder setTemporalInstant(final TemporalInstant instant) {
+            this.instant = instant;
+            return this;
+        }
+
+        /**
+         * @return The new {@link Event}.
+         */
+        public Event build() {
+            if(instant == null) {
+                return new Event(geo, interval, subject);
+            } else {
+                return new Event(geo, instant, subject);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/af5964aa/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/model/EventQueryNode.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/model/EventQueryNode.java
 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/model/EventQueryNode.java
new file mode 100644
index 0000000..6953714
--- /dev/null
+++ 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/model/EventQueryNode.java
@@ -0,0 +1,283 @@
+/*
+ * 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.geotemporal.model;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+import org.apache.rya.api.domain.RyaURI;
+import org.apache.rya.indexing.IndexingExpr;
+import org.apache.rya.indexing.TemporalInstantRfc3339;
+import org.apache.rya.indexing.geotemporal.storage.EventStorage;
+import 
org.apache.rya.indexing.mongodb.update.RyaObjectStorage.ObjectStorageException;
+import org.apache.rya.rdftriplestore.evaluation.ExternalBatchingIterator;
+import org.openrdf.model.Value;
+import org.openrdf.model.impl.ValueFactoryImpl;
+import org.openrdf.query.BindingSet;
+import org.openrdf.query.QueryEvaluationException;
+import org.openrdf.query.algebra.FunctionCall;
+import org.openrdf.query.algebra.StatementPattern;
+import org.openrdf.query.algebra.Var;
+import org.openrdf.query.algebra.evaluation.impl.ExternalSet;
+import org.openrdf.query.algebra.evaluation.iterator.CollectionIteration;
+import org.openrdf.query.impl.MapBindingSet;
+
+import com.vividsolutions.jts.geom.Geometry;
+
+import info.aduna.iteration.CloseableIteration;
+
+public class EventQueryNode extends ExternalSet implements 
ExternalBatchingIterator {
+    private final Collection<FunctionCall> usedFilters;
+    private final Collection<IndexingExpr> geoFilters;
+    private final Collection<IndexingExpr> temporalFilters;
+
+    private final StatementPattern geoPattern;
+    private final StatementPattern temporalPattern;
+
+    //Information about the subject of the patterns.
+    private final boolean subjectIsConstant;
+    private final Optional<String> subjectConstant;
+    private final Optional<String> subjectVar;
+
+    //since and EventQueryNode exists in a single segment, all binding names 
are garunteed to be assured.
+    private final Set<String> bindingNames;
+
+    private Collection<StatementPattern> patterns;
+
+    private final EventStorage eventStore;
+
+    /**
+     * Constructs an instance of {@link EventQueryNode}.
+     * @param usedFilters
+     *
+     * @param type - The type of {@link Event} this node matches. (not null)
+     * @param patterns - The query StatementPatterns that are solved using an
+     *   Event of the Type. (not null)
+     * @param entities - The {@link EventStorage} that will be searched to 
match
+     *   {@link BindingSet}s when evaluating a query. (not null)
+     */
+    public EventQueryNode(final EventStorage eventStore, final 
StatementPattern geoPattern, final StatementPattern temporalPattern, final 
Collection<IndexingExpr> geoFilters, final Collection<IndexingExpr> 
temporalFilters, final Collection<FunctionCall> usedFilters) throws 
IllegalStateException {
+        this.geoPattern = requireNonNull(geoPattern);
+        this.temporalPattern = requireNonNull(temporalPattern);
+        this.geoFilters = requireNonNull(geoFilters);
+        this.temporalFilters = requireNonNull(temporalFilters);
+        this.eventStore = requireNonNull(eventStore);
+        this.usedFilters = requireNonNull(usedFilters);
+        bindingNames = new HashSet<>();
+
+        // Subject based preconditions.
+        verifySameSubjects(getPatterns());
+        // Predicate based preconditions.
+        verifyAllPredicatesAreConstants(getPatterns());
+
+        // The Subject may either be constant or a variable.
+        final Var subject = patterns.iterator().next().getSubjectVar();
+        subjectIsConstant = subject.isConstant();
+        if(subjectIsConstant) {
+            subjectConstant = Optional.of( subject.getValue().toString() );
+            subjectVar = Optional.empty();
+        } else {
+            subjectConstant = Optional.empty();
+            subjectVar = Optional.of( subject.getName() );
+        }
+    }
+
+    @Override
+    public Set<String> getBindingNames() {
+        return bindingNames;
+    }
+
+    @Override
+    public Set<String> getAssuredBindingNames() {
+        return bindingNames;
+    }
+
+    /**
+     * Verify the Subject for all of the patterns is the same.
+     *
+     * @param patterns - The patterns to check.
+     * @throws IllegalStateException If all of the Subjects are not the same.
+     */
+    private static void verifySameSubjects(final Collection<StatementPattern> 
patterns) throws IllegalStateException {
+        requireNonNull(patterns);
+
+        final Iterator<StatementPattern> it = patterns.iterator();
+        final Var subject = it.next().getSubjectVar();
+
+        while(it.hasNext()) {
+            final StatementPattern pattern = it.next();
+            if(!pattern.getSubjectVar().equals(subject)) {
+                throw new IllegalStateException("At least one of the patterns 
has a different subject from the others. " +
+                        "All subjects must be the same.");
+            }
+        }
+    }
+
+    /**
+     * Verifies all of the Statement Patterns have Constants for their 
predicates.
+     *
+     * @param patterns - The patterns to check. (not null)
+     * @throws IllegalStateException A pattern has a variable predicate.
+     */
+    private static void verifyAllPredicatesAreConstants(final 
Collection<StatementPattern> patterns) throws IllegalStateException {
+        requireNonNull(patterns);
+
+        for(final StatementPattern pattern : patterns) {
+            if(!pattern.getPredicateVar().isConstant()) {
+                throw new IllegalStateException("The Predicate of a Statement 
Pattern must be constant. Pattern: " + pattern);
+            }
+        }
+    }
+
+    @Override
+    public CloseableIteration<BindingSet, QueryEvaluationException> 
evaluate(final BindingSet bindings) throws QueryEvaluationException {
+        final List<BindingSet> list = new ArrayList<>();
+        try {
+            final Collection<Event> searchEvents;
+            final String subj;
+            // If the subject needs to be filled in, check if the subject 
variable is in the binding set.
+            if(subjectIsConstant) {
+                // if it is, fetch that value and then fetch the entity for 
the subject.
+                subj = subjectConstant.get();
+                searchEvents = eventStore.search(Optional.of(new 
RyaURI(subj)), Optional.of(geoFilters), Optional.of(temporalFilters));
+            } else {
+                searchEvents = eventStore.search(Optional.empty(), 
Optional.of(geoFilters), Optional.of(temporalFilters));
+            }
+
+            for(final Event event : searchEvents) {
+                final MapBindingSet resultSet = new MapBindingSet();
+                if(event.getGeometry().isPresent()) {
+                    final Geometry geo = event.getGeometry().get();
+                    final Value geoValue = 
ValueFactoryImpl.getInstance().createLiteral(geo.toText());
+                    final Var geoObj = geoPattern.getObjectVar();
+                    resultSet.addBinding(geoObj.getName(), geoValue);
+                }
+
+                final Value temporalValue;
+                if(event.isInstant() && event.getInstant().isPresent()) {
+                    temporalValue = 
ValueFactoryImpl.getInstance().createLiteral(event.getInstant().get().getAsDateTime().toString(TemporalInstantRfc3339.FORMATTER));
+                } else if(event.getInterval().isPresent()) {
+                    temporalValue = 
ValueFactoryImpl.getInstance().createLiteral(event.getInterval().get().getAsPair());
+                } else {
+                    temporalValue = null;
+                }
+
+                if(temporalValue != null) {
+                    final Var temporalObj = temporalPattern.getObjectVar();
+                    resultSet.addBinding(temporalObj.getName(), temporalValue);
+                }
+                list.add(resultSet);
+            }
+        } catch (final ObjectStorageException e) {
+            throw new QueryEvaluationException("Failed to evaluate the binding 
set", e);
+        }
+        if(bindings.size() != 0) {
+            list.add(bindings);
+        }
+        return new CollectionIteration<>(list);
+    }
+
+    public Collection<IndexingExpr> getGeoFilters() {
+        return geoFilters;
+    }
+
+    public Collection<IndexingExpr> getTemporalFilters() {
+        return temporalFilters;
+    }
+
+    public Collection<FunctionCall> getFilters() {
+        return usedFilters;
+    }
+
+    public Collection<StatementPattern> getPatterns() {
+        if(patterns == null) {
+            patterns = new ArrayList<>();
+            patterns.add(geoPattern);
+            patterns.add(temporalPattern);
+        }
+        return patterns;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(subjectIsConstant,
+                subjectVar,
+                geoFilters,
+                temporalFilters,
+                geoPattern,
+                temporalPattern,
+                bindingNames,
+                eventStore);
+    }
+
+    @Override
+    public boolean equals(final Object other) {
+        if(other instanceof EventQueryNode) {
+            final EventQueryNode otherNode = (EventQueryNode)other;
+
+            return  Objects.equals(subjectIsConstant, 
otherNode.subjectIsConstant) &&
+                    Objects.equals(subjectVar, otherNode.subjectVar) &&
+                    Objects.equals(geoFilters, otherNode.geoFilters) &&
+                    Objects.equals(geoPattern, otherNode.geoPattern) &&
+                    Objects.equals(temporalFilters, otherNode.temporalFilters) 
&&
+                    Objects.equals(temporalPattern, otherNode.temporalPattern) 
&&
+                    Objects.equals(bindingNames, otherNode.bindingNames) &&
+                    Objects.equals(subjectConstant, otherNode.subjectConstant);
+        }
+        return false;
+    }
+
+    @Override
+    public EventQueryNode clone() {
+        return new EventQueryNode(eventStore, geoPattern, temporalPattern, 
geoFilters, temporalFilters, usedFilters);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("Geo Pattern: " + geoPattern.toString());
+        sb.append("\n--Geo Filters--\n");
+        for(final IndexingExpr filter : geoFilters) {
+            sb.append(filter.toString());
+            sb.append("\n");
+        }
+        sb.append("\n-------------------\n");
+        sb.append("Temporal Pattern: " + temporalPattern.toString());
+        sb.append("\n--Temporal Filters--\n");
+        for(final IndexingExpr filter : temporalFilters) {
+            sb.append(filter.toString());
+            sb.append("\n");
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public CloseableIteration<BindingSet, QueryEvaluationException> 
evaluate(final Collection<BindingSet> bindingset)
+            throws QueryEvaluationException {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/af5964aa/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/storage/EventStorage.java
----------------------------------------------------------------------
diff --git 
a/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/storage/EventStorage.java
 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/storage/EventStorage.java
new file mode 100644
index 0000000..47c18a0
--- /dev/null
+++ 
b/extras/rya.geoindexing/src/main/java/org/apache/rya/indexing/geotemporal/storage/EventStorage.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.indexing.geotemporal.storage;
+
+import java.util.Collection;
+import java.util.Optional;
+
+import org.apache.rya.api.domain.RyaURI;
+import org.apache.rya.indexing.IndexingExpr;
+import org.apache.rya.indexing.geotemporal.GeoTemporalIndexer;
+import org.apache.rya.indexing.geotemporal.model.Event;
+import org.apache.rya.indexing.mongodb.update.RyaObjectStorage;
+
+public interface EventStorage extends RyaObjectStorage<Event> {
+    /**
+     * Search for {@link Event}s from the storage by its subject.
+     * Will query based on present parameters.
+     *
+     * @param subject - The subject key to find events.
+     * @param geoFilters - The geo filters to find Events.
+     * @param temporalFilters - The temporal filters to find Events.
+     * @return The {@link Event}, if one exists for the subject.
+     * @throws ObjectStorageException A problem occurred while fetching the 
Entity from the storage.
+     */
+    public Collection<Event> search(final Optional<RyaURI> subject, 
Optional<Collection<IndexingExpr>> geoFilters, 
Optional<Collection<IndexingExpr>> temporalFilters) throws 
ObjectStorageException;
+
+    /**
+     * Indicates a problem while interacting with an {@link EventStorage}.
+     */
+    public static class EventStorageException extends ObjectStorageException {
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Constructs a new exception with the specified detail message.  The
+         * cause is not initialized, and may subsequently be initialized by
+         * a call to {@link #initCause}.
+         *
+         * @param   message   the detail message. The detail message is saved 
for
+         *          later retrieval by the {@link #getMessage()} method.
+         */
+        public EventStorageException(final String message) {
+            super(message);
+        }
+
+        /**
+         * Constructs a new exception with the specified detail message and
+         * cause.  <p>Note that the detail message associated with
+         * {@code cause} is <i>not</i> automatically incorporated in
+         * this exception's detail message.
+         *
+         * @param  message the detail message (which is saved for later 
retrieval
+         *         by the {@link #getMessage()} method).
+         * @param  cause the cause (which is saved for later retrieval by the
+         *         {@link #getCause()} method).  (A <tt>null</tt> value is
+         *         permitted, and indicates that the cause is nonexistent or
+         *         unknown.)
+         */
+        public EventStorageException(final String message, final Throwable 
cause) {
+            super(message, cause);
+        }
+    }
+
+    /**
+     * An {@link Event} could not be created because one already exists for 
the Subject.
+     */
+    public static class EventAlreadyExistsException extends 
EventStorageException {
+        private static final long serialVersionUID = 1L;
+
+        public EventAlreadyExistsException(final String message) {
+            super(message);
+        }
+
+        public EventAlreadyExistsException(final String message, final 
Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+    /**
+     * An {@link TypedEvent} could not be updated because the old state does 
not
+     * match the current state.
+     */
+    public static class StaleUpdateException extends EventStorageException {
+        private static final long serialVersionUID = 1L;
+
+        public StaleUpdateException(final String message) {
+            super(message);
+        }
+
+        public StaleUpdateException(final String message, final Throwable 
cause) {
+            super(message, cause);
+        }
+    }
+
+    /**
+     *  A {@link EventFilter} is a translation from an {@link IndexingExpr}
+     *  to a format the {@link GeoTemporalIndexer} can use to easily determine 
which
+     *  filter function is being used.
+     *
+     *   @param T - The type of
+     */
+    interface EventFilter<T> {
+        /**
+         * Gets the translated query friendly form of the filter.
+         */
+        public T getQueryObject();
+    }
+
+    /**
+     * Factory for getting the {@link EventFilter} from an {@link 
IndexingExpr}.
+     */
+    interface EventFilterFactory<T> {
+        public EventFilter<T> getSearchFunction(final IndexingExpr filter);
+    }
+}

Reply via email to