http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/mvm/rya/indexing/entity/storage/mongo/MongoTypeStorage.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/mvm/rya/indexing/entity/storage/mongo/MongoTypeStorage.java b/extras/indexing/src/main/java/mvm/rya/indexing/entity/storage/mongo/MongoTypeStorage.java deleted file mode 100644 index dbeff56..0000000 --- a/extras/indexing/src/main/java/mvm/rya/indexing/entity/storage/mongo/MongoTypeStorage.java +++ /dev/null @@ -1,149 +0,0 @@ -/** - * 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 mvm.rya.indexing.entity.storage.mongo; - -import static java.util.Objects.requireNonNull; - -import javax.annotation.ParametersAreNonnullByDefault; - -import org.bson.Document; -import org.bson.conversions.Bson; - -import com.google.common.base.Optional; -import com.mongodb.MongoClient; -import com.mongodb.MongoException; -import com.mongodb.client.MongoCursor; -import com.mongodb.client.model.Filters; - -import mvm.rya.api.domain.RyaURI; -import mvm.rya.indexing.entity.model.Type; -import mvm.rya.indexing.entity.storage.CloseableIterator; -import mvm.rya.indexing.entity.storage.TypeStorage; -import mvm.rya.indexing.entity.storage.mongo.DocumentConverter.DocumentConverterException; - -/** - * A Mongo DB implementation of {@link TypeStorage}. - */ -@ParametersAreNonnullByDefault -public class MongoTypeStorage implements TypeStorage { - - private static final String COLLECTION_NAME = "entity-types"; - - private static final TypeDocumentConverter TYPE_CONVERTER = new TypeDocumentConverter(); - - /** - * A client connected to the Mongo instance that hosts the Rya instance. - */ - private final MongoClient mongo; - - /** - * The name of the Rya instance the {@link Type}s are for. - */ - private final String ryaInstanceName; - - /** - * Constructs an instance of {@link MongoTypeStorage}. - * - * @param mongo - A client connected to the Mongo instance that hosts the Rya instance. (not null) - * @param ryaInstanceName - The name of the Rya instance the {@link Type}s are for. (not null) - */ - public MongoTypeStorage(MongoClient mongo, String ryaInstanceName) { - this.mongo = requireNonNull(mongo); - this.ryaInstanceName = requireNonNull(ryaInstanceName); - } - - @Override - public void create(Type type) throws TypeStorageException { - requireNonNull(type); - - try { - mongo.getDatabase(ryaInstanceName) - .getCollection(COLLECTION_NAME) - .insertOne(TYPE_CONVERTER.toDocument(type)); - - } catch(final MongoException e) { - throw new TypeStorageException("Failed to create Type with ID '" + type.getId().getData() + "'.", e); - } - } - - @Override - public Optional<Type> get(RyaURI typeId) throws TypeStorageException { - requireNonNull(typeId); - - try { - final Document document = mongo.getDatabase(ryaInstanceName) - .getCollection(COLLECTION_NAME) - .find( makeIdFilter(typeId) ) - .first(); - - return document == null ? - Optional.absent() : - Optional.of( TYPE_CONVERTER.fromDocument(document) ); - - } catch(final MongoException | DocumentConverterException e) { - throw new TypeStorageException("Could not get the Type with ID '" + typeId.getData() + "'.", e); - } - } - - @Override - public CloseableIterator<Type> search(RyaURI propertyName) throws TypeStorageException { - requireNonNull(propertyName); - - try { - // Create a Filter that finds Types who have the provided property names. - final Bson byPropertyName = Filters.eq(TypeDocumentConverter.PROPERTY_NAMES, propertyName.getData()); - - final MongoCursor<Document> cursor = mongo.getDatabase(ryaInstanceName) - .getCollection(COLLECTION_NAME) - .find( byPropertyName ) - .iterator(); - - return new ConvertingCursor<Type>(document -> { - try { - return TYPE_CONVERTER.fromDocument(document); - } catch (final Exception e) { - throw new RuntimeException("Could not convert the Document '" + document + "' into a Type.", e); - } - }, cursor); - - } catch(final MongoException e) { - throw new TypeStorageException("Could not fetch Types that include the property '" + propertyName.getData() + "'.", e); - } - } - - @Override - public boolean delete(RyaURI typeId) throws TypeStorageException { - requireNonNull(typeId); - - try { - final Document deleted = mongo.getDatabase(ryaInstanceName) - .getCollection(COLLECTION_NAME) - .findOneAndDelete( makeIdFilter(typeId) ); - - return deleted != null; - - } catch(final MongoException e) { - throw new TypeStorageException("Could not delete the Type with ID '" + typeId.getData() + "'.", e); - } - } - - private static Bson makeIdFilter(RyaURI typeId) { - return Filters.eq(TypeDocumentConverter.ID, typeId.getData()); - } -} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/mvm/rya/indexing/entity/storage/mongo/RyaTypeDocumentConverter.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/mvm/rya/indexing/entity/storage/mongo/RyaTypeDocumentConverter.java b/extras/indexing/src/main/java/mvm/rya/indexing/entity/storage/mongo/RyaTypeDocumentConverter.java deleted file mode 100644 index ab6148b..0000000 --- a/extras/indexing/src/main/java/mvm/rya/indexing/entity/storage/mongo/RyaTypeDocumentConverter.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * 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 mvm.rya.indexing.entity.storage.mongo; - -import static java.util.Objects.requireNonNull; - -import javax.annotation.ParametersAreNonnullByDefault; - -import org.bson.Document; -import org.openrdf.model.ValueFactory; -import org.openrdf.model.impl.ValueFactoryImpl; - -import mvm.rya.api.domain.RyaType; - -/** - * Converts between {@link RyaType} and {@link Document}. - */ -@ParametersAreNonnullByDefault -public class RyaTypeDocumentConverter implements DocumentConverter<RyaType> { - - private static final ValueFactory VF = new ValueFactoryImpl(); - - public static final String DATA_TYPE = "dataType"; - public static final String VALUE = "value"; - - @Override - public Document toDocument(RyaType ryaType) { - requireNonNull(ryaType); - - return new Document() - .append(DATA_TYPE, ryaType.getDataType().toString()) - .append(VALUE, ryaType.getData()); - } - - @Override - public RyaType fromDocument(Document document) throws DocumentConverterException { - requireNonNull(document); - - if(!document.containsKey(DATA_TYPE)) { - throw new DocumentConverterException("Could not convert document '" + document + - "' because its '" + DATA_TYPE + "' field is missing."); - } - - if(!document.containsKey(VALUE)) { - throw new DocumentConverterException("Could not convert document '" + document + - "' because its '" + VALUE + "' field is missing."); - } - - return new RyaType( - VF.createURI( document.getString(DATA_TYPE) ), - document.getString(VALUE)); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/mvm/rya/indexing/entity/storage/mongo/TypeDocumentConverter.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/mvm/rya/indexing/entity/storage/mongo/TypeDocumentConverter.java b/extras/indexing/src/main/java/mvm/rya/indexing/entity/storage/mongo/TypeDocumentConverter.java deleted file mode 100644 index 2d171d7..0000000 --- a/extras/indexing/src/main/java/mvm/rya/indexing/entity/storage/mongo/TypeDocumentConverter.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * 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 mvm.rya.indexing.entity.storage.mongo; - -import static java.util.Objects.requireNonNull; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.ParametersAreNonnullByDefault; - -import org.bson.Document; - -import com.google.common.collect.ImmutableSet; - -import mvm.rya.api.domain.RyaURI; -import mvm.rya.indexing.entity.model.Type; - -/** - * Converts between {@link Type} and {@link Document}. - */ -@ParametersAreNonnullByDefault -public class TypeDocumentConverter implements DocumentConverter<Type> { - - public static final String ID = "_id"; - public static final String PROPERTY_NAMES = "propertyNames"; - - @Override - public Document toDocument(final Type type) { - requireNonNull(type); - - final Document doc = new Document(); - doc.append(ID, type.getId().getData()); - - final List<String> propertyNames = new ArrayList<>(); - type.getPropertyNames().forEach(field -> propertyNames.add(field.getData())); - doc.append(PROPERTY_NAMES, propertyNames); - - return doc; - } - - @Override - public Type fromDocument(final Document document) throws DocumentConverterException { - requireNonNull(document); - - if(!document.containsKey(ID)) { - throw new DocumentConverterException("Could not convert document '" + document + - "' because its '" + ID + "' field is missing."); - } - - if(!document.containsKey(PROPERTY_NAMES)) { - throw new DocumentConverterException("Could not convert document '" + document + - "' because its '" + PROPERTY_NAMES + "' field is missing."); - } - - final RyaURI typeId = new RyaURI( document.getString(ID) ); - - final ImmutableSet.Builder<RyaURI> propertyNames = ImmutableSet.builder(); - ((List<String>) document.get(PROPERTY_NAMES)) - .forEach(propertyName -> propertyNames.add(new RyaURI(propertyName))); - - return new Type(typeId, propertyNames.build()); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/BaseEntityIndexer.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/BaseEntityIndexer.java b/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/BaseEntityIndexer.java deleted file mode 100644 index 627b879..0000000 --- a/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/BaseEntityIndexer.java +++ /dev/null @@ -1,266 +0,0 @@ -/** - * 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 mvm.rya.indexing.entity.update; - -import static com.google.common.base.Preconditions.checkState; -import static java.util.Collections.singleton; -import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.groupingBy; - -import java.io.IOException; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; - -import javax.annotation.Nullable; -import javax.annotation.ParametersAreNonnullByDefault; - -import org.apache.hadoop.conf.Configuration; -import org.openrdf.model.URI; -import org.openrdf.model.vocabulary.RDF; - -import com.google.common.base.Objects; - -import mvm.rya.api.domain.RyaStatement; -import mvm.rya.api.domain.RyaType; -import mvm.rya.api.domain.RyaURI; -import mvm.rya.indexing.entity.model.Entity; -import mvm.rya.indexing.entity.model.Property; -import mvm.rya.indexing.entity.model.Type; -import mvm.rya.indexing.entity.storage.CloseableIterator; -import mvm.rya.indexing.entity.storage.EntityStorage; -import mvm.rya.indexing.entity.storage.EntityStorage.EntityStorageException; -import mvm.rya.indexing.entity.storage.TypeStorage; -import mvm.rya.indexing.entity.storage.TypeStorage.TypeStorageException; -import mvm.rya.mongodb.MongoDBRdfConfiguration; - -/** - * A base class that may be used to update an {@link EntityStorage} as new - * {@link RyaStatement}s are added to/removed from the Rya instance. - */ -@ParametersAreNonnullByDefault -public abstract class BaseEntityIndexer implements EntityIndexer { - - /** - * When this URI is the Predicate of a Statement, it indicates a {@link Type} for an {@link Entity}. - */ - private static final RyaURI TYPE_URI = new RyaURI( RDF.TYPE.toString() ); - - private final AtomicReference<MongoDBRdfConfiguration> configuration = new AtomicReference<>(); - private final AtomicReference<EntityStorage> entities = new AtomicReference<>(); - private final AtomicReference<TypeStorage> types = new AtomicReference<>(); - - /** - * Creates the {@link EntityStorage} that will be used by the indexer. - * - * @param conf - Indicates how the {@link EntityStorage} is initialized. (not null) - * @return The {@link EntityStorage} that will be used by this indexer. - */ - public abstract @Nullable EntityStorage getEntityStorage(Configuration conf); - - /** - * Creates the {@link TypeStorage} that will be used by the indexer. - * - * @param conf - Indicates how the {@link TypeStorage} is initialized. (not null) - * @return The {@link TypeStorage} that will be used by this indexer. - */ - public abstract @Nullable TypeStorage getTypeStorage(Configuration conf); - - @Override - public void setConf(Configuration conf) { - requireNonNull(conf); - entities.set( getEntityStorage(conf) ); - types.set( getTypeStorage(conf) ); - } - - @Override - public Configuration getConf() { - return configuration.get(); - } - - @Override - public void storeStatement(RyaStatement statement) throws IOException { - requireNonNull(statement); - storeStatements( singleton(statement) ); - } - - @Override - public void storeStatements(Collection<RyaStatement> statements) throws IOException { - requireNonNull(statements); - - final Map<RyaURI,List<RyaStatement>> groupedBySubject = statements.stream() - .collect(groupingBy(RyaStatement::getSubject)); - - for(final Entry<RyaURI, List<RyaStatement>> entry : groupedBySubject.entrySet()) { - try { - updateEntity(entry.getKey(), entry.getValue()); - } catch (final EntityStorageException e) { - throw new IOException("Failed to update the Entity index.", e); - } - } - } - - /** - * Updates a {@link Entity} to reflect new {@link RyaStatement}s. - * - * @param subject - The Subject of the {@link Entity} the statements are for. (not null) - * @param statements - Statements that the {@link Entity} will be updated with. (not null) - */ - private void updateEntity(final RyaURI subject, final Collection<RyaStatement> statements) throws EntityStorageException { - requireNonNull(subject); - requireNonNull(statements); - - final EntityStorage entities = this.entities.get(); - final TypeStorage types = this.types.get(); - checkState(entities != null, "Must set this indexers configuration before storing statements."); - checkState(types != null, "Must set this indexers configuration before storing statements."); - - new EntityUpdater(entities).update(subject, old -> { - // Create a builder with the updated Version. - final Entity.Builder updated; - if(!old.isPresent()) { - updated = Entity.builder() - .setSubject(subject) - .setVersion(0); - } else { - final int updatedVersion = old.get().getVersion() + 1; - updated = Entity.builder(old.get()) - .setVersion( updatedVersion ); - } - - // Update the entity based on the Statements. - for(final RyaStatement statement : statements) { - - // The Statement is setting an Explicit Type ID for the Entity. - if(Objects.equal(TYPE_URI, statement.getPredicate())) { - final RyaURI typeId = new RyaURI(statement.getObject().getData()); - updated.setExplicitType(typeId); - } - - // The Statement is adding a Property to the Entity. - else { - final RyaURI propertyName = statement.getPredicate(); - final RyaType propertyValue = statement.getObject(); - - try(final CloseableIterator<Type> typesIt = types.search(propertyName)) { - // Set the Property for each type that includes the Statement's predicate. - while(typesIt.hasNext()) { - final RyaURI typeId = typesIt.next().getId(); - updated.setProperty(typeId, new Property(propertyName, propertyValue)); - } - } catch (final TypeStorageException | IOException e) { - throw new RuntimeException("Failed to fetch Types that include the property name '" + - statement.getPredicate().getData() + "'.", e); - } - } - } - - return Optional.of( updated.build() ); - }); - } - - @Override - public void deleteStatement(RyaStatement statement) throws IOException { - requireNonNull(statement); - - final EntityStorage entities = this.entities.get(); - final TypeStorage types = this.types.get(); - checkState(entities != null, "Must set this indexers configuration before storing statements."); - checkState(types != null, "Must set this indexers configuration before storing statements."); - - try { - new EntityUpdater(entities).update(statement.getSubject(), old -> { - // If there is no Entity for the subject of the statement, then do nothing. - if(!old.isPresent()) { - return Optional.empty(); - } - - final Entity oldEntity = old.get(); - - // Increment the version of the Entity. - final Entity.Builder updated = Entity.builder(oldEntity); - updated.setVersion(oldEntity.getVersion() + 1); - - if(TYPE_URI.equals(statement.getPredicate())) { - // If the Type ID already isn't in the list of explicit types, then do nothing. - final RyaURI typeId = new RyaURI( statement.getObject().getData() ); - if(!oldEntity.getExplicitTypeIds().contains(typeId)) { - return Optional.empty(); - } - - // Otherwise remove it from the list. - updated.unsetExplicitType(typeId); - } else { - // If the deleted property appears within the old entity's properties, then remove it. - final RyaURI deletedPropertyName = statement.getPredicate(); - - boolean propertyWasPresent = false; - for(final RyaURI typeId : oldEntity.getProperties().keySet()) { - for(final RyaURI propertyName : oldEntity.getProperties().get(typeId).keySet()) { - if(deletedPropertyName.equals(propertyName)) { - propertyWasPresent = true; - updated.unsetProperty(typeId, deletedPropertyName); - } - } - } - - // If no properties were removed, then do nothing. - if(!propertyWasPresent) { - return Optional.empty(); - } - } - - return Optional.of( updated.build() ); - }); - } catch (final EntityStorageException e) { - throw new IOException("Failed to update the Entity index.", e); - } - } - - @Override - public String getTableName() { - // Storage details have been abstracted away from the indexer. - return null; - } - - @Override - public void flush() throws IOException { - // We do not need to do anything to flush since we do not batch work. - } - - @Override - public void close() throws IOException { - // Nothing to close. - } - - @Override - public void dropGraph(RyaURI... graphs) { - // We do not support graphs when performing entity centric indexing. - } - - @Override - public Set<URI> getIndexablePredicates() { - // This isn't used anywhere in Rya, so it will not be implemented. - return null; - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/EntityIndexer.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/EntityIndexer.java b/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/EntityIndexer.java deleted file mode 100644 index 6d74836..0000000 --- a/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/EntityIndexer.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 mvm.rya.indexing.entity.update; - -import mvm.rya.api.domain.RyaStatement; -import mvm.rya.api.persist.index.RyaSecondaryIndexer; -import mvm.rya.indexing.entity.storage.EntityStorage; - -/** - * Updates the {@link Entity}s that are in a {@link EntityStorage} when new - * {@link RyaStatement}s are added/removed from the Rya instance. - */ -public interface EntityIndexer extends RyaSecondaryIndexer { - -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/EntityUpdater.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/EntityUpdater.java b/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/EntityUpdater.java deleted file mode 100644 index 6f883d4..0000000 --- a/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/EntityUpdater.java +++ /dev/null @@ -1,121 +0,0 @@ -/** - * 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 mvm.rya.indexing.entity.update; - -import static java.util.Objects.requireNonNull; - -import java.util.Optional; -import java.util.function.Function; - -import javax.annotation.ParametersAreNonnullByDefault; - -import mvm.rya.api.domain.RyaURI; -import mvm.rya.indexing.entity.model.Entity; -import mvm.rya.indexing.entity.storage.EntityStorage; -import mvm.rya.indexing.entity.storage.EntityStorage.EntityAlreadyExistsException; -import mvm.rya.indexing.entity.storage.EntityStorage.EntityStorageException; -import mvm.rya.indexing.entity.storage.EntityStorage.StaleUpdateException; - -/** - * Performs update operations over an {@link EntityStorage}. - */ -@ParametersAreNonnullByDefault -public class EntityUpdater { - - private final EntityStorage storage; - - /** - * Constructs an instance of {@link EntityUpdater}. - * - * @param storage - The storage this updater operates over. (not null) - */ - public EntityUpdater(EntityStorage storage) { - this.storage = requireNonNull(storage); - } - - /** - * Tries to updates the state of an {@link Entity} until the update succeeds - * or a non-recoverable exception is thrown. - * - * @param subject - The Subject of the {@link Entity} that will be updated. (not null) - * @param mutator - Performs the mutation on the old state of the Entity and returns - * the new state of the Entity. (not null) - * @throws EntityStorageException A non-recoverable error has caused the update to fail. - */ - public void update(RyaURI subject, EntityMutator mutator) throws EntityStorageException { - requireNonNull(subject); - requireNonNull(mutator); - - // Fetch the current state of the Entity. - boolean completed = false; - while(!completed) { - try { - final Optional<Entity> old = storage.get(subject); - final Optional<Entity> updated = mutator.apply(old); - - final boolean doWork = updated.isPresent(); - if(doWork) { - if(!old.isPresent()) { - storage.create(updated.get()); - } else { - storage.update(old.get(), updated.get()); - } - } - completed = true; - } catch(final EntityAlreadyExistsException | StaleUpdateException e) { - // These are recoverable exceptions. Try again. - } catch(final RuntimeException e) { - throw new EntityStorageException("Failed to update Entity with Subject '" + subject.getData() + "'.", e); - } - } - } - - /** - * Implementations of this interface are used to update the state of an - * {@link Entity} in unison with a {@link EntityUpdater}. - * </p> - * This table describes what the updater will do depending on if an Entity - * exists and if an updated Entity is returned. - * </p> - * <table border="1px"> - * <tr><th>Entity Provided</th><th>Update Returned</th><th>Effect</th></tr> - * <tr> - * <td>true</td> - * <td>true</td> - * <td>The old Entity will be updated using the returned state.</td> - * </tr> - * <tr> - * <td>true</td> - * <td>false</td> - * <td>No work is performed.</td> - * </tr> - * <tr> - * <td>false</td> - * <td>true</td> - * <td>A new Entity will be created using the returned state.</td> - * </tr> - * <tr> - * <td>false</td> - * <td>false</td> - * <td>No work is performed.</td> - * </tr> - * </table> - */ - public interface EntityMutator extends Function<Optional<Entity>, Optional<Entity>> { } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/mongo/MongoEntityIndexer.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/mongo/MongoEntityIndexer.java b/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/mongo/MongoEntityIndexer.java deleted file mode 100644 index 94550ac..0000000 --- a/extras/indexing/src/main/java/mvm/rya/indexing/entity/update/mongo/MongoEntityIndexer.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * 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 mvm.rya.indexing.entity.update.mongo; - -import javax.annotation.ParametersAreNonnullByDefault; - -import org.apache.hadoop.conf.Configuration; - -import com.mongodb.MongoClient; - -import mvm.rya.indexing.entity.storage.EntityStorage; -import mvm.rya.indexing.entity.storage.TypeStorage; -import mvm.rya.indexing.entity.storage.mongo.MongoEntityStorage; -import mvm.rya.indexing.entity.storage.mongo.MongoTypeStorage; -import mvm.rya.indexing.entity.update.BaseEntityIndexer; -import mvm.rya.indexing.entity.update.EntityIndexer; -import mvm.rya.mongodb.MongoConnectorFactory; -import mvm.rya.mongodb.MongoDBRdfConfiguration; - -/** - * A Mongo DB implementation of {@link EntityIndexer}. - */ -@ParametersAreNonnullByDefault -public class MongoEntityIndexer extends BaseEntityIndexer { - - @Override - public EntityStorage getEntityStorage(Configuration conf) { - final MongoClient mongoClient = MongoConnectorFactory.getMongoClient(conf); - final String ryaInstanceName = new MongoDBRdfConfiguration(conf).getMongoDBName(); - return new MongoEntityStorage(mongoClient, ryaInstanceName); - } - - @Override - public TypeStorage getTypeStorage(Configuration conf) { - final MongoClient mongoClient = MongoConnectorFactory.getMongoClient(conf); - final String ryaInstanceName = new MongoDBRdfConfiguration(conf).getMongoDBName(); - return new MongoTypeStorage(mongoClient, ryaInstanceName); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/org/apache/rya/indexing/entity/EntityIndexException.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/EntityIndexException.java b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/EntityIndexException.java new file mode 100644 index 0000000..61efc91 --- /dev/null +++ b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/EntityIndexException.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.entity; + +import org.apache.rya.indexing.entity.model.TypedEntity; + +/** + * An operation over the {@link TypedEntity} index failed to complete. + */ +public class EntityIndexException 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 EntityIndexException(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 EntityIndexException(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/07643eb7/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/Entity.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/Entity.java b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/Entity.java new file mode 100644 index 0000000..c37eed3 --- /dev/null +++ b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/Entity.java @@ -0,0 +1,323 @@ +/** + * 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.entity.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; + +import org.apache.rya.api.domain.RyaURI; +import org.apache.rya.indexing.entity.storage.EntityStorage; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +/** + * An {@link Entity} is a named concept that has at least one defined structure + * and a bunch of values that fit within each of those structures. A structure is + * defined by a {@link Type}. A value that fits within that Type is a {@link Property}. + * </p> + * For example, suppose we want to represent a type of icecream as an Entity. + * First we must define what properties an icecream entity may have: + * <pre> + * Type ID: <urn:icecream> + * Properties: <urn:brand> + * <urn:flavor> + * <urn:ingredients> + * <urn:nutritionalInformation> + * </pre> + * Now we can represent our icecream whose brand is "Awesome Icecream" and whose + * flavor is "Chocolate", but has no ingredients or nutritional information, as + * an Entity by doing the following: + * <pre> + * final Entity entity = Entity.builder() + * .setSubject(new RyaURI("urn:GTIN-14/00012345600012")) + * .setExplicitType(new RyaURI("urn:icecream")) + * .setProperty(new RyaURI("urn:icecream"), new Property(new RyaURI("urn:brand"), new RyaType(XMLSchema.STRING, "Awesome Icecream"))) + * .setProperty(new RyaURI("urn:icecream"), new Property(new RyaURI("urn:flavor"), new RyaType(XMLSchema.STRING, "Chocolate"))) + * .build(); + * </pre> + * The two types of Entities that may be created are implicit and explicit. + * An implicit Entity is one who has at least one {@link Property} that matches + * the {@link Type}, but nothing has explicitly indicated it is of that Type. + * Once something has done so, it is an explicitly typed Entity. + */ +@Immutable + +public class Entity { + + private final RyaURI subject; + private final ImmutableList<RyaURI> explicitTypeIds; + + // First key is Type ID. + // Second key is Property Name. + // Value is the Property value for a specific type. + private final ImmutableMap<RyaURI, ImmutableMap<RyaURI, Property>> properties; + + private final int version; + + /** + * To construct an instances of this class, use {@link Builder}. + */ + private Entity( + final RyaURI subject, + final ImmutableList<RyaURI> explicitTypeIds, + final ImmutableMap<RyaURI, ImmutableMap<RyaURI, Property>> typeProperties, + int version) { + subject = requireNonNull(subject); + explicitTypeIds = requireNonNull(explicitTypeIds); + properties = requireNonNull(typeProperties); + version = version; + } + + /** + * @return Identifies the thing that is being represented as an Entity. + */ + public RyaURI getSubject() { + return subject; + } + + /** + * @return {@link Type}s that have been explicitly applied to the {@link Entity}. + */ + public ImmutableList<RyaURI> getExplicitTypeIds() { + return explicitTypeIds; + } + + /** + * @return All {@link Property}s that have been set for the Entity, grouped by Type ID. + */ + public ImmutableMap<RyaURI, ImmutableMap<RyaURI, Property>> getProperties() { + return properties; + } + + /** + * @return The version of this Entity. This value is used by the {@link EntityStorage} + * to prevent stale updates. + */ + public int getVersion() { + return version; + } + + @Override + public int hashCode() { + return Objects.hash(subject, explicitTypeIds, properties, version); + } + + @Override + public boolean equals(Object o) { + if(this == o) { + return true; + } + if(o instanceof Entity) { + final Entity entity = (Entity) o; + return Objects.equals(subject, entity.subject) && + Objects.equals(explicitTypeIds, entity.explicitTypeIds) && + Objects.equals(properties, entity.properties) && + version == entity.version; + } + return false; + } + + /** + * Builds an {@link TypedEntity} using this object's values for the specified {@link Type}. + * + * @param typeId - The ID of the Type the TypedEntity will be for. (not null) + * @return A TypedEntity using this object's values if any properties for the Type + * are present or if the Type was explicitly set. Otherwise an empty {@link Optional}. + */ + public Optional<TypedEntity> makeTypedEntity(RyaURI typeId) { + requireNonNull(typeId); + + final boolean explicitlyHasType = explicitTypeIds.contains(typeId); + final boolean hasTypesProperties = properties.containsKey(typeId); + + // The case where the MongoEntity can be represented as the typeId's Type. + if(explicitlyHasType || hasTypesProperties) { + // Set required fields. + final TypedEntity.Builder builder = TypedEntity.builder() + .setId( subject ) + .setTypeId( typeId ) + .setExplicitelyTyped( explicitTypeIds.contains(typeId) ); + + // Set Type's properties if present. + if(properties.containsKey(typeId)) { + properties.get(typeId).forEach( (propertyName, property) -> builder.setProperty(property)); + } + + return Optional.of( builder.build() ); + } + + // This MongoEntity can not be represented by the typeId's Type. + return Optional.empty(); + } + + /** + * @return An empty instance of {@link Builder}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Create a {@link Builder} initialized with an {@link Entity}'s values. + * + * @param entity - The Entity the builder will be based on. (not null) + * @return A {@link Builder} loaded with {@code entity}'s values. + */ + public static Builder builder(Entity entity) { + return new Builder(entity); + } + + /** + * Builds instances of {@link Entity}. + */ + @ParametersAreNonnullByDefault + public static class Builder { + + private RyaURI subject = null; + private final List<RyaURI> explicitTypes = new ArrayList<>(); + private final Map<RyaURI, Map<RyaURI, Property>> properties = new HashMap<>(); + + private int version = 0; + + /** + * Constructs an empty instance of {@link Builder}. + */ + public Builder() { } + + /** + * Constructs an instance of {@link Builder}. + * + * @param entity - The Entity the builder will be based on. (not null) + */ + public Builder(Entity entity) { + requireNonNull(entity); + + subject = entity.getSubject(); + explicitTypes.addAll( entity.getExplicitTypeIds() ); + + for(final Entry<RyaURI, ImmutableMap<RyaURI, Property>> entry : entity.getProperties().entrySet()) { + properties.put(entry.getKey(), Maps.newHashMap(entry.getValue())); + } + + version = entity.getVersion(); + } + + /** + * @param subject - Identifies the {@link TypedEntity}. + * @return This {@link Builder} so that method invocations may be chained. + */ + public Builder setSubject(@Nullable final RyaURI subject) { + subject = subject; + return this; + } + + /** + * @param typeId - A {@link Type} that has been explicity set for the {@link TypedEntity}. + * @return This {@link Builder} so that method invocations may be chained. + */ + public Builder setExplicitType(@Nullable final RyaURI typeId) { + if(typeId != null) { + explicitTypes.add(typeId); + } + return this; + } + + /** + * Removed a Type ID from the set of explicit Type IDs. + * + * @param typeId - The Type ID to remove from the set of explicit types. + * @return This {@link Builder} so that method invocations may be chained. + */ + public Builder unsetExplicitType(@Nullable final RyaURI typeId) { + if(typeId != null) { + explicitTypes.remove(typeId); + } + return this; + } + + /** + * Adds a {@link Property} for a specific {@link Type} of {@link TypedEntity}. + * + * @param typeId - The Type the Property is for. + * @param property - The Property values to add. + * @return This {@link Builder} so that method invocations may be chained. + */ + public Builder setProperty(@Nullable final RyaURI typeId, @Nullable final Property property) { + if(typeId != null && property != null) { + if(!properties.containsKey(typeId)) { + properties.put(typeId, new HashMap<>()); + } + + properties.get(typeId).put(property.getName(), property); + } + return this; + } + + /** + * Removes a {@link Property} for a specific {@link Type} of {@link TypedEntity}. + * + * @param typeId - The Type the Property will be removed from. + * @param propertyName - The name of the Property to remove. + * @return This {@link Builder} so that method invocations may be chained. + */ + public Builder unsetProperty(@Nullable final RyaURI typeId, @Nullable final RyaURI propertyName) { + if(typeId != null && propertyName != null) { + if(properties.containsKey(typeId)) { + final Map<RyaURI, Property> typedProperties = properties.get(typeId); + if(typedProperties.containsKey(propertyName)) { + typedProperties.remove(propertyName); + } + } + } + return this; + } + + /** + * @param version - The version of this Entity. This value is used by the + * {@link EntityStorage} to prevent stale updates. + * @return This {@link Builder} so that method invocations may be chained. + */ + public Builder setVersion(int version) { + version = version; + return this; + } + + /** + * @return Builds an instance of {@link Entity} using this builder's values. + */ + public Entity build() { + final ImmutableMap.Builder<RyaURI, ImmutableMap<RyaURI, Property>> propertiesBuilder = ImmutableMap.builder(); + for(final Entry<RyaURI, Map<RyaURI, Property>> entry : properties.entrySet()) { + propertiesBuilder.put(entry.getKey(), ImmutableMap.copyOf( entry.getValue() )); + } + + return new Entity(subject, + ImmutableList.copyOf( explicitTypes ), + propertiesBuilder.build(), + version); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/Property.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/Property.java b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/Property.java new file mode 100644 index 0000000..4388024 --- /dev/null +++ b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/Property.java @@ -0,0 +1,83 @@ +/** + * 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.entity.model; + +import static java.util.Objects.requireNonNull; + +import java.util.Objects; + +import javax.annotation.ParametersAreNonnullByDefault; +import javax.annotation.concurrent.Immutable; + +import mvm.rya.api.domain.RyaType; +import mvm.rya.api.domain.RyaURI; + +/** + * A value that has been set for an {@link TypedEntity}. + */ +@Immutable +@ParametersAreNonnullByDefault +public class Property { + + private final RyaURI name; + private final RyaType value; + + /** + * Constructs an instance of {@link Property}. + * + * @param name - Uniquely identifies the {@link Property}. (not null) + * @param value - The value of the {@link Property}. (not null) + */ + public Property(final RyaURI name, final RyaType value) { + this.name = requireNonNull(name); + this.value = requireNonNull(value); + } + + /** + * @return Uniquely identifies the {@link Property}. + */ + public RyaURI getName() { + return name; + } + + /** + * @return The value of the {@link Property}. + */ + public RyaType getValue() { + return value; + } + + @Override + public int hashCode() { + return Objects.hash(name, value); + } + + @Override + public boolean equals(final Object o) { + if(this == o) { + return true; + } + if(o instanceof Property) { + final Property field = (Property) o; + return Objects.equals(name, field.name) && + Objects.equals(value, field.value); + } + return false; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/Type.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/Type.java b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/Type.java new file mode 100644 index 0000000..7d8e0ee --- /dev/null +++ b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/Type.java @@ -0,0 +1,103 @@ +/** + * 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.entity.model; + +import static java.util.Objects.requireNonNull; + +import java.util.Objects; + +import javax.annotation.ParametersAreNonnullByDefault; +import javax.annotation.concurrent.Immutable; + +import org.apache.rya.indexing.entity.storage.TypeStorage; + +import com.google.common.collect.ImmutableSet; + +import mvm.rya.api.domain.RyaURI; + +/** + * Defines the structure of an {@link TypedEntity}. + * </p> + * For example, suppose you want a {@link Type} that defines what properties are + * available for icecream. It could be modeled like this: + * <pre> + * Type ID: <urn:icecream> + * Properties: <urn:brand> + * <urn:flavor> + * <urn:ingredients> + * <urn:nutritionalInformation> + * </pre> + */ +@Immutable +@ParametersAreNonnullByDefault +public class Type { + + /** + * Uniquely identifies the Type within a {@link TypeStorage}. + */ + private final RyaURI id; + + /** + * The names of {@link Property}s that may be part of an {@link TypedEntity} of this type. + */ + private final ImmutableSet<RyaURI> propertyNames; + + /** + * Constructs an instance of {@link Type}. + * + * @param id - Uniquely identifies the Type within a {@link TypeStorage}. (not null) + * @param propertyNames - The names of {@link Property}s that may be part of an {@link TypedEntity} of this type. (not null) + */ + public Type(final RyaURI id, final ImmutableSet<RyaURI> propertyNames) { + this.id = requireNonNull(id); + this.propertyNames = requireNonNull(propertyNames); + } + + /** + * @return Uniquely identifies the Type within a {@link TypeStorage}. + */ + public RyaURI getId() { + return id; + } + + /** + * @return The names of {@link Property}s that may be part of an {@link TypedEntity} of this type. + */ + public ImmutableSet<RyaURI> getPropertyNames() { + return propertyNames; + } + + @Override + public int hashCode() { + return Objects.hash(id, propertyNames); + } + + @Override + public boolean equals(final Object o) { + if(this == o) { + return true; + } + if(o instanceof Type) { + final Type type = (Type) o; + return Objects.equals(id, type.id) && + Objects.equals(propertyNames, type.propertyNames); + } + return false; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/TypedEntity.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/TypedEntity.java b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/TypedEntity.java new file mode 100644 index 0000000..a49c8d2 --- /dev/null +++ b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/model/TypedEntity.java @@ -0,0 +1,242 @@ +/** + * 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.entity.model; + +import static java.util.Objects.requireNonNull; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; +import javax.annotation.concurrent.Immutable; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableMap; + +import mvm.rya.api.domain.RyaType; +import mvm.rya.api.domain.RyaURI; + +/** + * A {@link TypedEntity} is a view of an {@link Entity} that has had a specific + * {@link Type} applied to it. + */ +@Immutable +@ParametersAreNonnullByDefault +public class TypedEntity { + + /** + * The Subject of the {@link Entity} this view was derived from. + */ + private final RyaURI subject; + + /** + * The ID of the {@link Type} that defines the structure of this TypedEntity. + */ + private final RyaURI typeId; + + /** + * {@code true} if the Entity's Type has been explicitly set to the {@link #typeId} + * value; {@code false} if it is implicit (this Entity only exists because it has + * properties that happen to match that type's properties). + */ + private final boolean explicitlyTyped; + + /** + * The optional {@link Property} values of this {@link TypedEntity}. + * </p> + * They are mapped from property name to property object for quick lookup. + */ + private final ImmutableMap<RyaURI, Property> optionalFields; + + /** + * Constructs an instance of {@link TypedEntity}. + * + * @param subject - The Subject of the {@link Entity} this view was derived from. (not null) + * @param dataTypeId - The ID of the {@link Type} that defines the structure of this Entity. (not null) + * @param explicitlyTyped - {@code true} if the Entity's Type has been explicitly set to the + * {@link #typeId} value; {@code false} if it is implicit (this Entity only exists because + * it has properties that happen to match that type's properties). + * @param properties - The optional {@link Property} values of this {@link TypedEntity}. (not null) + */ + private TypedEntity(final RyaURI subject, + final RyaURI dataTypeId, + boolean explicitlyTyped, + final ImmutableMap<RyaURI, Property> optionalFields) { + this.subject = requireNonNull(subject); + this.typeId = requireNonNull(dataTypeId); + this.optionalFields = requireNonNull(optionalFields); + this.explicitlyTyped = explicitlyTyped; + } + + /** + * @return The Subject of the {@link Entity} this view was derived from. + */ + public RyaURI getSubject() { + return subject; + } + + /** + * @return The ID of the {@link Type} that defines the structure of this Entity. + */ + public RyaURI getTypeId() { + return typeId; + } + + /** + * @return {@code true} if the Entity's Type has been explicitly set to the {@link #typeId} + * value; {@code false} if it is implicit (this Entity only exists because it has + * properties that happen to match that type's properties). + */ + public boolean isExplicitlyTyped() { + return explicitlyTyped; + } + + /** + * @return The optional {@link Property} values of this {@link TypedEntity}. + */ + public ImmutableCollection<Property> getProperties() { + return optionalFields.values(); + } + + /** + * Get the value of a specific {@link Property} of this {@link TypedEntity} + * if the property has been set. + * + * @param propertyName - The name of {@link Property} that may be in this Entity. (not null) + * @return The value of the Property if it has been set. + */ + public Optional<RyaType> getPropertyValue(final RyaURI propertyName) { + requireNonNull(propertyName); + + final Property field = optionalFields.get(propertyName); + return field == null ? + Optional.absent() : + Optional.of( field.getValue() ); + } + + @Override + public int hashCode() { + return Objects.hash(subject, typeId, optionalFields); + } + + @Override + public boolean equals(Object o) { + if(this == o) { + return true; + } + if(o instanceof TypedEntity) { + final TypedEntity other = (TypedEntity) o; + return Objects.equals(subject, other.subject) && + Objects.equals(typeId, other.typeId) && + Objects.equals(optionalFields, other.optionalFields); + } + return false; + } + + /** + * @return An empty instance of {@link Builder}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Makes a {@link Builder} that is populated with an existing {@link TypedEntity}. + * + * @param entity - The initial values of the builder. (not null) + * @return An instance of {@link Builder} loaded with {@code entity}'s values. + */ + public static Builder builder(TypedEntity entity) { + requireNonNull(entity); + + final Builder builder = builder() + .setId(entity.getSubject()) + .setTypeId(entity.getTypeId()); + + entity.getProperties().forEach(builder::setProperty); + + return builder; + } + + /** + * Builds instances of {@link TypedEntity}. + */ + @ParametersAreNonnullByDefault + public static class Builder { + + private RyaURI subject; + private RyaURI typeId; + private boolean explicitlyTyped = false; + private final Map<RyaURI, Property> properties = new HashMap<>(); + + /** + * @param subject - The Subject of the {@link Entity} this view was derived from. + * @return This {@link Builder} so that method invocations may be chained. + */ + public Builder setId(@Nullable final RyaURI subject) { + this.subject = subject; + return this; + } + + /** + * @param typeId - The ID of the {@link Type} that defines the structure of this Entity. + * @return This {@link Builder} so that method invocations may be chained. + */ + public Builder setTypeId(@Nullable final RyaURI typeId) { + this.typeId = typeId; + return this; + } + + /** + * @param explicitlyTyped - {@code true} if the Entity's Type has been explicitly + * set to the {@link #typeId} value; {@code false} if it is implicit (this Entity + * only exists because it has properties that happen to match that type's properties). + * @return This {@link Builder} so that method invocations may be chained. + */ + public Builder setExplicitelyTyped(boolean explicitlyTyped) { + this.explicitlyTyped = explicitlyTyped; + return this; + } + + /** + * @param property - A {@link Property} of the {@link TypedEntity}. + * @return This {@link Builder} so that method invocations may be chained. + */ + public Builder setProperty(@Nullable final Property property) { + if(property != null) { + properties.put(property.getName(), property); + } + return this; + } + + /** + * @return An instance of {@link TypedEntity} built with this builder's values. + */ + public TypedEntity build() { + return new TypedEntity( + subject, + typeId, + explicitlyTyped, + ImmutableMap.copyOf(properties)); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/org/apache/rya/indexing/entity/query/EntityQueryNode.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/query/EntityQueryNode.java b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/query/EntityQueryNode.java new file mode 100644 index 0000000..bb14922 --- /dev/null +++ b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/query/EntityQueryNode.java @@ -0,0 +1,238 @@ +/** + * 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.entity.query; + +import static java.util.Objects.requireNonNull; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Optional; + +import javax.annotation.ParametersAreNonnullByDefault; + +import org.apache.rya.indexing.entity.model.Type; +import org.apache.rya.indexing.entity.storage.EntityStorage; +import org.openrdf.model.vocabulary.RDF; +import org.openrdf.query.BindingSet; +import org.openrdf.query.QueryEvaluationException; +import org.openrdf.query.algebra.StatementPattern; +import org.openrdf.query.algebra.Var; +import org.openrdf.query.algebra.evaluation.impl.ExternalSet; + +import com.google.common.collect.ImmutableMap; + +import info.aduna.iteration.CloseableIteration; +import mvm.rya.api.domain.RyaURI; +import mvm.rya.rdftriplestore.evaluation.ExternalBatchingIterator; + +/** + * TODO impl, test, doc + */ +@ParametersAreNonnullByDefault +public class EntityQueryNode extends ExternalSet implements ExternalBatchingIterator { + + /** + * The RyaURI that when used as the Predicate of a Statement Pattern indicates the Type of the Entities. + */ + private static final RyaURI TYPE_ID_URI = new RyaURI(RDF.TYPE.toString()); + + // Provided at construction time. + private final Type type; + private final Collection<StatementPattern> patterns; + private final EntityStorage entities; + + // Information about the subject of the patterns. + private final boolean subjectIsConstant; + private final Optional<String> subjectConstant; + private final Optional<String> subjectVar; + + // Information about the objects of the patterns. + + // XXX what does this map? property name -> binding variable? + // for any property of the entity that has a variable, have to fill it in? + private final ImmutableMap<RyaURI, String> objectVariables; + + + /** + * Constructs an instance of {@link EntityQueryNode}. + * + * @param type - The type of {@link Entity} this node matches. (not null) + * @param patterns - The query StatementPatterns that are solved using an + * Entity of the Type. (not null) + * @param entities - The {@link EntityStorage} that will be searched to match + * {@link BindingSet}s when evaluating a query. (not null) + */ + public EntityQueryNode(final Type type, final Collection<StatementPattern> patterns, final EntityStorage entities) throws IllegalStateException { + this.type = requireNonNull(type); + this.patterns = requireNonNull(patterns); + this.entities = requireNonNull(entities); + + // Subject based preconditions. + verifySameSubjects(patterns); + + // Predicate based preconditions. + verifyAllPredicatesAreConstants(patterns); + verifyHasCorrectTypePattern(type, patterns); + verifyAllPredicatesPartOfType(type, patterns); + + // 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() ); + } + + // TODO Also, map each variable that is in an Object spot each variable can be mapped to a property name as well + // Processing note: + // Any constant that appears in the Object portion of the SP will be used to make sure they match. + + objectVariables = null; + } + + /** + * 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(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(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); + } + } + } + + /** + * Verifies a single Statement Pattern defines the Type of Entity this query node matches. + * + * @param type - The expected Type. (not null) + * @param patterns - The patterns to check. (not null) + * @throws IllegalStateException No Type or the wrong Type is specified by the patterns. + */ + private static void verifyHasCorrectTypePattern(Type type, Collection<StatementPattern> patterns) throws IllegalStateException { + requireNonNull(type); + requireNonNull(patterns); + + boolean typeFound = false; + + for(final StatementPattern pattern : patterns) { + final RyaURI predicate = new RyaURI(pattern.getPredicateVar().getValue().toString()); + + if(predicate.equals(TYPE_ID_URI)) { + final RyaURI typeId = new RyaURI( pattern.getObjectVar().getValue().stringValue() ); + if(typeId.equals(type.getId())) { + typeFound = true; + } else { + throw new IllegalStateException("Statement Pattern encountred for a Type that does not match the expected Type." + + " Expected Type = '" + type.getId().getData() + "' Found Type = '" + typeId.getData() + "'"); + } + } + } + + if(!typeFound) { + throw new IllegalStateException("The collection of Statement Patterns that this node matches must define which Type they match."); + } + } + + /** + * Verify all of the patterns have predicates that match one of the Type's property names. + * + * @param type - The Type the patterns match. (not null) + * @param patterns - The patterns to check. + * @throws IllegalStateException If any of the non-type defining Statement Patterns + * contain a predicate that does not match one of the Type's property names. + */ + private static void verifyAllPredicatesPartOfType(Type type, Collection<StatementPattern> patterns) throws IllegalStateException { + requireNonNull(type); + requireNonNull(patterns); + + for(final StatementPattern pattern : patterns) { + // Skip TYPE patterns. + final RyaURI predicate = new RyaURI( pattern.getPredicateVar().getValue().toString() ); + if(predicate.equals(TYPE_ID_URI)) { + continue; + } + + if(!type.getPropertyNames().contains(predicate)) { + throw new IllegalStateException("The Predicate of a Statement Pattern must be a property name for the Type. " + + "Type ID: '" + type.getId().getData() + "' Pattern: " + pattern); + } + } + } + + + + + + + @Override + public CloseableIteration<BindingSet, QueryEvaluationException> evaluate(Collection<BindingSet> bindingSets) throws QueryEvaluationException { + // TODO Auto-generated method stub + return null; + } + + @Override + public CloseableIteration<BindingSet, QueryEvaluationException> evaluate(BindingSet bindingSet) throws QueryEvaluationException { + requireNonNull(bindingSet); + + // ... ok, so if the subject needs to be filled in, then we need to see if the subject variable is in the binding set. + // if it is, fetch that value and then fetch the entity for the subject. + + // if it isn't, fetch the entity for the constant? + + + // RETURN AN EMPTY ITERATION IF IT CAN NOT FILL IT IN! + + // for all variables in the OBJECT portion of the SPs, fill 'em in using the entity that is stored in the index. + + // x = alice's SSN + // y = blue <-- how do i know this is for urn:eye property? FROM THE STATEMENT PATTERN. look for the ?y in the SP, and that has the property name in it. + + // TODO Auto-generated method stub + return null; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/CloseableIterator.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/CloseableIterator.java b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/CloseableIterator.java new file mode 100644 index 0000000..3b2e10f --- /dev/null +++ b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/CloseableIterator.java @@ -0,0 +1,29 @@ +/** + * 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.entity.storage; + +import java.io.Closeable; +import java.util.Iterator; + +/** + * An {@link Iterator} that is also {@link Closeable}. + * + * @param <T> - The type of object that will be iterated over. + */ +public interface CloseableIterator<T> extends Iterator<T>, Closeable { } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/07643eb7/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/EntityStorage.java ---------------------------------------------------------------------- diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/EntityStorage.java b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/EntityStorage.java new file mode 100644 index 0000000..44db8e2 --- /dev/null +++ b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/EntityStorage.java @@ -0,0 +1,154 @@ +/** + * 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.entity.storage; + +import java.util.Optional; +import java.util.Set; + +import javax.annotation.ParametersAreNonnullByDefault; + +import org.apache.rya.indexing.entity.EntityIndexException; +import org.apache.rya.indexing.entity.model.Entity; +import org.apache.rya.indexing.entity.model.Property; +import org.apache.rya.indexing.entity.model.Type; +import org.apache.rya.indexing.entity.model.TypedEntity; + +import mvm.rya.api.domain.RyaURI; + +/** + * Stores and provides access to {@link Entity}s. + */ +@ParametersAreNonnullByDefault +public interface EntityStorage { + + /** + * Creates a new {@link Entity} within the storage. The new Entity's subject must be unique. + * + * @param entity - The {@link Entity} to create. (not null) + * @throws EntityAlreadyExistsException An {@link Entity} could not be created because one already exists for the Subject. + * @throws EntityStorageException A problem occurred while creating the Entity. + */ + public void create(Entity entity) throws EntityAlreadyExistsException, EntityStorageException; + + /** + * Get an {@link Entity} from the storage by its subject. + * + * @param subject - Identifies which {@link Entity} to get. (not null) + * @return The {@link Entity} if one exists for the subject. + * @throws EntityStorageException A problem occurred while fetching the Entity from the storage. + */ + public Optional<Entity> get(RyaURI subject) throws EntityStorageException; + + /** + * Update the state of an {@link Entity}. + * + * @param old - The Entity the changes were applied to. (not null) + * @param updated - The updated Entity to store. (not null) + * @throws StaleUpdateException The {@code old} Entity does not match any Entities that are stored. + * @throws EntityStorageException A problem occurred while updating the Entity within the storage. + */ + public void update(Entity old, Entity updated) throws StaleUpdateException, EntityStorageException; + + /** + * Search the stored {@link Entity}s that have a specific {@link Type} as + * well as the provided {@link Property} values. + * + * @param type - The {@link Type} of the Entities. (not null) + * @param properties - The {@link Property} values that must be set on the Entity. (not null) + * @return A {@link CloseableIterator} over the {@link TypedEntity}s that match the search parameters. + * @throws EntityStorageException A problem occurred while searching the storage. + */ + public CloseableIterator<TypedEntity> search(Type type, Set<Property> properties) throws EntityStorageException; + + /** + * Deletes an {@link Entity} from the storage. + * + * @param subject -Identifies which {@link Entity} to delete. (not null) + * @return {@code true} if something was deleted; otherwise {@code false}. + * @throws EntityStorageException A problem occurred while deleting from the storage. + */ + public boolean delete(RyaURI subject) throws EntityStorageException; + + /** + * Indicates a problem while interacting with an {@link EntityStorage}. + */ + public static class EntityStorageException extends EntityIndexException { + 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 EntityStorageException(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 EntityStorageException(String message, Throwable cause) { + super(message, cause); + } + } + + /** + * An {@link Entity} could not be created because one already exists for the Subject. + */ + public static class EntityAlreadyExistsException extends EntityStorageException { + private static final long serialVersionUID = 1L; + + public EntityAlreadyExistsException(String message) { + super(message); + } + + public EntityAlreadyExistsException(String message, Throwable cause) { + super(message, cause); + } + } + + /** + * An {@link TypedEntity} could not be updated because the old state does not + * match the current state. + */ + public static class StaleUpdateException extends EntityStorageException { + private static final long serialVersionUID = 1L; + + public StaleUpdateException(String message) { + super(message); + } + + public StaleUpdateException(String message, Throwable cause) { + super(message, cause); + } + } +} \ No newline at end of file
