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); + } +}
