You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rya.apache.org by pu...@apache.org on 2017/05/09 14:09:37 UTC
[2/2] incubator-rya git commit: RYA-250 Smart URI; Closes #148
RYA-250 Smart URI; Closes #148
Project: http://git-wip-us.apache.org/repos/asf/incubator-rya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-rya/commit/2e71ff2a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-rya/tree/2e71ff2a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-rya/diff/2e71ff2a
Branch: refs/heads/master
Commit: 2e71ff2a711fc857533f87b9af5c7258ca897823
Parents: 4b755c6
Author: eric.white <Er...@parsons.com>
Authored: Mon Feb 27 11:12:05 2017 -0500
Committer: pujav65 <pu...@gmail.com>
Committed: Tue May 9 10:09:03 2017 -0400
----------------------------------------------------------------------
.../org/apache/rya/api/domain/RyaTypeUtils.java | 157 +++++
extras/indexing/pom.xml | 5 +
.../rya/indexing/entity/model/Entity.java | 117 +++-
.../storage/mongo/EntityDocumentConverter.java | 18 +-
.../storage/mongo/MongoEntityStorage.java | 6 +-
.../storage/mongo/key/MongoDbSafeKey.java | 81 +++
.../rya/indexing/mongodb/MongoDbSmartUri.java | 172 +++++
.../rya/indexing/smarturi/SmartUriAdapter.java | 688 +++++++++++++++++++
.../indexing/smarturi/SmartUriException.java | 36 +
.../rya/indexing/smarturi/SmartUriStorage.java | 78 +++
.../rya/indexing/mongo/MongoDbSmartUriTest.java | 333 +++++++++
extras/indexingExample/pom.xml | 6 +
extras/rya.merger/pom.xml | 5 -
pom.xml | 21 +-
14 files changed, 1705 insertions(+), 18 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e71ff2a/common/rya.api/src/main/java/org/apache/rya/api/domain/RyaTypeUtils.java
----------------------------------------------------------------------
diff --git a/common/rya.api/src/main/java/org/apache/rya/api/domain/RyaTypeUtils.java b/common/rya.api/src/main/java/org/apache/rya/api/domain/RyaTypeUtils.java
new file mode 100644
index 0000000..8ca65fc
--- /dev/null
+++ b/common/rya.api/src/main/java/org/apache/rya/api/domain/RyaTypeUtils.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.rya.api.domain;
+
+import java.util.Date;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.format.ISODateTimeFormat;
+import org.openrdf.model.URI;
+import org.openrdf.model.vocabulary.XMLSchema;
+
+/**
+ * Utility methods for using {@link RyaType}.
+ */
+public final class RyaTypeUtils {
+ /**
+ * Private constructor to prevent instantiation.
+ */
+ private RyaTypeUtils() {
+ }
+
+ /**
+ * Creates a boolean {@link RyaType} object.
+ * @param value the {@link Boolean} object.
+ * @return the {@link RyaType} with the data type set to
+ * {@link XMLSchema#BOOLEAN} and the data set to the specified
+ * {@code value}.
+ */
+ public static RyaType booleanRyaType(final Boolean value) {
+ return new RyaType(XMLSchema.BOOLEAN, Boolean.toString(value));
+ }
+
+ /**
+ * Creates a byte {@link RyaType} object.
+ * @param value the {@link Byte} object.
+ * @return the {@link RyaType} with the data type set to
+ * {@link XMLSchema#BYTE} and the data set to the specified {@code value}.
+ */
+ public static RyaType byteRyaType(final Byte value) {
+ return new RyaType(XMLSchema.BYTE, Byte.toString(value));
+ }
+
+ /**
+ * Creates a date {@link RyaType} object.
+ * @param value the {@link Date} object.
+ * @return the {@link RyaType} with the data type set to
+ * {@link XMLSchema#DATETIME} and the data set to the specified
+ * {@code value}.
+ */
+ public static RyaType dateRyaType(final Date value) {
+ final DateTime dateTime = new DateTime(value.getTime());
+ return dateRyaType(dateTime);
+ }
+
+ /**
+ * Creates a date/time {@link RyaType} object.
+ * @param value the {@link DateTime} object.
+ * @return the {@link RyaType} with the data type set to
+ * {@link XMLSchema#DATETIME} and the data set to the specified
+ * {@code value}.
+ */
+ public static RyaType dateRyaType(final DateTime value) {
+ final StringBuffer sb = new StringBuffer();
+ ISODateTimeFormat.dateTime().withZone(DateTimeZone.UTC).printTo(sb, value.getMillis());
+ final String formattedDate = sb.toString();
+ return new RyaType(XMLSchema.DATETIME, formattedDate);
+ }
+
+ /**
+ * Creates a double {@link RyaType} object.
+ * @param value the {@link Double} object.
+ * @return the {@link RyaType} with the data type set to
+ * {@link XMLSchema#DOUBLE} and the data set to the specified {@code value}.
+ */
+ public static RyaType doubleRyaType(final Double value) {
+ return new RyaType(XMLSchema.DOUBLE, Double.toString(value));
+ }
+
+ /**
+ * Creates a float {@link RyaType} object.
+ * @param value the {@link Float} object.
+ * @return the {@link RyaType} with the data type set to
+ * {@link XMLSchema#FLOAT} and the data set to the specified {@code value}.
+ */
+ public static RyaType floatRyaType(final Float value) {
+ return new RyaType(XMLSchema.FLOAT, Float.toString(value));
+ }
+
+ /**
+ * Creates an integer {@link RyaType} object.
+ * @param value the {@link Integer} object.
+ * @return the {@link RyaType} with the data type set to
+ * {@link XMLSchema#INTEGER} and the data set to the specified
+ * {@code value}.
+ */
+ public static RyaType intRyaType(final Integer value) {
+ return new RyaType(XMLSchema.INTEGER, Integer.toString(value));
+ }
+
+ /**
+ * Creates a long {@link RyaType} object.
+ * @param value the {@link Long} object.
+ * @return the {@link RyaType} with the data type set to
+ * {@link XMLSchema#LONG} and the data set to the specified {@code value}.
+ */
+ public static RyaType longRyaType(final Long value) {
+ return new RyaType(XMLSchema.LONG, Long.toString(value));
+ }
+
+ /**
+ * Creates a short {@link RyaType} object.
+ * @param value the {@link Short} object.
+ * @return the {@link RyaType} with the data type set to
+ * {@link XMLSchema#SHORT} and the data set to the specified {@code value}.
+ */
+ public static RyaType shortRyaType(final Short value) {
+ return new RyaType(XMLSchema.SHORT, Short.toString(value));
+ }
+
+ /**
+ * Creates a string {@link RyaType} object.
+ * @param value the {@link String} object.
+ * @return the {@link RyaType} with the data type set to
+ * {@link XMLSchema#STRING} and the data set to the specified {@code value}.
+ */
+ public static RyaType stringRyaType(final String value) {
+ return new RyaType(XMLSchema.STRING, value);
+ }
+
+ /**
+ *
+ * Creates a URI {@link RyaType} object.
+ * @param value the {@link URI} object.
+ * @return the {@link RyaType} with the data type set to
+ * {@link XMLSchema#ANYURI} and the data set to the specified {@code value}.
+ */
+ public static RyaType uriRyaType(final URI value) {
+ return new RyaType(XMLSchema.ANYURI, value.stringValue());
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e71ff2a/extras/indexing/pom.xml
----------------------------------------------------------------------
diff --git a/extras/indexing/pom.xml b/extras/indexing/pom.xml
index e38e75d..755646c 100644
--- a/extras/indexing/pom.xml
+++ b/extras/indexing/pom.xml
@@ -72,6 +72,11 @@
<artifactId>commons-codec</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ </dependency>
+
<!-- PCJ Indexing -->
<dependency>
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e71ff2a/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
index 3eb4c39..a90e469 100644
--- 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
@@ -29,8 +29,12 @@ import java.util.Objects;
import java.util.Optional;
import org.apache.http.annotation.Immutable;
+import org.apache.log4j.Logger;
import org.apache.rya.api.domain.RyaURI;
import org.apache.rya.indexing.entity.storage.EntityStorage;
+import org.apache.rya.indexing.smarturi.SmartUriAdapter;
+import org.apache.rya.indexing.smarturi.SmartUriException;
+import org.openrdf.model.URI;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -73,6 +77,7 @@ import edu.umd.cs.findbugs.annotations.Nullable;
@Immutable
@DefaultAnnotation(NonNull.class)
public class Entity {
+ private static final Logger log = Logger.getLogger(Entity.class);
private final RyaURI subject;
private final ImmutableList<RyaURI> explicitTypeIds;
@@ -84,18 +89,60 @@ public class Entity {
private final int version;
+ private URI smartUri = null;
+
/**
- * To construct an instances of this class, use {@link Builder}.
+ * To construct an instance of this class, use {@link Builder}.
+ * @param subject Identifies the thing that is being represented as an
+ * Entity.
+ * @param explicitTypeIds {@link Type}s that have been explicitly applied to
+ * the {@link Entity}.
+ * @param typeProperties All {@link Property}s that have been set for the
+ * Entity, grouped by Type ID.
+ * @param version The version of this Entity. This value is used by the
+ * {@link EntityStorage} to prevent stale updates.
+ * @param smartUri the Smart {@link URI} representation of this
+ * {@link Entity}.
*/
private Entity(
final RyaURI subject,
final ImmutableList<RyaURI> explicitTypeIds,
final ImmutableMap<RyaURI, ImmutableMap<RyaURI, Property>> typeProperties,
- final int version) {
+ final int version,
+ final URI smartUri) {
this.subject = requireNonNull(subject);
this.explicitTypeIds = requireNonNull(explicitTypeIds);
properties = requireNonNull(typeProperties);
this.version = version;
+ if (smartUri != null) {
+ this.smartUri = smartUri;
+ } else {
+ // if Smart URI isn't provided create it from the given properties
+ try {
+ this.smartUri = SmartUriAdapter.serializeUriEntity(this);
+ } catch (final SmartUriException e) {
+ log.error("Unable to create a Smart URI for the entity", e);
+ }
+ }
+ }
+
+ /**
+ * To construct an instance of this class, use {@link Builder}.
+ * @param subject Identifies the thing that is being represented as an
+ * Entity.
+ * @param explicitTypeIds {@link Type}s that have been explicitly applied to
+ * the {@link Entity}.
+ * @param typeProperties All {@link Property}s that have been set for the
+ * Entity, grouped by Type ID.
+ * @param version The version of this Entity. This value is used by the
+ * {@link EntityStorage} to prevent stale updates.
+ */
+ private Entity(
+ final RyaURI subject,
+ final ImmutableList<RyaURI> explicitTypeIds,
+ final ImmutableMap<RyaURI, ImmutableMap<RyaURI, Property>> typeProperties,
+ final int version) {
+ this(subject, explicitTypeIds, typeProperties, version, null);
}
/**
@@ -127,6 +174,45 @@ public class Entity {
return version;
}
+ /**
+ * @return the Smart {@link URI} representation of this {@link Entity}.
+ */
+ public URI getSmartUri() {
+ return smartUri;
+ }
+
+ /**
+ * Does a lookup to see if the {@link Entity} contains the specified
+ * property for the specified type.
+ * @param typeRyaUri the type {@link RyaURI}. (not {@code null})
+ * @param propertyRyaUri the property {@link RyaURI}. (not {@code null})
+ * @return the {@link Property} or an empty {@link Optional} if it could not
+ * be found in the {@link Entity}.
+ */
+ public Optional<Property> lookupTypeProperty(final Type type, final RyaURI propertyRyaUri) {
+ requireNonNull(type);
+ return lookupTypeProperty(type.getId(), propertyRyaUri);
+ }
+
+ /**
+ * Does a lookup to see if the {@link Entity} contains the specified
+ * property for the specified type.
+ * @param typeRyaUri the type {@link RyaURI}. (not {@code null})
+ * @param propertyRyaUri the property {@link RyaURI}. (not {@code null})
+ * @return the {@link Property} or an empty {@link Optional} if it could not
+ * be found in the {@link Entity}.
+ */
+ public Optional<Property> lookupTypeProperty(final RyaURI typeRyaUri, final RyaURI propertyRyaUri) {
+ requireNonNull(typeRyaUri);
+ requireNonNull(propertyRyaUri);
+ final ImmutableMap<RyaURI, Property> typePropertyMap = properties.get(typeRyaUri);
+ Optional<Property> property = Optional.empty();
+ if (typePropertyMap != null) {
+ property = Optional.of(typePropertyMap.get(propertyRyaUri));
+ }
+ return property;
+ }
+
@Override
public int hashCode() {
return Objects.hash(subject, explicitTypeIds, properties, version);
@@ -206,6 +292,7 @@ public class Entity {
private RyaURI subject = null;
private final List<RyaURI> explicitTypes = new ArrayList<>();
private final Map<RyaURI, Map<RyaURI, Property>> properties = new HashMap<>();
+ private URI smartUri = null;
private int version = 0;
@@ -230,6 +317,8 @@ public class Entity {
}
version = entity.getVersion();
+
+ smartUri = entity.getSmartUri();
}
/**
@@ -303,6 +392,27 @@ public class Entity {
}
/**
+ * @param smartUri - the Smart {@link URI} representation of this
+ * {@link Entity}.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setSmartUri(final URI smartUri) {
+ this.smartUri = smartUri;
+ return this;
+ }
+
+ /**
+ * Indicates that the builder should rebuild the Smart URI. This should
+ * be used when properties or anything else in the {@link Entity} has
+ * changed.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder rebuildSmartUri() {
+ setSmartUri(null);
+ 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.
@@ -324,7 +434,8 @@ public class Entity {
return new Entity(subject,
ImmutableList.copyOf( explicitTypes ),
propertiesBuilder.build(),
- version);
+ version,
+ smartUri);
}
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e71ff2a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/EntityDocumentConverter.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/EntityDocumentConverter.java b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/EntityDocumentConverter.java
index a46fd5a..f2647ef 100644
--- a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/EntityDocumentConverter.java
+++ b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/EntityDocumentConverter.java
@@ -27,7 +27,9 @@ import org.apache.rya.api.domain.RyaType;
import org.apache.rya.api.domain.RyaURI;
import org.apache.rya.indexing.entity.model.Entity;
import org.apache.rya.indexing.entity.model.Property;
+import org.apache.rya.indexing.entity.storage.mongo.key.MongoDbSafeKey;
import org.bson.Document;
+import org.openrdf.model.impl.URIImpl;
import edu.umd.cs.findbugs.annotations.DefaultAnnotation;
import edu.umd.cs.findbugs.annotations.NonNull;
@@ -42,6 +44,7 @@ public class EntityDocumentConverter implements DocumentConverter<Entity> {
public static final String EXPLICIT_TYPE_IDS = "explicitTypeIds";
public static final String PROPERTIES = "properties";
public static final String VERSION = "version";
+ public static final String SMART_URI = "smartUri";
private final RyaTypeDocumentConverter ryaTypeConverter = new RyaTypeDocumentConverter();
@@ -62,8 +65,9 @@ public class EntityDocumentConverter implements DocumentConverter<Entity> {
entity.getProperties().get(typeId)
.forEach((propertyNameUri, property) -> {
final String propertyName = property.getName().getData();
+ final String encodedPropertyName = MongoDbSafeKey.encodeKey(propertyName);
final RyaType value = property.getValue();
- typePropertiesDoc.append(propertyName, ryaTypeConverter.toDocument(value));
+ typePropertiesDoc.append(encodedPropertyName, ryaTypeConverter.toDocument(value));
});
propertiesDoc.append(typeId.getData(), typePropertiesDoc);
}
@@ -71,6 +75,8 @@ public class EntityDocumentConverter implements DocumentConverter<Entity> {
doc.append(VERSION, entity.getVersion());
+ doc.append(SMART_URI, entity.getSmartUri().stringValue());
+
return doc;
}
@@ -99,6 +105,11 @@ public class EntityDocumentConverter implements DocumentConverter<Entity> {
"' because its '" + VERSION + "' field is missing.");
}
+ if(!document.containsKey(SMART_URI)) {
+ throw new DocumentConverterException("Could not convert document '" + document +
+ "' because its '" + SMART_URI + "' field is missing.");
+ }
+
// Perform the conversion.
final Entity.Builder builder = Entity.builder()
.setSubject( new RyaURI(document.getString(SUBJECT)) );
@@ -110,14 +121,17 @@ public class EntityDocumentConverter implements DocumentConverter<Entity> {
for(final String typeId : propertiesDoc.keySet()) {
final Document typePropertiesDoc = (Document) propertiesDoc.get(typeId);
for(final String propertyName : typePropertiesDoc.keySet()) {
+ final String decodedPropertyName = MongoDbSafeKey.decodeKey(propertyName);
final Document value = (Document) typePropertiesDoc.get(propertyName);
final RyaType propertyValue = ryaTypeConverter.fromDocument( value );
- builder.setProperty(new RyaURI(typeId), new Property(new RyaURI(propertyName), propertyValue));
+ builder.setProperty(new RyaURI(typeId), new Property(new RyaURI(decodedPropertyName), propertyValue));
}
}
builder.setVersion( document.getInteger(VERSION) );
+ builder.setSmartUri( new URIImpl(document.getString(SMART_URI)) );
+
return builder.build();
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e71ff2a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/MongoEntityStorage.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/MongoEntityStorage.java b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/MongoEntityStorage.java
index 3fc817b..1b4681d 100644
--- a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/MongoEntityStorage.java
+++ b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/MongoEntityStorage.java
@@ -34,6 +34,7 @@ import org.apache.rya.indexing.entity.model.TypedEntity;
import org.apache.rya.indexing.entity.storage.EntityStorage;
import org.apache.rya.indexing.entity.storage.mongo.ConvertingCursor.Converter;
import org.apache.rya.indexing.entity.storage.mongo.DocumentConverter.DocumentConverterException;
+import org.apache.rya.indexing.entity.storage.mongo.key.MongoDbSafeKey;
import org.bson.Document;
import org.bson.conversions.Bson;
@@ -225,16 +226,17 @@ public class MongoEntityStorage implements EntityStorage {
private static Stream<Bson> makePropertyFilters(final RyaURI typeId, final Property property) {
final String propertyName = property.getName().getData();
+ final String encodedPropertyName = MongoDbSafeKey.encodeKey(propertyName);
// Must match the property's data type.
final String dataTypePath = Joiner.on(".").join(
- new String[]{EntityDocumentConverter.PROPERTIES, typeId.getData(), propertyName, RyaTypeDocumentConverter.DATA_TYPE});
+ new String[]{EntityDocumentConverter.PROPERTIES, typeId.getData(), encodedPropertyName, RyaTypeDocumentConverter.DATA_TYPE});
final String propertyDataType = property.getValue().getDataType().stringValue();
final Bson dataTypeFilter = Filters.eq(dataTypePath, propertyDataType);
// Must match the property's value.
final String valuePath = Joiner.on(".").join(
- new String[]{EntityDocumentConverter.PROPERTIES, typeId.getData(), propertyName, RyaTypeDocumentConverter.VALUE});
+ new String[]{EntityDocumentConverter.PROPERTIES, typeId.getData(), encodedPropertyName, RyaTypeDocumentConverter.VALUE});
final String propertyValue = property.getValue().getData();
final Bson valueFilter = Filters.eq(valuePath, propertyValue);
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e71ff2a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/key/MongoDbSafeKey.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/key/MongoDbSafeKey.java b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/key/MongoDbSafeKey.java
new file mode 100644
index 0000000..3d569f9
--- /dev/null
+++ b/extras/indexing/src/main/java/org/apache/rya/indexing/entity/storage/mongo/key/MongoDbSafeKey.java
@@ -0,0 +1,81 @@
+/*
+ * 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.mongo.key;
+
+import org.javatuples.Pair;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+/**
+ * Utilities to escape keys to be safe for MongoDB use.
+ */
+public final class MongoDbSafeKey {
+ /**
+ * Encode characters in this order.
+ */
+ private static final ImmutableList<Pair<String, String>> ESCAPE_CHARACTERS = ImmutableList.of(
+ new Pair<>("\\", "\\\\"),
+ new Pair<>("\\$", "\\u0024"),
+ new Pair<>(".", "\\u002e")
+ );
+
+ /**
+ * Decode characters in this order (reverse order of encoding).
+ */
+ private static final ImmutableList<Pair<String, String>> UNESCAPE_CHARACTERS = ImmutableList.copyOf(Lists.reverse(ESCAPE_CHARACTERS));
+
+ /**
+ * Private constructor to prevent instantiation.
+ */
+ private MongoDbSafeKey() {
+ }
+
+ /**
+ * Encodes a string to be safe for use as a MongoDB field name.
+ * @param key the unencoded key.
+ * @return the encoded key.
+ */
+ public static String encodeKey(final String key) {
+ String encodedKey = key;
+ for (final Pair<String, String> pair : ESCAPE_CHARACTERS) {
+ final String unescapedCharacter = pair.getValue0();
+ final String escapedCharacter = pair.getValue1();
+ encodedKey = encodedKey.replace(unescapedCharacter, escapedCharacter);
+ }
+
+ return encodedKey;
+ }
+
+ /**
+ * Decodes a MongoDB safe field name to an unencoded string.
+ * @param key the encoded key.
+ * @return the unencoded key.
+ */
+ public static String decodeKey(final String key) {
+ String decodedKey = key;
+ for (final Pair<String, String> pair : UNESCAPE_CHARACTERS) {
+ final String unescapedCharacter = pair.getValue0();
+ final String escapedCharacter = pair.getValue1();
+ decodedKey = decodedKey.replace(escapedCharacter, unescapedCharacter);
+ }
+
+ return decodedKey;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e71ff2a/extras/indexing/src/main/java/org/apache/rya/indexing/mongodb/MongoDbSmartUri.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/mongodb/MongoDbSmartUri.java b/extras/indexing/src/main/java/org/apache/rya/indexing/mongodb/MongoDbSmartUri.java
new file mode 100644
index 0000000..9fdfad6
--- /dev/null
+++ b/extras/indexing/src/main/java/org/apache/rya/indexing/mongodb/MongoDbSmartUri.java
@@ -0,0 +1,172 @@
+/*
+ * 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.mongodb;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.net.UnknownHostException;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.rya.api.domain.RyaURI;
+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 org.apache.rya.indexing.entity.storage.EntityStorage;
+import org.apache.rya.indexing.entity.storage.EntityStorage.EntityStorageException;
+import org.apache.rya.indexing.entity.storage.mongo.ConvertingCursor;
+import org.apache.rya.indexing.entity.storage.mongo.MongoEntityStorage;
+import org.apache.rya.indexing.smarturi.SmartUriAdapter;
+import org.apache.rya.indexing.smarturi.SmartUriException;
+import org.apache.rya.indexing.smarturi.SmartUriStorage;
+import org.apache.rya.mongodb.MongoConnectorFactory;
+import org.apache.rya.mongodb.MongoDBRdfConfiguration;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+
+import com.mongodb.MongoClient;
+import com.mongodb.MongoException;
+
+/**
+ * MongoDB implementation of the Smart URI.
+ */
+public class MongoDbSmartUri implements SmartUriStorage {
+ private boolean isInit = false;
+ private final MongoDBRdfConfiguration conf;
+ private MongoClient mongoClient = null;
+ private EntityStorage entityStorage;
+
+ /**
+ * Creates a new instance of {@link MongoDbSmartUri}.
+ * @param conf the {@link MongoDBRdfConfiguration}. (not {@code null})
+ */
+ public MongoDbSmartUri(final MongoDBRdfConfiguration conf) {
+ this.conf = checkNotNull(conf);
+ }
+
+ @Override
+ public void storeEntity(final RyaURI subject, final Map<URI, Value> map) throws SmartUriException {
+ checkInit();
+
+ final URI uri = SmartUriAdapter.serializeUri(subject, map);
+ final Entity entity = SmartUriAdapter.deserializeUriEntity(uri);
+
+ // Create it.
+ try {
+ entityStorage.create(entity);
+ } catch (final EntityStorageException e) {
+ throw new SmartUriException("Failed to create entity storage", e);
+ }
+ }
+
+ @Override
+ public void storeEntity(final Entity entity) throws SmartUriException {
+ checkInit();
+
+ // Create it.
+ try {
+ entityStorage.create(entity);
+ } catch (final EntityStorageException e) {
+ throw new SmartUriException("Failed to create entity storage", e);
+ }
+ }
+
+ @Override
+ public void updateEntity(final Entity oldEntity, final Entity updatedEntity) throws SmartUriException {
+ checkInit();
+
+ // Update it.
+ try {
+ entityStorage.update(oldEntity, updatedEntity);
+ } catch (final EntityStorageException e) {
+ throw new SmartUriException("Failed to update entity", e);
+ }
+ }
+
+ @Override
+ public Entity queryEntity(final RyaURI subject) throws SmartUriException {
+ checkInit();
+
+ // Query it.
+ try {
+ final Optional<Entity> resultEntity = entityStorage.get(subject);
+ return resultEntity.get();
+ } catch (final EntityStorageException e) {
+ throw new SmartUriException("Failed to query entity storage", e);
+ }
+ }
+
+ @Override
+ public ConvertingCursor<TypedEntity> queryEntity(final Type type, final Map<URI, Value> map) throws SmartUriException {
+ checkInit();
+
+ // Query it.
+ try {
+ final Set<Property> properties = SmartUriAdapter.mapToProperties(map);
+ final ConvertingCursor<TypedEntity> cursor = entityStorage.search(Optional.empty(), type, properties);
+ return cursor;
+ } catch (final EntityStorageException e) {
+ throw new SmartUriException("Failed to query entity storage", e);
+ }
+ }
+
+ private void checkInit() throws SmartUriException {
+ if (!isInit) {
+ try {
+ setupClient(conf);
+ } catch (final UnknownHostException | MongoException e) {
+ throw new SmartUriException("Failed to setup MongoDB client", e);
+ }
+ }
+ }
+
+ /**
+ * Setup the MongoDB client.
+ * @param conf the {@link Configuration}.
+ * @throws UnknownHostException
+ * @throws MongoException
+ */
+ private void setupClient(final Configuration conf) throws UnknownHostException, MongoException {
+ final MongoDBRdfConfiguration mongoConf = (MongoDBRdfConfiguration) conf;
+ mongoClient = mongoConf.getMongoClient();
+ if (mongoClient == null) {
+ mongoClient = MongoConnectorFactory.getMongoClient(conf);
+ }
+ entityStorage = new MongoEntityStorage(mongoClient, mongoConf.getMongoInstance());
+ isInit = true;
+ }
+
+ /**
+ * Shutdown the MongoDB client.
+ */
+ public void shutdown() {
+ IOUtils.closeQuietly(mongoClient);
+ }
+
+ /**
+ * @return the {@link EntityStorage}.
+ */
+ public EntityStorage getEntityStorage() {
+ return entityStorage;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e71ff2a/extras/indexing/src/main/java/org/apache/rya/indexing/smarturi/SmartUriAdapter.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/smarturi/SmartUriAdapter.java b/extras/indexing/src/main/java/org/apache/rya/indexing/smarturi/SmartUriAdapter.java
new file mode 100644
index 0000000..d6a5e8a
--- /dev/null
+++ b/extras/indexing/src/main/java/org/apache/rya/indexing/smarturi/SmartUriAdapter.java
@@ -0,0 +1,688 @@
+/*
+ * 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.smarturi;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.net.URLDecoder;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.rya.api.domain.RyaType;
+import org.apache.rya.api.domain.RyaURI;
+import org.apache.rya.api.resolver.RdfToRyaConversions;
+import org.apache.rya.api.resolver.RyaToRdfConversions;
+import org.apache.rya.indexing.entity.model.Entity;
+import org.apache.rya.indexing.entity.model.Property;
+import org.joda.time.DateTime;
+import org.joda.time.format.ISODateTimeFormat;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+import org.openrdf.model.impl.URIImpl;
+import org.openrdf.model.vocabulary.XMLSchema;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.primitives.Doubles;
+import com.google.common.primitives.Floats;
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Longs;
+
+/**
+ * Interface for serializing and deserializing Smart URIs.
+ */
+public class SmartUriAdapter {
+ private static final String ENTITY_TYPE_MAP_URN = "urn://entityTypeMap";
+ private static final URI RYA_TYPES_URI = new URIImpl("urn://ryaTypes");
+
+ /**
+ * Private constructor to prevent instantiation.
+ */
+ private SmartUriAdapter() {
+ }
+
+ private static URI createTypePropertiesUri(final ImmutableMap<RyaURI, ImmutableMap<RyaURI, Property>> typeProperties) throws SmartUriException {
+ final List<NameValuePair> nameValuePairs = new ArrayList<>();
+ for (final Entry<RyaURI, ImmutableMap<RyaURI, Property>> typeProperty : typeProperties.entrySet()) {
+ final RyaURI type = typeProperty.getKey();
+ final Map<RyaURI, Property> propertyMap = typeProperty.getValue();
+ final URI typeUri = createIndividualTypeWithPropertiesUri(type, propertyMap);
+ final String keyString = type.getDataType().getLocalName();
+ final String valueString = typeUri.getLocalName();
+ nameValuePairs.add(new BasicNameValuePair(keyString, valueString));
+ }
+
+ final URIBuilder uriBuilder = new URIBuilder();
+ uriBuilder.addParameters(nameValuePairs);
+
+ String uriString;
+ try {
+ final java.net.URI uri = uriBuilder.build();
+ final String queryString = uri.getRawSchemeSpecificPart();
+ uriString = "urn:test" + queryString;
+ } catch (final URISyntaxException e) {
+ throw new SmartUriException("Unable to create type properties for the Smart URI", e);
+ }
+
+ return new URIImpl(uriString);
+ }
+
+ private static String getShortNameForType(final RyaURI type) throws SmartUriException {
+ String typeUriString;
+ try {
+ typeUriString = new java.net.URI(type.getData()).getRawSchemeSpecificPart();
+ } catch (final URISyntaxException e) {
+ throw new SmartUriException("Unable to get create URI for type", e);
+ }
+ final Path path = Paths.get(typeUriString);
+ final String shortName = path.getFileName().toString();
+ return shortName;
+ }
+
+
+ private static String addTypePrefixToUri(final String uriString, final String typePrefix) {
+ int location = StringUtils.lastIndexOf(uriString, "#");
+ if (location == - 1) {
+ location = StringUtils.lastIndexOf(uriString, "/");
+ }
+
+ final String lastSegment = uriString.substring(location + 1);
+
+ final String formattedUriString = uriString.substring(0, location + 1) + typePrefix + lastSegment;
+ return formattedUriString;
+ }
+
+ private static String removeTypePrefixFromUri(final String uriString, final String typePrefix) {
+ int location = StringUtils.lastIndexOf(uriString, "#");
+ if (location == - 1) {
+ location = StringUtils.lastIndexOf(uriString, "/");
+ }
+
+ final String lastSegment = uriString.substring(location + 1);
+ final String replacement = lastSegment.replaceFirst(typePrefix + ".", "");
+
+ final String formattedUriString = uriString.substring(0, location + 1) + replacement;
+ return formattedUriString;
+ }
+
+ private static Map<RyaURI, String> createTypeMap(final List<RyaURI> types) throws SmartUriException {
+ final Map<RyaURI, String> map = new LinkedHashMap<>();
+ for (final RyaURI type : types) {
+ final String shortName = getShortNameForType(type);
+ map.put(type, shortName);
+ }
+ return map;
+ }
+
+ private static URI createTypeMapUri(final List<RyaURI> types) throws SmartUriException {
+ final List<NameValuePair> nameValuePairs = new ArrayList<>();
+ for (final RyaURI type : types) {
+ final String shortName = getShortNameForType(type);
+ nameValuePairs.add(new BasicNameValuePair(type.getData(), shortName));
+ }
+
+ final URIBuilder uriBuilder = new URIBuilder();
+ uriBuilder.addParameters(nameValuePairs);
+
+ String uriString;
+ try {
+ final java.net.URI uri = uriBuilder.build();
+ final String queryString = uri.getRawSchemeSpecificPart();
+ uriString = ENTITY_TYPE_MAP_URN + queryString;
+ } catch (final URISyntaxException e) {
+ throw new SmartUriException("Unable to create type properties for the Smart URI", e);
+ }
+
+ return new URIImpl(uriString);
+ }
+
+ private static Map<RyaURI, String> convertUriToTypeMap(final URI typeMapUri) throws SmartUriException {
+ final Map<RyaURI, String> map = new HashMap<>();
+ java.net.URI uri;
+ try {
+ final URIBuilder uriBuilder = new URIBuilder(typeMapUri.stringValue());
+ uri = uriBuilder.build();
+ } catch (final URISyntaxException e) {
+ throw new SmartUriException("Unable to parse Rya type map in Smart URI", e);
+ }
+
+ final List<NameValuePair> params = URLEncodedUtils.parse(uri, Charsets.UTF_8.name());
+
+ for (final NameValuePair param : params) {
+ final String name = param.getName();
+ final String value = param.getValue();
+ final RyaURI type = new RyaURI(name);
+ map.put(type, value);
+ }
+ return map;
+ }
+
+ private static URI createIndividualTypeWithPropertiesUri(final RyaURI type, final Map<RyaURI, Property> map) throws SmartUriException {
+ final List<NameValuePair> nameValuePairs = new ArrayList<>();
+ for (final Entry<RyaURI, Property> entry : map.entrySet()) {
+ final RyaURI key = entry.getKey();
+ final Property property = entry.getValue();
+
+ final RyaType ryaType = property.getValue();
+ final String keyString = (new URIImpl(key.getData())).getLocalName();
+ final Value value = RyaToRdfConversions.convertValue(ryaType);
+ final String valueString = value.stringValue();
+ nameValuePairs.add(new BasicNameValuePair(keyString, valueString));
+ }
+
+ final URIBuilder uriBuilder = new URIBuilder();
+ uriBuilder.addParameters(nameValuePairs);
+
+ String uriString;
+ try {
+ final java.net.URI uri = uriBuilder.build();
+ final String queryString = uri.getRawSchemeSpecificPart();
+ uriString = type.getData()/*new URIImpl(type.getData()).getLocalName()*/ + queryString;
+ } catch (final URISyntaxException e) {
+ throw new SmartUriException("Unable to create type URI with all its properties for the Smart URI", e);
+ }
+
+ return new URIImpl(uriString);
+ }
+
+ private static Entity convertMapToEntity(final RyaURI subject, final Map<RyaURI, Map<URI, Value>> map) {
+ final Entity.Builder entityBuilder = Entity.builder();
+ entityBuilder.setSubject(subject);
+
+ for (final Entry<RyaURI, Map<URI, Value>> typeEntry : map.entrySet()) {
+ final RyaURI type = typeEntry.getKey();
+ final Map<URI, Value> subMap = typeEntry.getValue();
+ entityBuilder.setExplicitType(type);
+ for (final Entry<URI, Value> entry : subMap.entrySet()) {
+ final URI uri = entry.getKey();
+ final Value value = entry.getValue();
+ final RyaURI ryaUri = new RyaURI(uri.stringValue());
+ final RyaURI ryaName = new RyaURI(uri.stringValue());
+ final RyaType ryaType = new RyaType(value.stringValue());
+ final Property property = new Property(ryaName, ryaType);
+ entityBuilder.setProperty(ryaUri, property);
+ }
+ }
+ final Entity entity = entityBuilder.build();
+ return entity;
+ }
+
+ public static RyaURI findSubject(final URI uri) throws SmartUriException {
+ final String uriString = uri.stringValue();
+ return findSubject(uriString);
+ }
+
+ public static RyaURI findSubject(final String uriString) throws SmartUriException {
+ java.net.URI uri;
+ try {
+ uri = new java.net.URI(uriString);
+ } catch (final URISyntaxException e) {
+ throw new SmartUriException("Could not find subject in Smart URI", e);
+ }
+ final RyaURI subject;
+ final String fullFragment = uri.getFragment();
+ if (fullFragment != null) {
+ final int queryPosition = fullFragment.indexOf("?");
+ String partialFragment = null;
+ if (queryPosition != - 1) {
+ partialFragment = fullFragment.substring(0, queryPosition);
+ }
+ final String subjectString = uri.getScheme() + ":" + uri.getSchemeSpecificPart() + "#" + partialFragment;
+ subject = new RyaURI(subjectString);
+ } else {
+ final int queryPosition = uriString.indexOf("?");
+ String subjectString = null;
+ if (queryPosition != - 1) {
+ subjectString = uriString.substring(0, queryPosition);
+ } else {
+ subjectString = uriString;
+ }
+ subject = new RyaURI(subjectString);
+ }
+
+ return subject;
+ }
+
+
+ /**
+ * Serializes an {@link Entity} into a Smart {@link URI}.
+ * @param entity the {@link Entity} to serialize into a Smart URI.
+ * @return the Smart {@link URI}.
+ * @throws SmartUriException
+ */
+ public static URI serializeUriEntity(final Entity entity) throws SmartUriException {
+ final Map<URI, Value> objectMap = new LinkedHashMap<>();
+
+ // Adds the entity's types to the Smart URI
+ final List<RyaURI> typeIds = entity.getExplicitTypeIds();
+ final Map<RyaURI, String> ryaTypeMap = createTypeMap(typeIds);
+ final URI ryaTypeMapUri = createTypeMapUri(typeIds);
+ final RyaType valueRyaType = new RyaType(XMLSchema.ANYURI, ryaTypeMapUri.stringValue());
+ final Value typeValue = RyaToRdfConversions.convertValue(valueRyaType);
+ objectMap.put(RYA_TYPES_URI, typeValue);
+
+ final RyaURI subject = entity.getSubject();
+ final Map<RyaURI, ImmutableMap<RyaURI, Property>> typeMap = entity.getProperties();
+ for (final Entry<RyaURI, ImmutableMap<RyaURI, Property>> typeEntry : typeMap.entrySet()) {
+ final RyaURI type = typeEntry.getKey();
+ String typeShortName = ryaTypeMap.get(type);
+ typeShortName = typeShortName != null ? typeShortName + "." : "";
+ final ImmutableMap<RyaURI, Property> typeProperties = typeEntry.getValue();
+ for (final Entry<RyaURI, Property> properties : typeProperties.entrySet()) {
+ final RyaURI key = properties.getKey();
+ final Property property = properties.getValue();
+ final String valueString = property.getValue().getData();
+ final RyaType ryaType = property.getValue();
+
+ //final RyaType ryaType = new RyaType(new URIImpl(key.getData()), valueString);
+
+ final Value value = RyaToRdfConversions.convertValue(ryaType);
+
+ String formattedKey = key.getData();
+ if (StringUtils.isNotBlank(typeShortName)) {
+ formattedKey = addTypePrefixToUri(key.getData(), typeShortName);
+ }
+ final URI uri = new URIImpl(formattedKey);
+ objectMap.put(uri, value);
+ }
+ }
+
+ return serializeUri(subject, objectMap);
+ }
+
+ /**
+ * Serializes a map into a URI.
+ * @param subject the {@link RyaURI} subject of the Entity. Identifies the
+ * thing that is being represented as an Entity.
+ * @param map the {@link Map} of {@link URI}s to {@link Value}s.
+ * @return the Smart {@link URI}.
+ * @throws SmartUriException
+ */
+ public static URI serializeUri(final RyaURI subject, final Map<URI, Value> map) throws SmartUriException {
+ final String subjectData = subject.getData();
+ final int fragmentPosition = subjectData.indexOf("#");
+ String prefix = subjectData;
+ String fragment = null;
+ if (fragmentPosition > -1) {
+ prefix = subjectData.substring(0, fragmentPosition);
+ fragment = subjectData.substring(fragmentPosition + 1, subjectData.length());
+ }
+
+ URIBuilder uriBuilder = null;
+ try {
+ if (fragmentPosition > -1) {
+ uriBuilder = new URIBuilder(new java.net.URI("urn://" + fragment));
+ } else {
+ uriBuilder = new URIBuilder(new java.net.URI(subjectData));
+ }
+ } catch (final URISyntaxException e) {
+ throw new SmartUriException("Unable to serialize a Smart URI from the provided properties", e);
+ }
+ final List<NameValuePair> nameValuePairs = new ArrayList<>();
+
+ for (final Entry<URI, Value> entry : map.entrySet()) {
+ final URI key = entry.getKey();
+ final Value value = entry.getValue();
+ nameValuePairs.add(new BasicNameValuePair(key.getLocalName(), value.stringValue()));
+ }
+
+ uriBuilder.setParameters(nameValuePairs);
+
+ URI uri = null;
+ try {
+ if (fragmentPosition > -1) {
+ final java.net.URI partialUri = uriBuilder.build();
+ final String uriString = StringUtils.removeStart(partialUri.getRawSchemeSpecificPart(), "//");
+ final URIBuilder fragmentUriBuilder = new URIBuilder(new java.net.URI(prefix));
+ fragmentUriBuilder.setFragment(uriString);
+ final String fragmentUriString = fragmentUriBuilder.build().toString();
+ uri = new URIImpl(fragmentUriString);
+ } else {
+ final String uriString = uriBuilder.build().toString();
+ uri = new URIImpl(uriString);
+ }
+ } catch (final URISyntaxException e) {
+ throw new SmartUriException("Smart URI could not serialize the property map.", e);
+ }
+
+ return uri;
+ }
+
+ /**
+ * Deserializes a URI into a map of URI's to values.
+ * @param uri the {@link URI}.
+ * @return the {@link Map} of {@link URI}s to {@link Value}s.
+ * @throws SmartUriException
+ */
+ public static Map<URI, Value> deserializeUri(final URI uri) throws SmartUriException {
+ final String uriString = uri.stringValue();
+ final int fragmentPosition = uriString.indexOf("#");
+ String prefix = uriString.substring(0, fragmentPosition + 1);
+ if (fragmentPosition == -1) {
+ prefix = uriString.split("\\?", 2)[0];
+ }
+ final String fragment = uriString.substring(fragmentPosition + 1, uriString.length());
+ java.net.URI queryUri;
+
+ URIBuilder uriBuilder = null;
+ try {
+ if (fragmentPosition > -1) {
+ queryUri = new java.net.URI("urn://" + fragment);
+ } else {
+ queryUri = new java.net.URI(uriString);
+ }
+ uriBuilder = new URIBuilder(queryUri);
+ } catch (final URISyntaxException e) {
+ throw new SmartUriException("Unable to deserialize Smart URI", e);
+ }
+ final Map<URI, Value> map = new HashMap<>();
+ final RyaURI subject = findSubject(uri.stringValue());
+
+ final List<NameValuePair> parameters = uriBuilder.getQueryParams();
+ Map<RyaURI, String> entityTypeMap = new LinkedHashMap<>();
+ Map<String, RyaURI> invertedEntityTypeMap = new LinkedHashMap<>();
+ final Map<RyaURI, Map<URI, Value>> fullMap = new LinkedHashMap<>();
+ for (final NameValuePair pair : parameters) {
+ final String keyString = pair.getName();
+ final String valueString = pair.getValue();
+
+ final URI keyUri = new URIImpl(prefix + keyString);
+ final String decoded;
+ try {
+ decoded = URLDecoder.decode(valueString, Charsets.UTF_8.name());
+ } catch (final UnsupportedEncodingException e) {
+ throw new SmartUriException("", e);
+ }
+ final URI type = TypeDeterminer.determineType(decoded);
+ if (type == XMLSchema.ANYURI) {
+ if (keyString.equals(RYA_TYPES_URI.getLocalName())) {
+ entityTypeMap = convertUriToTypeMap(new URIImpl(decoded));
+ invertedEntityTypeMap = HashBiMap.create(entityTypeMap).inverse();
+ }
+ } else {
+ final int keyPrefixLocation = keyString.indexOf(".");
+ final String keyPrefix = keyString.substring(0, keyPrefixLocation);
+ final RyaURI keyCorrespondingType = invertedEntityTypeMap.get(keyPrefix);
+ final String keyName = keyString.substring(keyPrefixLocation + 1, keyString.length());
+ final RyaType ryaType = new RyaType(type, valueString);
+
+ final Value value = RyaToRdfConversions.convertValue(ryaType);
+
+ final String formattedKeyUriString = removeTypePrefixFromUri(keyUri.stringValue(), keyPrefix);
+ final URI formattedKeyUri = new URIImpl(formattedKeyUriString);
+
+ map.put(formattedKeyUri, value);
+ }
+ }
+ return map;
+ }
+
+// public static Map<URI, Value> deserializeUri(final URI uri) throws SmartUriException {
+// final String uriString = uri.stringValue();
+// final int fragmentPosition = uriString.indexOf("#");
+// String prefix = uriString.substring(0, fragmentPosition + 1);
+// if (fragmentPosition == -1) {
+// prefix = uriString.split("\\?", 2)[0];
+// }
+// final String fragment = uriString.substring(fragmentPosition + 1, uriString.length());
+// java.net.URI queryUri;
+//
+// URIBuilder uriBuilder = null;
+// try {
+// if (fragmentPosition > -1) {
+// queryUri = new java.net.URI("urn://" + fragment);
+// } else {
+// queryUri = new java.net.URI(uriString);
+// }
+// uriBuilder = new URIBuilder(queryUri);
+// } catch (final URISyntaxException e) {
+// throw new SmartUriException("Unable to deserialize Smart URI", e);
+// }
+// final Map<URI, Value> map = new HashMap<>();
+// final List<NameValuePair> parameters = uriBuilder.getQueryParams();
+// Map<RyaURI, String> entityTypeMap = new LinkedHashMap<>();
+// for (final NameValuePair pair : parameters) {
+// final String keyString = pair.getName();
+// final String valueString = pair.getValue();
+//
+// final URI keyUri = new URIImpl(prefix + keyString);
+// final URI type = TypeDeterminer.determineType(valueString);
+// if (type == XMLSchema.ANYURI) {
+// final String decoded;
+// try {
+// decoded = URLDecoder.decode(valueString, Charsets.UTF_8.name());
+// } catch (final UnsupportedEncodingException e) {
+// throw new SmartUriException("", e);
+// }
+// entityTypeMap = convertUriToTypeMap(new URIImpl(decoded));
+// } else {
+// final RyaType ryaType = new RyaType(type, valueString);
+//
+// final Value value = RyaToRdfConversions.convertValue(ryaType);
+//
+// map.put(keyUri, value);
+// }
+// }
+// return map;
+// }
+
+ public static Entity deserializeUriEntity(final URI uri) throws SmartUriException {
+ final String uriString = uri.stringValue();
+ final int fragmentPosition = uriString.indexOf("#");
+ String prefix = uriString.substring(0, fragmentPosition + 1);
+ if (fragmentPosition == -1) {
+ prefix = uriString.split("\\?", 2)[0];
+ }
+ final String fragment = uriString.substring(fragmentPosition + 1, uriString.length());
+ java.net.URI queryUri;
+
+ URIBuilder uriBuilder = null;
+ try {
+ if (fragmentPosition > -1) {
+ queryUri = new java.net.URI("urn://" + fragment);
+ } else {
+ queryUri = new java.net.URI(uriString);
+ }
+ uriBuilder = new URIBuilder(queryUri);
+ } catch (final URISyntaxException e) {
+ throw new SmartUriException("Unable to deserialize Smart URI", e);
+ }
+
+ final RyaURI subject = findSubject(uri.stringValue());
+
+ final List<NameValuePair> parameters = uriBuilder.getQueryParams();
+ Map<RyaURI, String> entityTypeMap = new LinkedHashMap<>();
+ Map<String, RyaURI> invertedEntityTypeMap = new LinkedHashMap<>();
+ final Map<RyaURI, Map<URI, Value>> fullMap = new LinkedHashMap<>();
+ for (final NameValuePair pair : parameters) {
+ final String keyString = pair.getName();
+ final String valueString = pair.getValue();
+
+ final URI keyUri = new URIImpl(prefix + keyString);
+ final String decoded;
+ try {
+ decoded = URLDecoder.decode(valueString, Charsets.UTF_8.name());
+ } catch (final UnsupportedEncodingException e) {
+ throw new SmartUriException("", e);
+ }
+ final URI type = TypeDeterminer.determineType(decoded);
+ if (type == XMLSchema.ANYURI) {
+ if (keyString.equals(RYA_TYPES_URI.getLocalName())) {
+ entityTypeMap = convertUriToTypeMap(new URIImpl(decoded));
+ invertedEntityTypeMap = HashBiMap.create(entityTypeMap).inverse();
+ }
+ } else {
+ final int keyPrefixLocation = keyString.indexOf(".");
+ final String keyPrefix = keyString.substring(0, keyPrefixLocation);
+ final RyaURI keyCorrespondingType = invertedEntityTypeMap.get(keyPrefix);
+ final String keyName = keyString.substring(keyPrefixLocation + 1, keyString.length());
+ final RyaType ryaType = new RyaType(type, valueString);
+
+ final Value value = RyaToRdfConversions.convertValue(ryaType);
+
+ final String formattedKeyUriString = removeTypePrefixFromUri(keyUri.stringValue(), keyPrefix);
+ final URI formattedKeyUri = new URIImpl(formattedKeyUriString);
+ final Map<URI, Value> map = fullMap.get(keyCorrespondingType);
+
+ if (map == null) {
+ final Map<URI, Value> subMap = new HashMap<>();
+ subMap.put(formattedKeyUri, value);
+ fullMap.put(keyCorrespondingType, subMap);
+ } else {
+ map.put(formattedKeyUri, value);
+ fullMap.put(keyCorrespondingType, map);
+ }
+ }
+ }
+ final Entity entity = convertMapToEntity(subject, fullMap);
+ return entity;
+ }
+
+ private static final class TypeDeterminer {
+ /**
+ * Private constructor to prevent instantiation.
+ */
+ private TypeDeterminer() {
+ }
+
+ private static URI determineType(final String data) {
+ if (Ints.tryParse(data) != null) {
+ return XMLSchema.INTEGER;
+ } else if (Doubles.tryParse(data) != null) {
+ return XMLSchema.DOUBLE;
+ } else if (Floats.tryParse(data) != null) {
+ return XMLSchema.FLOAT;
+ } else if (isShort(data)) {
+ return XMLSchema.SHORT;
+ } else if (Longs.tryParse(data) != null) {
+ return XMLSchema.LONG;
+ } if (Boolean.parseBoolean(data)) {
+ return XMLSchema.BOOLEAN;
+ } else if (isByte(data)) {
+ return XMLSchema.BYTE;
+ } else if (isDate(data)) {
+ return XMLSchema.DATETIME;
+ } else if (isUri(data)) {
+ return XMLSchema.ANYURI;
+ }
+
+ return XMLSchema.STRING;
+ }
+
+ private static boolean isDate(final String data) {
+ try {
+ DateTime.parse(data, ISODateTimeFormat.dateTimeParser());
+ return true;
+ } catch (final IllegalArgumentException e) {
+ // not a date
+ return false;
+ }
+ }
+
+ private static boolean isShort(final String data) {
+ try {
+ Short.parseShort(data);
+ return true;
+ } catch (final NumberFormatException e) {
+ // not a short
+ return false;
+ }
+ }
+
+ private static boolean isByte(final String data) {
+ try {
+ Byte.parseByte(data);
+ return true;
+ } catch (final NumberFormatException e) {
+ // not a byte
+ return false;
+ }
+ }
+
+ private static boolean isUri(final String data) {
+ try {
+ final String decoded = URLDecoder.decode(data, Charsets.UTF_8.name());
+ new URIImpl(decoded);
+ return true;
+ } catch (final IllegalArgumentException | UnsupportedEncodingException e) {
+ // not a URI
+ return false;
+ }
+ }
+ }
+
+ public static Map<URI, Value> entityToValueMap(final Entity entity) {
+ final Map<URI, Value> map = new LinkedHashMap<>();
+ for (final Entry<RyaURI, ImmutableMap<RyaURI, Property>> entry : entity.getProperties().entrySet()) {
+ for (final Entry<RyaURI, Property> property : entry.getValue().entrySet()) {
+ final RyaURI propertyKey = property.getKey();
+ final URI uri = new URIImpl(propertyKey.getData());
+ final Property propertyValue = property.getValue();
+ final Value value = RyaToRdfConversions.convertValue(propertyValue.getValue());
+ map.put(uri, value);
+ }
+ }
+ return map;
+ }
+
+ /**
+ * Converts a {@link Map} of {@link URI}/{@link Value}s to a {@link Set} of
+ * {@link Property}s.
+ * @param map the {@link Map} of {@link URI}/{@link Value}.
+ * @return the {@link Set} of {@link Property}s.
+ */
+ public static Set<Property> mapToProperties(final Map<URI, Value> map) {
+ final Set<Property> properties = new LinkedHashSet<>();
+ for (final Entry<URI, Value> entry : map.entrySet()) {
+ final URI uri = entry.getKey();
+ final Value value = entry.getValue();
+
+ final RyaURI ryaUri = new RyaURI(uri.stringValue());
+ final RyaType ryaType = RdfToRyaConversions.convertValue(value);
+
+ final Property property = new Property(ryaUri, ryaType);
+ properties.add(property);
+ }
+ return properties;
+ }
+
+ public static Map<URI, Value> propertiesToMap(final Set<Property> properties) {
+ final Map<URI, Value> map = new LinkedHashMap<>();
+ for (final Property property : properties) {
+ final URI uri = new URIImpl(property.getName().getData());
+ final Value value = RyaToRdfConversions.convertValue(property.getValue());
+ map.put(uri, value);
+ }
+ return map;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e71ff2a/extras/indexing/src/main/java/org/apache/rya/indexing/smarturi/SmartUriException.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/smarturi/SmartUriException.java b/extras/indexing/src/main/java/org/apache/rya/indexing/smarturi/SmartUriException.java
new file mode 100644
index 0000000..979b6b9
--- /dev/null
+++ b/extras/indexing/src/main/java/org/apache/rya/indexing/smarturi/SmartUriException.java
@@ -0,0 +1,36 @@
+/*
+ * 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.smarturi;
+
+/**
+ * Exception thrown when a SmartURI is malformed when attempting to
+ * serialize/deserialize it.
+ */
+public class SmartUriException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Creates a new instance of {@link SmartUriException}.
+ * @param message the message to be displayed by the exception.
+ * @param throwable the source {#link Throwable} cause of the exception.
+ */
+ public SmartUriException(final String message, final Throwable throwable) {
+ super(message, throwable);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e71ff2a/extras/indexing/src/main/java/org/apache/rya/indexing/smarturi/SmartUriStorage.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/org/apache/rya/indexing/smarturi/SmartUriStorage.java b/extras/indexing/src/main/java/org/apache/rya/indexing/smarturi/SmartUriStorage.java
new file mode 100644
index 0000000..1043d42
--- /dev/null
+++ b/extras/indexing/src/main/java/org/apache/rya/indexing/smarturi/SmartUriStorage.java
@@ -0,0 +1,78 @@
+/*
+ * 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.smarturi;
+
+import java.util.Map;
+
+import org.apache.rya.api.domain.RyaURI;
+import org.apache.rya.indexing.entity.model.Entity;
+import org.apache.rya.indexing.entity.model.Type;
+import org.apache.rya.indexing.entity.model.TypedEntity;
+import org.apache.rya.indexing.entity.storage.mongo.ConvertingCursor;
+import org.calrissian.mango.collect.CloseableIterator;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+
+/**
+ * Interface for interacting with a Smart URI's datastore.
+ */
+public interface SmartUriStorage {
+ /**
+ * Stores the map into the datastore.
+ * @param subject the {@link RyaURI} subject of the Entity. Identifies the
+ * thing that is being represented as an Entity.
+ * @param map the {@link Map} of {@link URI}s to {@link Value}s.
+ * @throws SmartUriException
+ */
+ public void storeEntity(final RyaURI subject, final Map<URI, Value> map) throws SmartUriException;
+
+ /**
+ * Stores the entity into the datastore.
+ * @param entity the {@link Entity}.
+ * @throws SmartUriException
+ */
+ public void storeEntity(final Entity entity) throws SmartUriException;
+
+ /**
+ * Updates the entity.
+ * @param oldEntity the old {@link Entity} to update
+ * @param updatedEntity the new {@link Entity} to replace the old one with.
+ * @throws SmartUriException
+ */
+ public void updateEntity(final Entity oldEntity, final Entity updatedEntity) throws SmartUriException;
+
+ /**
+ * Queries for the entity based on the subject
+ * @param subject the {@link RyaURI} subject of the Entity. Identifies the
+ * thing that is being represented as an Entity.
+ * @return the {@link Entity} matching the subject.
+ * @throws SmartUriException
+ */
+ public Entity queryEntity(final RyaURI subject) throws SmartUriException;
+
+ /**
+ * Queries the datastore for the map.
+ * @param type the type associated with the entity values.
+ * @param map the {@link Map} of {@link URI}s to {@link Value}s.
+ * @return a {@link CloseableIterator} over the {@link TypedEntity}s that
+ * match the search parameters.
+ * @throws SmartUriException
+ */
+ public ConvertingCursor<TypedEntity> queryEntity(final Type type, final Map<URI, Value> map) throws SmartUriException;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e71ff2a/extras/indexing/src/test/java/org/apache/rya/indexing/mongo/MongoDbSmartUriTest.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/test/java/org/apache/rya/indexing/mongo/MongoDbSmartUriTest.java b/extras/indexing/src/test/java/org/apache/rya/indexing/mongo/MongoDbSmartUriTest.java
new file mode 100644
index 0000000..60efbed
--- /dev/null
+++ b/extras/indexing/src/test/java/org/apache/rya/indexing/mongo/MongoDbSmartUriTest.java
@@ -0,0 +1,333 @@
+/*
+ * 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.mongo;
+
+import static org.apache.rya.api.domain.RyaTypeUtils.booleanRyaType;
+import static org.apache.rya.api.domain.RyaTypeUtils.byteRyaType;
+import static org.apache.rya.api.domain.RyaTypeUtils.dateRyaType;
+import static org.apache.rya.api.domain.RyaTypeUtils.doubleRyaType;
+import static org.apache.rya.api.domain.RyaTypeUtils.floatRyaType;
+import static org.apache.rya.api.domain.RyaTypeUtils.intRyaType;
+import static org.apache.rya.api.domain.RyaTypeUtils.longRyaType;
+import static org.apache.rya.api.domain.RyaTypeUtils.shortRyaType;
+import static org.apache.rya.api.domain.RyaTypeUtils.stringRyaType;
+import static org.apache.rya.api.domain.RyaTypeUtils.uriRyaType;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URISyntaxException;
+import java.util.Date;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.rya.api.domain.RyaSchema;
+import org.apache.rya.api.domain.RyaURI;
+import org.apache.rya.api.resolver.RdfToRyaConversions;
+import org.apache.rya.api.resolver.RyaToRdfConversions;
+import org.apache.rya.indexing.accumulo.ConfigUtils;
+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 org.apache.rya.indexing.entity.query.EntityQueryNode;
+import org.apache.rya.indexing.entity.storage.mongo.ConvertingCursor;
+import org.apache.rya.indexing.mongodb.MongoDbSmartUri;
+import org.apache.rya.indexing.smarturi.SmartUriAdapter;
+import org.apache.rya.indexing.smarturi.SmartUriException;
+import org.apache.rya.mongodb.MockMongoFactory;
+import org.apache.rya.mongodb.MongoDBRdfConfiguration;
+import org.joda.time.DateTime;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+import org.openrdf.model.ValueFactory;
+import org.openrdf.model.impl.URIImpl;
+import org.openrdf.model.impl.ValueFactoryImpl;
+import org.openrdf.model.vocabulary.RDF;
+import org.openrdf.query.BindingSet;
+import org.openrdf.query.MalformedQueryException;
+import org.openrdf.query.QueryEvaluationException;
+import org.openrdf.query.algebra.StatementPattern;
+import org.openrdf.query.algebra.evaluation.QueryBindingSet;
+import org.openrdf.query.algebra.helpers.StatementPatternCollector;
+import org.openrdf.query.parser.sparql.SPARQLParser;
+
+import com.beust.jcommander.internal.Lists;
+import com.google.common.collect.ImmutableSet;
+import com.mongodb.MongoClient;
+
+import info.aduna.iteration.CloseableIteration;
+
+/**
+ * Tests for MongoDB based Smart URI.
+ */
+public class MongoDbSmartUriTest {
+ private static final String NAMESPACE = RyaSchema.NAMESPACE;
+ private static final ValueFactory VALUE_FACTORY = ValueFactoryImpl.getInstance();
+
+ // People
+ private static final RyaURI BOB = createRyaUri("Bob");
+
+ // Attributes
+ private static final RyaURI HAS_WEIGHT = createRyaUri("hasWeight");
+ private static final RyaURI HAS_HEIGHT = createRyaUri("hasHeight");
+ private static final RyaURI HAS_SSN = createRyaUri("hasSSN");
+ private static final RyaURI HAS_AGE = createRyaUri("hasAge");
+ private static final RyaURI HAS_INCOME = createRyaUri("hasIncome");
+ private static final RyaURI HAS_NUMBER_OF_CHILDREN = createRyaUri("hasNumberOfChildren");
+ private static final RyaURI HAS_LICENSE_NUMBER = createRyaUri("hasLicenseNumber");
+ private static final RyaURI HAS_EYE_COLOR = createRyaUri("hasEyeColor");
+ private static final RyaURI HAS_HAIR_COLOR = createRyaUri("hasHairColor");
+ private static final RyaURI HAS_DATE_OF_BIRTH = createRyaUri("hasDateOfBirth");
+ private static final RyaURI HAS_EXPIRATION_DATE = createRyaUri("hasExpirationDate");
+ private static final RyaURI HAS_GLASSES = createRyaUri("hasGlasses");
+ private static final RyaURI HAS_EMAIL_ADDRESS = createRyaUri("hasEmailAddress");
+ private static final RyaURI HAS_ATTRIBUTE_SPACE = createRyaUri("has Attribute Space");
+ private static final RyaURI HAS_MOTTO = createRyaUri("hasMotto");
+ private static final RyaURI HAS_BLOOD_TYPE = createRyaUri("hasBloodType");
+ private static final RyaURI HAS_SEX = createRyaUri("hasSex");
+ private static final RyaURI HAS_ADDRESS = createRyaUri("hasAddress");
+ private static final RyaURI HAS_POSITION_TITLE = createRyaUri("hasPositionTitle");
+ private static final RyaURI HAS_WORK_ADDRESS = createRyaUri("hasWorkAddress");
+ private static final RyaURI HAS_EXTENSION = createRyaUri("hasExtension");
+ private static final RyaURI HAS_OFFICE_ROOM_NUMBER = createRyaUri("hasOfficeRoomNumber");
+
+ // Type URIs
+ private static final RyaURI PERSON_TYPE_URI = new RyaURI("urn:example/person");
+ private static final RyaURI EMPLOYEE_TYPE_URI = new RyaURI("urn:example/employee");
+
+ // Entities
+ private static final Entity BOB_ENTITY = createBobEntity();
+
+ // Types
+ private static final Type PERSON_TYPE = createPersonType();
+ private static final Type EMPLOYEE_TYPE = createEmployeeType();
+
+ private static MongoDBRdfConfiguration conf;
+ private static MongoDbSmartUri smartUriConverter;
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ conf = new MongoDBRdfConfiguration();
+ conf.set(ConfigUtils.USE_MONGO, "true");
+ conf.set(MongoDBRdfConfiguration.MONGO_DB_NAME, "test");
+ conf.set(MongoDBRdfConfiguration.MONGO_COLLECTION_PREFIX, "rya_");
+ conf.setTablePrefix("another_");
+ }
+
+ @Before
+ public void setupTest() throws Exception {
+ final MongoClient client = MockMongoFactory.newFactory().newMongoClient();
+ conf.setMongoClient(client);
+
+ if (smartUriConverter != null) {
+ smartUriConverter.shutdown();
+ }
+ smartUriConverter = new MongoDbSmartUri(conf);
+ }
+
+ /**
+ * Creates a {@link RyaURI} for the specified local name.
+ * @param localName the URI's local name.
+ * @return the {@link RyraURI}.
+ */
+ private static RyaURI createRyaUri(final String localName) {
+ return createRyaUri(NAMESPACE, localName);
+ //return new RyaURI("http://" + localName);
+ }
+
+ /**
+ * Creates a {@link RyaURI} for the specified local name.
+ * @param namespace the namespace.
+ * @param localName the URI's local name.
+ * @return the {@link RyraURI}.
+ */
+ private static RyaURI createRyaUri(final String namespace, final String localName) {
+ return RdfToRyaConversions.convertURI(VALUE_FACTORY.createURI(namespace, localName));
+ }
+
+ private static Entity createBobEntity() {
+ final Entity bobEntity = Entity.builder()
+ .setSubject(BOB)
+ .setExplicitType(PERSON_TYPE_URI)
+ .setExplicitType(EMPLOYEE_TYPE_URI)
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_WEIGHT, floatRyaType(250.75f)))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_HEIGHT, doubleRyaType(72.5)))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_SSN, stringRyaType("123-45-6789")))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_AGE, shortRyaType((short) 40)))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_INCOME, intRyaType(50000)))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_NUMBER_OF_CHILDREN, byteRyaType((byte) 2)))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_LICENSE_NUMBER, longRyaType(123456789012L)))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_EYE_COLOR, stringRyaType("blue")))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_HAIR_COLOR, stringRyaType("brown")))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_DATE_OF_BIRTH, dateRyaType(new DateTime().minusYears(40))))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_EXPIRATION_DATE, dateRyaType(new Date())))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_GLASSES, booleanRyaType(true)))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_EMAIL_ADDRESS, uriRyaType(new URIImpl("mailto:bob.smitch00@gmail.com"))))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_ATTRIBUTE_SPACE, stringRyaType("attribute space")))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_MOTTO, stringRyaType("!@#*\\&%20^ smörgåsbord")))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_BLOOD_TYPE, stringRyaType("A+ blood type")))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_SEX, stringRyaType("M")))
+ .setProperty(PERSON_TYPE_URI, new Property(HAS_ADDRESS, stringRyaType("123 Fake St. Washington, DC 20024")))
+ .setProperty(EMPLOYEE_TYPE_URI, new Property(HAS_POSITION_TITLE, stringRyaType("Assistant to the Regional Manager")))
+ .setProperty(EMPLOYEE_TYPE_URI, new Property(HAS_WORK_ADDRESS, stringRyaType("987 Fake Office Rd. Washington, DC 20024")))
+ .setProperty(EMPLOYEE_TYPE_URI, new Property(HAS_EXTENSION, shortRyaType((short) 555)))
+ .setProperty(EMPLOYEE_TYPE_URI, new Property(HAS_OFFICE_ROOM_NUMBER, shortRyaType((short) 9999)))
+ .build();
+
+ return bobEntity;
+ }
+
+ private static Type createPersonType() {
+ final Type personType = new Type(PERSON_TYPE_URI,
+ ImmutableSet.<RyaURI>builder()
+ .add(HAS_WEIGHT)
+ .add(HAS_HEIGHT)
+ .add(HAS_SSN)
+ .add(HAS_AGE)
+ .add(HAS_INCOME)
+ .add(HAS_NUMBER_OF_CHILDREN)
+ .add(HAS_LICENSE_NUMBER)
+ .add(HAS_EYE_COLOR)
+ .add(HAS_HAIR_COLOR)
+ .add(HAS_DATE_OF_BIRTH)
+ .add(HAS_EXPIRATION_DATE)
+ .add(HAS_GLASSES)
+ .add(HAS_EMAIL_ADDRESS)
+ .add(HAS_ATTRIBUTE_SPACE)
+ .add(HAS_MOTTO)
+ .add(HAS_BLOOD_TYPE)
+ .add(HAS_SEX)
+ .add(HAS_ADDRESS)
+ .build());
+ return personType;
+ }
+
+ private static Type createEmployeeType() {
+ final Type employeeType = new Type(EMPLOYEE_TYPE_URI,
+ ImmutableSet.<RyaURI>builder()
+ .add(HAS_POSITION_TITLE)
+ .add(HAS_WORK_ADDRESS)
+ .add(HAS_EXTENSION)
+ .add(HAS_OFFICE_ROOM_NUMBER)
+ .build());
+ return employeeType;
+ }
+
+ private static String getRyaUriLocalName(final RyaURI ryaUri) {
+ return new URIImpl(ryaUri.getData()).getLocalName();
+ }
+
+ @Test
+ public void testSerializeDeserialize() throws SmartUriException, URISyntaxException {
+ final URI smartUri = SmartUriAdapter.serializeUriEntity(BOB_ENTITY);
+ System.out.println(smartUri);
+ final Entity resultEntity = SmartUriAdapter.deserializeUriEntity(smartUri);
+ System.out.println(resultEntity);
+ assertEquals(BOB_ENTITY.getSubject(), resultEntity.getSubject());
+ //assertTrue(Paths.get(BOB_ENTITY.getSubject().getData()).equals(Paths.get(resultEntity.getSubject().getData())));
+ }
+
+ @Test
+ public void testStorage() throws SmartUriException, MalformedQueryException, RuntimeException, QueryEvaluationException {
+ smartUriConverter.storeEntity(BOB_ENTITY);
+
+ final String sparql = "SELECT * WHERE { " +
+ "<" + BOB.getData() + "> <" + RDF.TYPE + "> <" + PERSON_TYPE.getId().getData() + "> . " +
+ "<" + BOB.getData() + "> <" + HAS_SSN.getData() + "> ?ssn . " +
+ "<" + BOB.getData() + "> <" + HAS_AGE.getData() + "> ?age . " +
+ "<" + BOB.getData() + "> <" + HAS_WEIGHT.getData() + "> ?weight . " +
+ "<" + BOB.getData() + "> <" + HAS_ADDRESS.getData() + "> ?address . " +
+ "}";
+
+ final StatementPatternCollector spCollector = new StatementPatternCollector();
+ new SPARQLParser().parseQuery(sparql, null).getTupleExpr().visit(spCollector);
+ final List<StatementPattern> patterns = spCollector.getStatementPatterns();
+ final EntityQueryNode entityQueryNode = new EntityQueryNode(PERSON_TYPE, patterns, smartUriConverter.getEntityStorage());
+ final QueryBindingSet queryBindingSet = new QueryBindingSet();
+ final Property ssnProperty = BOB_ENTITY.lookupTypeProperty(PERSON_TYPE, HAS_SSN).get();
+ queryBindingSet.addBinding(HAS_SSN.getData(), RyaToRdfConversions.convertValue(ssnProperty.getValue()));
+
+ final CloseableIteration<BindingSet, QueryEvaluationException> iter = entityQueryNode.evaluate(queryBindingSet);
+ int count = 0;
+ // These should match what was used in the SPARQL query.
+ final List<String> queryParamNames = Lists.newArrayList("ssn", "age", "weight", "address");
+ while (iter.hasNext()) {
+ final BindingSet bs = iter.next();
+ assertTrue(bs.getBindingNames().containsAll(queryParamNames));
+ count++;
+ }
+ assertEquals(count, 1);
+ }
+
+ @Test
+ public void testUpdate() throws SmartUriException {
+ smartUriConverter.storeEntity(BOB_ENTITY);
+
+ // New properties to add
+ final RyaURI hasNickName = createRyaUri("hasNickName");
+ final RyaURI hasWindowOffice = createRyaUri("hasWindowOffice");
+
+ final Entity.Builder builder = Entity.builder(BOB_ENTITY);
+ builder.setProperty(PERSON_TYPE_URI, new Property(HAS_AGE, shortRyaType((short) 41)));
+ builder.setProperty(PERSON_TYPE_URI, new Property(hasNickName, stringRyaType("Bobby")));
+ builder.setProperty(EMPLOYEE_TYPE_URI, new Property(HAS_POSITION_TITLE, stringRyaType("Assistant Regional Manager")));
+ builder.setProperty(EMPLOYEE_TYPE_URI, new Property(hasWindowOffice, booleanRyaType(true)));
+ builder.setVersion(BOB_ENTITY.getVersion() + 1);
+ builder.rebuildSmartUri();
+
+ final Entity newBobEntity = builder.build();
+
+ smartUriConverter.updateEntity(BOB_ENTITY, newBobEntity);
+
+ final Entity resultEntity = smartUriConverter.queryEntity(BOB_ENTITY.getSubject());
+ assertEquals(newBobEntity.getVersion(), resultEntity.getVersion());
+ assertEquals(newBobEntity.lookupTypeProperty(PERSON_TYPE, HAS_AGE), resultEntity.lookupTypeProperty(PERSON_TYPE, HAS_AGE));
+ assertEquals(newBobEntity.lookupTypeProperty(PERSON_TYPE, hasNickName), resultEntity.lookupTypeProperty(PERSON_TYPE, hasNickName));
+ assertEquals(newBobEntity.lookupTypeProperty(EMPLOYEE_TYPE, HAS_POSITION_TITLE), resultEntity.lookupTypeProperty(EMPLOYEE_TYPE, HAS_POSITION_TITLE));
+ assertEquals(newBobEntity.lookupTypeProperty(EMPLOYEE_TYPE, hasWindowOffice), resultEntity.lookupTypeProperty(EMPLOYEE_TYPE, hasWindowOffice));
+ assertEquals(newBobEntity.getSmartUri(), resultEntity.getSmartUri());
+ final String resultUriString = resultEntity.getSmartUri().stringValue();
+ assertTrue(resultUriString.contains(getRyaUriLocalName(hasWindowOffice)));
+ assertTrue(resultUriString.contains(getRyaUriLocalName(hasNickName)));
+ }
+
+ @Test
+ public void testQuery() throws SmartUriException {
+ smartUriConverter.storeEntity(BOB_ENTITY);
+
+ // Look up Person Type Entities that match Bob's SSN property
+ final Set<Property> properties = new LinkedHashSet<>();
+ properties.add(BOB_ENTITY.lookupTypeProperty(PERSON_TYPE, HAS_SSN).get());
+ final Map<URI, Value> map = SmartUriAdapter.propertiesToMap(properties);
+
+ final ConvertingCursor<TypedEntity> cursor = smartUriConverter.queryEntity(PERSON_TYPE, map);
+ int count = 0;
+ while (cursor.hasNext()) {
+ final TypedEntity typedEntity = cursor.next();
+ System.out.println(typedEntity);
+ count++;
+ }
+ assertEquals(count, 1);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e71ff2a/extras/indexingExample/pom.xml
----------------------------------------------------------------------
diff --git a/extras/indexingExample/pom.xml b/extras/indexingExample/pom.xml
index 5304884..50c9a29 100644
--- a/extras/indexingExample/pom.xml
+++ b/extras/indexingExample/pom.xml
@@ -78,6 +78,12 @@ under the License.
<artifactId>sesame-queryrender</artifactId>
<version>${openrdf.sesame.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e71ff2a/extras/rya.merger/pom.xml
----------------------------------------------------------------------
diff --git a/extras/rya.merger/pom.xml b/extras/rya.merger/pom.xml
index 62b799e..44f65ef 100644
--- a/extras/rya.merger/pom.xml
+++ b/extras/rya.merger/pom.xml
@@ -133,11 +133,6 @@ under the License.
</dependency>
<dependency>
- <groupId>commons-httpclient</groupId>
- <artifactId>commons-httpclient</artifactId>
- </dependency>
-
- <dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
</dependency>