http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/definition/BaseResourceDefinition.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/definition/BaseResourceDefinition.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/definition/BaseResourceDefinition.java
new file mode 100644
index 0000000..32d6f30
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/definition/BaseResourceDefinition.java
@@ -0,0 +1,150 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.definition;
+
+import org.apache.atlas.AtlasException;
+import org.apache.atlas.catalog.*;
+import org.apache.atlas.catalog.exception.CatalogRuntimeException;
+import org.apache.atlas.catalog.exception.InvalidPayloadException;
+import org.apache.atlas.catalog.projection.Projection;
+import org.apache.atlas.catalog.projection.Relation;
+import org.apache.atlas.typesystem.types.AttributeDefinition;
+import org.apache.atlas.typesystem.types.AttributeInfo;
+import org.apache.atlas.typesystem.types.Multiplicity;
+import org.apache.atlas.typesystem.types.TypeSystem;
+
+import java.util.*;
+
+/**
+ * Base class for resource definitions.
+ */
+public abstract class BaseResourceDefinition implements ResourceDefinition {
+    protected static final TypeSystem typeSystem = TypeSystem.getInstance();
+
+    protected final Set<String> instanceProperties = new HashSet<>();
+    protected final Set<String> collectionProperties = new HashSet<>();
+    protected Map<String, AttributeDefinition> propertyDefs = new HashMap<>();
+    protected Map<String, AttributeInfo> properties = new HashMap<>();
+
+    protected final Map<String, Projection> projections = new HashMap<>();
+    protected final Map<String, Relation> relations = new HashMap<>();
+
+    protected final PropertyMapper propertyMapper;
+    protected final Map<String, PropertyValueFormatter> 
propertyValueFormatters = new HashMap<>();
+
+
+    public BaseResourceDefinition() {
+        DefaultDateFormatter defaultDateFormatter = new DefaultDateFormatter();
+        registerPropertyValueFormatter("creation_time", defaultDateFormatter);
+        registerPropertyValueFormatter("modified_time", defaultDateFormatter);
+
+        this.propertyMapper = createPropertyMapper();
+    }
+
+    @Override
+    public void validate(Request request) throws InvalidPayloadException {
+        Collection<String> propKeys = new 
HashSet<>(request.getProperties().keySet());
+        Collection<String> missingProperties = new HashSet<>();
+        for (AttributeInfo property : properties.values()) {
+            String name = property.name;
+            if (property.multiplicity == Multiplicity.REQUIRED) {
+                if (request.getProperty(name) == null) {
+                    missingProperties.add(name);
+                }
+            }
+            propKeys.remove(name);
+        }
+        if (! missingProperties.isEmpty() || ! propKeys.isEmpty()) {
+            throw new InvalidPayloadException(missingProperties, propKeys);
+        }
+        //todo: property type validation
+    }
+
+    @Override
+    public Collection<AttributeDefinition> getPropertyDefinitions() {
+        return propertyDefs.values();
+    }
+
+    @Override
+    public  Map<String, Object> filterProperties(Request request, Map<String, 
Object> propertyMap) {
+        Request.Cardinality cardinality = request.getCardinality();
+        Collection<String> requestProperties = 
request.getAdditionalSelectProperties();
+        Iterator<Map.Entry<String, Object>> propIter = 
propertyMap.entrySet().iterator();
+        while(propIter.hasNext()) {
+            Map.Entry<String, Object> propEntry = propIter.next();
+            String prop = propEntry.getKey();
+            if (! requestProperties.contains(prop)) {
+                if (cardinality == Request.Cardinality.COLLECTION) {
+                    if (! collectionProperties.contains(prop)) {
+                        propIter.remove();
+                    }
+                } else {
+                    if (! instanceProperties.isEmpty() && ! 
instanceProperties.contains(prop)) {
+                        propIter.remove();
+                    }
+                }
+            }
+        }
+        return propertyMap;
+    }
+
+    @Override
+    public Map<String, Projection> getProjections() {
+        return projections;
+    }
+
+    @Override
+    public Map<String, Relation> getRelations() {
+        return relations;
+    }
+
+
+    @Override
+    public synchronized PropertyMapper getPropertyMapper() {
+        return propertyMapper;
+    }
+
+    @Override
+    public Map<String, PropertyValueFormatter> getPropertyValueFormatters() {
+        return propertyValueFormatters;
+    }
+
+    protected void registerProperty(AttributeDefinition propertyDefinition) {
+        try {
+            propertyDefs.put(propertyDefinition.name, propertyDefinition);
+            properties.put(propertyDefinition.name, new 
AttributeInfo(typeSystem, propertyDefinition, null));
+        } catch (AtlasException e) {
+            throw new CatalogRuntimeException("Unable to create attribute: " + 
propertyDefinition.name, e);
+        }
+    }
+
+    protected void registerPropertyValueFormatter(String property, 
PropertyValueFormatter valueFormatter) {
+        propertyValueFormatters.put(property, valueFormatter);
+    }
+
+    /**
+     * Create a new property mapper instance.
+     * Should be overridden in children where the default implementation isn't 
sufficient.
+     *
+     * @return a new property mapper instance
+     */
+    protected PropertyMapper createPropertyMapper() {
+        return new DefaultPropertyMapper();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/definition/EntityResourceDefinition.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/definition/EntityResourceDefinition.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/definition/EntityResourceDefinition.java
new file mode 100644
index 0000000..cf55f1f
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/definition/EntityResourceDefinition.java
@@ -0,0 +1,120 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.definition;
+
+import com.tinkerpop.pipes.PipeFunction;
+import com.tinkerpop.pipes.transform.TransformFunctionPipe;
+import org.apache.atlas.catalog.Request;
+import org.apache.atlas.catalog.exception.InvalidPayloadException;
+import org.apache.atlas.catalog.projection.*;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Entity resource definition.
+ */
+public class EntityResourceDefinition extends BaseResourceDefinition {
+    public EntityResourceDefinition() {
+        collectionProperties.add("name");
+        collectionProperties.add("id");
+        collectionProperties.add("type");
+
+        RelationProjection tagProjection = getTagProjection();
+        projections.put("tags", tagProjection);
+        RelationProjection traitProjection = getTraitProjection();
+        projections.put("traits", traitProjection);
+        projections.put("default", getDefaultRelationProjection());
+
+        relations.put(tagProjection.getName(), tagProjection.getRelation());
+        relations.put(traitProjection.getName(), 
traitProjection.getRelation());
+    }
+
+    @Override
+    public String getIdPropertyName() {
+        return "id";
+    }
+
+    // not meaningful for entities
+    @Override
+    public String getTypeName() {
+        return null;
+    }
+
+    @Override
+    public void validate(Request request) throws InvalidPayloadException {
+        // no op for entities as we don't currently create entities and
+        // each entity type is different
+    }
+
+    @Override
+    public String resolveHref(Map<String, Object> properties) {
+        Object id = properties.get("id");
+        return id == null ? null : String.format("v1/entities/%s", id);
+    }
+
+    private RelationProjection getTagProjection() {
+        Relation traitRelation = new TagRelation();
+        RelationProjection tagProjection = new RelationProjection("tags", 
Collections.singleton("name"),
+                traitRelation, Projection.Cardinality.MULTIPLE);
+        tagProjection.addPipe(new TransformFunctionPipe<>(
+                new PipeFunction<Collection<ProjectionResult>, 
Collection<ProjectionResult>>() {
+                    @Override
+                    public Collection<ProjectionResult> 
compute(Collection<ProjectionResult> results) {
+                        for (ProjectionResult result : results) {
+                            for (Map<String, Object> properties : 
result.getPropertyMaps()) {
+                                properties.put("href", 
String.format("v1/entities/%s/tags/%s",
+                                        
result.getStartingVertex().getProperty("id"), properties.get("name")));
+                            }
+                        }
+                        return results;
+                    }
+                }));
+        return tagProjection;
+    }
+
+    private RelationProjection getTraitProjection() {
+        return new RelationProjection("traits", Collections.<String>emptySet(),
+                new TraitRelation(), Projection.Cardinality.MULTIPLE);
+    }
+
+    private RelationProjection getDefaultRelationProjection() {
+        Relation genericRelation = new GenericRelation(this);
+        RelationProjection relationProjection = new RelationProjection(
+                "relations",
+                Arrays.asList("type", "id", "name"),
+                genericRelation, Projection.Cardinality.MULTIPLE);
+
+        relationProjection.addPipe(new TransformFunctionPipe<>(
+                new PipeFunction<Collection<ProjectionResult>, 
Collection<ProjectionResult>>() {
+                    @Override
+                    public Collection<ProjectionResult> 
compute(Collection<ProjectionResult> results) {
+                        for (ProjectionResult result : results) {
+                            for (Map<String, Object> properties : 
result.getPropertyMaps()) {
+                                properties.put("href", 
String.format("v1/entities/%s", properties.get("id")));
+                            }
+                        }
+                        return results;
+                    }
+                }));
+        return relationProjection;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/definition/EntityTagResourceDefinition.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/definition/EntityTagResourceDefinition.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/definition/EntityTagResourceDefinition.java
new file mode 100644
index 0000000..c5a4213
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/definition/EntityTagResourceDefinition.java
@@ -0,0 +1,105 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.definition;
+
+import com.tinkerpop.pipes.PipeFunction;
+import com.tinkerpop.pipes.transform.TransformFunctionPipe;
+import org.apache.atlas.catalog.DefaultPropertyMapper;
+import org.apache.atlas.catalog.PropertyMapper;
+import org.apache.atlas.catalog.ResourceComparator;
+import org.apache.atlas.catalog.VertexWrapper;
+import org.apache.atlas.catalog.projection.Projection;
+import org.apache.atlas.catalog.projection.ProjectionResult;
+import org.apache.atlas.repository.Constants;
+import org.apache.atlas.typesystem.types.DataTypes;
+import org.apache.atlas.typesystem.types.utils.TypesUtil;
+
+import java.util.*;
+
+/**
+ * Entity Tag resource definition.
+ */
+public class EntityTagResourceDefinition extends BaseResourceDefinition {
+    public static final String ENTITY_GUID_PROPERTY = "entity-guid";
+
+    public EntityTagResourceDefinition() {
+        registerProperty(TypesUtil.createRequiredAttrDef("name", 
DataTypes.STRING_TYPE));
+
+        instanceProperties.add("name");
+        instanceProperties.add("description");
+        instanceProperties.add("creation_time");
+
+        collectionProperties.add("name");
+        collectionProperties.add("description");
+
+        projections.put("terms", getTermProjection());
+    }
+
+    @Override
+    public String getIdPropertyName() {
+        return "name";
+    }
+
+    //not meaningful for entity tags
+    @Override
+    public String getTypeName() {
+        return null;
+    }
+
+    @Override
+    public String resolveHref(Map<String, Object> properties) {
+        return String.format("v1/entities/%s/tags/%s", 
properties.get(ENTITY_GUID_PROPERTY), properties.get("name"));
+    }
+
+    private Projection getTermProjection() {
+        return new Projection("term", Projection.Cardinality.SINGLE,
+                new TransformFunctionPipe<>(new PipeFunction<VertexWrapper, 
Collection<ProjectionResult>>() {
+                    @Override
+                    public Collection<ProjectionResult> compute(VertexWrapper 
start) {
+                        Map<String, Object> map = new TreeMap<>(new 
ResourceComparator());
+
+                        StringBuilder sb = new StringBuilder();
+                        sb.append("v1/taxonomies/");
+
+                        String fullyQualifiedName = 
start.getVertex().getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY);
+                        String[] paths = fullyQualifiedName.split("\\.");
+                        // first path segment is the taxonomy
+                        sb.append(paths[0]);
+
+                        for (int i = 1; i < paths.length; ++i) {
+                            String path = paths[i];
+                            if (path != null && !path.isEmpty()) {
+                                sb.append("/terms/");
+                                sb.append(path);
+                            }
+                        }
+
+                        map.put("href", sb.toString());
+                        return Collections.singleton(new 
ProjectionResult("term", start,
+                                Collections.singleton(map)));
+                    }
+                }));
+    }
+
+    @Override
+    protected PropertyMapper createPropertyMapper() {
+        return new 
DefaultPropertyMapper(Collections.singletonMap(Constants.ENTITY_TYPE_PROPERTY_KEY,
 "name"),
+                Collections.singletonMap("name", 
Constants.ENTITY_TYPE_PROPERTY_KEY));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/definition/ResourceDefinition.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/definition/ResourceDefinition.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/definition/ResourceDefinition.java
new file mode 100644
index 0000000..f310c5a
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/definition/ResourceDefinition.java
@@ -0,0 +1,112 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.definition;
+
+import org.apache.atlas.catalog.PropertyMapper;
+import org.apache.atlas.catalog.PropertyValueFormatter;
+import org.apache.atlas.catalog.Request;
+import org.apache.atlas.catalog.exception.InvalidPayloadException;
+import org.apache.atlas.catalog.projection.Projection;
+import org.apache.atlas.catalog.projection.Relation;
+import org.apache.atlas.typesystem.types.AttributeDefinition;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Resource definition.
+ */
+public interface ResourceDefinition {
+    /**
+     * The type name of the resource.
+     *
+     * @return the resources type name
+     */
+    String getTypeName();
+    /**
+     * Validate a user request.
+     *
+     * @param request  user request
+     *
+     * @throws InvalidPayloadException if the request payload is invalid in 
any way
+     */
+    void validate(Request request) throws InvalidPayloadException;
+
+    /**
+     * Get the name of the resources id property.
+     *
+     * @return the id property name
+     */
+    String getIdPropertyName();
+
+    /**
+     * Get the property definitions for the resource.
+     *
+     * @return resource property definitions
+     */
+    //todo: abstract usage of AttributeDefinition
+    Collection<AttributeDefinition> getPropertyDefinitions();
+
+    /**
+     * Filter out properties which shouldn't be returned in the result.
+     * The passed in map is directly modified as well as returned.
+     *
+     * @param request      user request
+     * @param propertyMap  property map to filter
+     *
+     * @return the filtered property map
+     */
+    Map<String, Object> filterProperties(Request request, Map<String, Object> 
propertyMap);
+
+    /**
+     * Generate an href for the resource from the provided resource property 
map.
+     *
+     * @param properties  resource property map
+     *
+     * @return a URL to be used as an href property value for the resource
+     */
+    String resolveHref(Map<String, Object> properties);
+
+    /**
+     * Get map of resource projections.
+     *
+     * @return map of resource projections
+     */
+    Map<String, Projection> getProjections();
+
+    /**
+     * Get map of resource relations.
+     *
+     * @return map of resource relations
+     */
+    Map<String, Relation> getRelations();
+
+    /**
+     * Get the property mapper associated with the resource.
+     *
+     * @return associated property mapper
+     */
+    PropertyMapper getPropertyMapper();
+
+    /**
+     * Get the registered property value formatters.
+     * @return map of property name to property value formatter
+     */
+    Map<String, PropertyValueFormatter> getPropertyValueFormatters();
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/definition/TaxonomyResourceDefinition.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/definition/TaxonomyResourceDefinition.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/definition/TaxonomyResourceDefinition.java
new file mode 100644
index 0000000..a3fbdf1
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/definition/TaxonomyResourceDefinition.java
@@ -0,0 +1,89 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.definition;
+
+import com.tinkerpop.pipes.PipeFunction;
+import com.tinkerpop.pipes.transform.TransformFunctionPipe;
+import org.apache.atlas.catalog.Request;
+import org.apache.atlas.catalog.VertexWrapper;
+import org.apache.atlas.catalog.exception.InvalidPayloadException;
+import org.apache.atlas.catalog.projection.Projection;
+import org.apache.atlas.catalog.projection.ProjectionResult;
+import org.apache.atlas.typesystem.types.DataTypes;
+import org.apache.atlas.typesystem.types.utils.TypesUtil;
+
+import java.util.*;
+
+/**
+ * Taxonomy resource definition.
+ */
+public class TaxonomyResourceDefinition extends BaseResourceDefinition {
+    public TaxonomyResourceDefinition() {
+        registerProperty(TypesUtil.createUniqueRequiredAttrDef("name", 
DataTypes.STRING_TYPE));
+        registerProperty(TypesUtil.createOptionalAttrDef("description", 
DataTypes.STRING_TYPE));
+
+        //todo: combine with above registrations
+        instanceProperties.add("name");
+        instanceProperties.add("description");
+        instanceProperties.add("creation_time");
+
+        collectionProperties.add("name");
+        collectionProperties.add("description");
+
+        projections.put("terms", getTermsProjection());
+    }
+
+    @Override
+    public void validate(Request request) throws InvalidPayloadException {
+        super.validate(request);
+        if (String.valueOf(request.getProperties().get("name")).contains(".")) 
{
+            throw new InvalidPayloadException("The \"name\" property may not 
contain the character '.'");
+        }
+    }
+
+    @Override
+    public String getTypeName() {
+        return "Taxonomy";
+    }
+
+    @Override
+    public String getIdPropertyName() {
+        return "name";
+    }
+
+    @Override
+    public String resolveHref(Map<String, Object> properties) {
+        return String.format("v1/taxonomies/%s", properties.get("name"));
+    }
+
+    private Projection getTermsProjection() {
+        final String termsProjectionName = "terms";
+        return new Projection(termsProjectionName, 
Projection.Cardinality.SINGLE,
+                new TransformFunctionPipe<>(new PipeFunction<VertexWrapper, 
Collection<ProjectionResult>>() {
+                    private String baseHref = "v1/taxonomies/";
+                    @Override
+                    public Collection<ProjectionResult> compute(VertexWrapper 
v) {
+                        Map<String, Object> map = new HashMap<>();
+                        map.put("href", baseHref + v.getProperty("name") + 
"/terms");
+                        return Collections.singleton(new 
ProjectionResult(termsProjectionName, v,
+                                Collections.singleton(map)));
+                    }
+                }));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/definition/TermResourceDefinition.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/definition/TermResourceDefinition.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/definition/TermResourceDefinition.java
new file mode 100644
index 0000000..19dd049
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/definition/TermResourceDefinition.java
@@ -0,0 +1,158 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.definition;
+
+import com.tinkerpop.pipes.PipeFunction;
+import com.tinkerpop.pipes.transform.TransformFunctionPipe;
+import org.apache.atlas.catalog.Request;
+import org.apache.atlas.catalog.ResourceComparator;
+import org.apache.atlas.catalog.TermPath;
+import org.apache.atlas.catalog.VertexWrapper;
+import org.apache.atlas.catalog.exception.InvalidPayloadException;
+import org.apache.atlas.catalog.projection.Projection;
+import org.apache.atlas.catalog.projection.ProjectionResult;
+import org.apache.atlas.repository.Constants;
+import org.apache.atlas.typesystem.types.*;
+import org.apache.atlas.typesystem.types.utils.TypesUtil;
+
+import java.util.*;
+
+/**
+ * Term resource definition.
+ */
+public class TermResourceDefinition extends BaseResourceDefinition {
+    public TermResourceDefinition() {
+        registerProperty(TypesUtil.createRequiredAttrDef("name", 
DataTypes.STRING_TYPE));
+        registerProperty(TypesUtil.createOptionalAttrDef("description", 
DataTypes.STRING_TYPE));
+        registerProperty(TypesUtil.createOptionalAttrDef("available_as_tag", 
DataTypes.BOOLEAN_TYPE));
+        registerProperty(TypesUtil.createOptionalAttrDef("acceptable_use", 
DataTypes.STRING_TYPE));
+
+        instanceProperties.add("name");
+        instanceProperties.add("description");
+        instanceProperties.add("creation_time");
+        instanceProperties.add("available_as_tag");
+        instanceProperties.add("acceptable_use");
+
+        collectionProperties.add("name");
+        collectionProperties.add("description");
+
+        projections.put("terms", getSubTermProjection());
+        projections.put("hierarchy", getHierarchyProjection());
+    }
+
+    @Override
+    public void validate(Request request) throws InvalidPayloadException {
+        super.validate(request);
+
+        String name = request.getProperty("name");
+        // name will be in the fully qualified form: taxonomyName.termName
+        if (! name.contains(".")) {
+            throw new InvalidPayloadException("Term name must be in the form 
'taxonomyName.termName.subTermName'");
+        }
+
+        if (! request.getProperties().containsKey("available_as_tag")) {
+            request.getProperties().put("available_as_tag", true);
+        }
+    }
+
+    @Override
+    public String getTypeName() {
+        return "Term";
+    }
+
+    @Override
+    public String getIdPropertyName() {
+        return "name";
+    }
+
+    //todo
+    @Override
+    public String resolveHref(Map<String, Object> properties) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("v1/taxonomies/");
+
+        TermPath termPath = new 
TermPath(String.valueOf(properties.get("name")));
+        String[] paths = termPath.getPathSegments();
+        sb.append(termPath.getTaxonomyName());
+
+        for (String path : paths) {
+            //todo: shouldn't need to check for null or empty after TermPath 
addition
+            if (path != null && !path.isEmpty()) {
+                sb.append("/terms/");
+                sb.append(path);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    private Projection getHierarchyProjection() {
+        final String projectionName = "hierarchy";
+        return new Projection(projectionName, Projection.Cardinality.SINGLE,
+                new TransformFunctionPipe<>(new PipeFunction<VertexWrapper, 
Collection<ProjectionResult>>() {
+                    @Override
+                    public Collection<ProjectionResult> compute(VertexWrapper 
start) {
+                        Map<String, Object> map = new TreeMap<>(new 
ResourceComparator());
+
+                        TermPath termPath = new 
TermPath(start.getVertex().<String>getProperty(
+                                Constants.ENTITY_TYPE_PROPERTY_KEY));
+
+                        map.put("path", termPath.getPath());
+                        map.put("short_name", termPath.getShortName());
+                        map.put("taxonomy", termPath.getTaxonomyName());
+
+                        return Collections.singleton(new 
ProjectionResult(projectionName, start,
+                                Collections.singleton(map)));
+                    }
+                }));
+
+    }
+
+    private Projection getSubTermProjection() {
+        //todo: combine with other term projections
+        final String termsProjectionName = "terms";
+        return new Projection(termsProjectionName, 
Projection.Cardinality.SINGLE,
+                new TransformFunctionPipe<>(new PipeFunction<VertexWrapper, 
Collection<ProjectionResult>>() {
+                    @Override
+                    public Collection<ProjectionResult> compute(VertexWrapper 
start) {
+                        Map<String, Object> map = new TreeMap<>(new 
ResourceComparator());
+
+                        StringBuilder sb = new StringBuilder();
+                        sb.append("v1/taxonomies/");
+
+                        TermPath termPath = new 
TermPath(start.getVertex().<String>getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY));
+                        String[] paths = termPath.getPathSegments();
+                        sb.append(termPath.getTaxonomyName());
+
+                        for (String path : paths) {
+                            //todo: shouldn't need to check for null or empty 
after TermPath addition
+                            if (path != null && !path.isEmpty()) {
+                                sb.append("/terms/");
+                                sb.append(path);
+                            }
+                        }
+                        sb.append("/terms");
+
+                        map.put("href", sb.toString());
+                        return Collections.singleton(new 
ProjectionResult(termsProjectionName, start,
+                                Collections.singleton(map)));
+                    }
+                }));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/exception/CatalogException.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/exception/CatalogException.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/exception/CatalogException.java
new file mode 100644
index 0000000..7bb2f7b
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/exception/CatalogException.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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.exception;
+
+/**
+ * Base checked catalog exception.
+ */
+public class CatalogException extends Exception {
+
+    private int status;
+
+    public CatalogException(String message, int status) {
+        super(message);
+        this.status = status;
+    }
+
+    public int getStatus() {
+        return status;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/exception/CatalogRuntimeException.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/exception/CatalogRuntimeException.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/exception/CatalogRuntimeException.java
new file mode 100644
index 0000000..51fd7af
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/exception/CatalogRuntimeException.java
@@ -0,0 +1,43 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.exception;
+
+/**
+ * Base runtime catalog exception.
+ */
+public class CatalogRuntimeException extends RuntimeException {
+    int statusCode = 500;
+
+    public CatalogRuntimeException(Exception e) {
+        super("", e);
+    }
+
+    public CatalogRuntimeException(String message, Exception e) {
+        super(message, e);
+    }
+
+    public CatalogRuntimeException(String message, int statusCode) {
+        super(message);
+        this.statusCode = statusCode;
+    }
+
+    public int getStatusCode() {
+        return statusCode;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/exception/InvalidPayloadException.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/exception/InvalidPayloadException.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/exception/InvalidPayloadException.java
new file mode 100644
index 0000000..34c5ab5
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/exception/InvalidPayloadException.java
@@ -0,0 +1,39 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.exception;
+
+import java.util.Collection;
+
+/**
+ * Exception used for invalid API payloads.
+ */
+public class InvalidPayloadException extends CatalogException {
+    private final static String baseMsg = "Invalid Request.";
+    private final static String missingMsg = " The following required 
properties are missing: %s.";
+    private final static String unknownMsg = " The following properties are 
not supported: %s";
+
+    public InvalidPayloadException(Collection<String> missingProperties, 
Collection<String> unknownProperties) {
+        super(baseMsg + (!missingProperties.isEmpty() ? 
String.format(missingMsg, missingProperties): "") +
+                        (!unknownProperties.isEmpty() ? 
String.format(unknownMsg, unknownProperties): ""), 400);
+    }
+
+    public InvalidPayloadException(String msg) {
+        super(msg, 400);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/exception/InvalidQueryException.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/exception/InvalidQueryException.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/exception/InvalidQueryException.java
new file mode 100644
index 0000000..a7abe23
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/exception/InvalidQueryException.java
@@ -0,0 +1,28 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.exception;
+
+/**
+ * Exception for invalid user query.
+ */
+public class InvalidQueryException extends CatalogException {
+    public InvalidQueryException(String message) {
+        super("Unable to parse query: " + message, 400);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/exception/ResourceAlreadyExistsException.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/exception/ResourceAlreadyExistsException.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/exception/ResourceAlreadyExistsException.java
new file mode 100644
index 0000000..d7670c1
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/exception/ResourceAlreadyExistsException.java
@@ -0,0 +1,28 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.exception;
+
+/**
+ * Exception used when an attempt is made to create a resource which already 
exists.
+ */
+public class ResourceAlreadyExistsException extends CatalogException {
+    public ResourceAlreadyExistsException(String message) {
+        super(message, 409);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/exception/ResourceNotFoundException.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/exception/ResourceNotFoundException.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/exception/ResourceNotFoundException.java
new file mode 100644
index 0000000..0307137
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/exception/ResourceNotFoundException.java
@@ -0,0 +1,28 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.exception;
+
+/**
+ * Exception used when an explicitly requested resource doesn't exist.
+ */
+public class ResourceNotFoundException extends CatalogException {
+    public ResourceNotFoundException(String message) {
+        super(message, 404);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/projection/GenericRelation.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/projection/GenericRelation.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/projection/GenericRelation.java
new file mode 100644
index 0000000..5f59bea
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/projection/GenericRelation.java
@@ -0,0 +1,80 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.projection;
+
+import com.tinkerpop.blueprints.Direction;
+import com.tinkerpop.blueprints.Edge;
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.pipes.Pipe;
+import org.apache.atlas.catalog.VertexWrapper;
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.atlas.repository.Constants;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represents a generic relation
+ */
+public class GenericRelation implements Relation {
+    private final ResourceDefinition resourceDefinition;
+
+    public GenericRelation(ResourceDefinition resourceDefinition) {
+        this.resourceDefinition = resourceDefinition;
+    }
+
+    @Override
+    public Collection<RelationSet> traverse(VertexWrapper vWrapper) {
+        Collection<RelationSet> relations = new ArrayList<>();
+        Vertex v = vWrapper.getVertex();
+        String vertexType = v.getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY);
+        Map<String, Collection<VertexWrapper>> vertexMap = new HashMap<>();
+        for (Edge e : v.getEdges(Direction.OUT)) {
+            String edgeLabel = e.getLabel();
+            String edgePrefix = String.format("%s%s.", 
Constants.INTERNAL_PROPERTY_KEY_PREFIX, vertexType);
+            if (edgeLabel.startsWith(edgePrefix)) {
+                Vertex adjacentVertex = e.getVertex(Direction.IN);
+                VertexWrapper relationVertex = new 
VertexWrapper(adjacentVertex, resourceDefinition);
+                String relationName = edgeLabel.substring(edgePrefix.length());
+                Collection<VertexWrapper> vertices = 
vertexMap.get(relationName);
+                if (vertices == null) {
+                    vertices = new ArrayList<>();
+                    vertexMap.put(relationName, vertices);
+                }
+                vertices.add(relationVertex);
+            }
+        }
+        for (Map.Entry<String, Collection<VertexWrapper>> entry : 
vertexMap.entrySet()) {
+            relations.add(new RelationSet(entry.getKey(), entry.getValue()));
+        }
+        return relations;
+    }
+
+    @Override
+    public Pipe asPipe() {
+        return null;
+    }
+
+    @Override
+    public ResourceDefinition getResourceDefinition() {
+        return resourceDefinition;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/projection/Projection.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/projection/Projection.java 
b/catalog/src/main/java/org/apache/atlas/catalog/projection/Projection.java
new file mode 100644
index 0000000..daa1351
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/projection/Projection.java
@@ -0,0 +1,66 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.projection;
+
+import com.tinkerpop.pipes.Pipe;
+import com.tinkerpop.pipes.util.Pipeline;
+import org.apache.atlas.catalog.VertexWrapper;
+
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Projection representation.
+ * Used to project properties onto a resource from another source.
+ */
+public class Projection {
+    public enum Cardinality {SINGLE, MULTIPLE}
+
+    private final String m_name;
+    private final Cardinality m_cardinality;
+    protected Pipeline<VertexWrapper, Collection<ProjectionResult>> m_pipeline 
= new Pipeline<>();
+
+    public Projection(String name, Cardinality cardinality) {
+        m_name = name;
+        m_cardinality = cardinality;
+    }
+
+    public Projection(String name, Cardinality cardinality, 
Pipe<VertexWrapper, Collection<ProjectionResult>> pipe) {
+        m_name = name;
+        m_cardinality = cardinality;
+        m_pipeline.addPipe(pipe);
+    }
+
+    public Collection<ProjectionResult> values(VertexWrapper start) {
+        m_pipeline.setStarts(Collections.singleton(start));
+        return m_pipeline.iterator().next();
+    }
+
+    public void addPipe(Pipe<Collection<ProjectionResult>, 
Collection<ProjectionResult>> p) {
+        m_pipeline.addPipe(p);
+    }
+
+    public String getName() {
+        return m_name;
+    }
+
+    public Cardinality getCardinality() {
+        return m_cardinality;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/projection/ProjectionResult.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/projection/ProjectionResult.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/projection/ProjectionResult.java
new file mode 100644
index 0000000..7b12e2d
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/projection/ProjectionResult.java
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.projection;
+
+import org.apache.atlas.catalog.VertexWrapper;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Result of a projection.
+ */
+public class ProjectionResult {
+    private final VertexWrapper m_startVertex;
+    private final String m_name;
+    private final Collection<Map<String, Object>> m_propertyMaps;
+
+    public ProjectionResult(String name, VertexWrapper startingVertex, 
Collection<Map<String, Object>> propertyMaps) {
+        m_name = name;
+        m_startVertex = startingVertex;
+        m_propertyMaps = propertyMaps;
+    }
+
+    public String getName() {
+        return m_name;
+    }
+
+    public VertexWrapper getStartingVertex() {
+        return m_startVertex;
+    }
+
+    public Collection<Map<String, Object>> getPropertyMaps() {
+        return m_propertyMaps;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/projection/Relation.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/projection/Relation.java 
b/catalog/src/main/java/org/apache/atlas/catalog/projection/Relation.java
new file mode 100644
index 0000000..b19bc15
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/projection/Relation.java
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.projection;
+
+import com.tinkerpop.pipes.Pipe;
+import org.apache.atlas.catalog.VertexWrapper;
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+
+import java.util.Collection;
+
+/**
+ * Represents the relationship from one vertex to another via an edge.
+ */
+public interface Relation {
+    /**
+     * Traverse the relation.
+     *
+     * @param vWrapper vertex to start traversal from
+     *
+     * @return results of the traversal
+     */
+    Collection<RelationSet> traverse(VertexWrapper vWrapper);
+
+    /**
+     * Get the pipe representation of the traversal.
+     *
+     * @return pipe representation
+     */
+    Pipe asPipe();
+
+    /**
+     * Get the associated resource definition.
+     *
+     * @return associated resource definition
+     */
+    ResourceDefinition getResourceDefinition();
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/projection/RelationProjection.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/projection/RelationProjection.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/projection/RelationProjection.java
new file mode 100644
index 0000000..e435628
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/projection/RelationProjection.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.projection;
+
+import com.tinkerpop.pipes.PipeFunction;
+import com.tinkerpop.pipes.transform.TransformFunctionPipe;
+import org.apache.atlas.catalog.ResourceComparator;
+import org.apache.atlas.catalog.VertexWrapper;
+
+import java.util.*;
+
+/**
+ * Projection based on a relation.
+ */
+public class RelationProjection extends Projection {
+
+    private Relation relation;
+    public RelationProjection(String name, final Collection<String> fields, 
final Relation relation, Cardinality cardinality) {
+        super(name, cardinality, new TransformFunctionPipe<>(
+                new PipeFunction<VertexWrapper, 
Collection<ProjectionResult>>() {
+                    @Override
+                    public Collection<ProjectionResult> compute(VertexWrapper 
start) {
+                        Collection<ProjectionResult> projectionResults = new 
ArrayList<>();
+
+                        for (RelationSet relationSet : 
relation.traverse(start)) {
+                            Collection<Map<String, Object>> propertyMaps = new 
ArrayList<>();
+
+                            for (VertexWrapper vWrapper : 
relationSet.getVertices()) {
+                                Map<String, Object> propertyMap = new 
TreeMap<>(new ResourceComparator());
+                                propertyMaps.add(propertyMap);
+
+                                if (fields.isEmpty()) {
+                                    for (String property : 
vWrapper.getPropertyKeys()) {
+                                        propertyMap.put(property, 
vWrapper.<String>getProperty(property));
+                                    }
+                                } else {
+                                    for (String property : fields) {
+                                        propertyMap.put(property, 
vWrapper.<String>getProperty(property));
+                                    }
+                                }
+                            }
+                            projectionResults.add(new 
ProjectionResult(relationSet.getName(), start, propertyMaps));
+                        }
+                        return projectionResults;
+                    }
+                }));
+        this.relation = relation;
+    }
+
+    public Relation getRelation() {
+        return relation;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/projection/RelationSet.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/projection/RelationSet.java 
b/catalog/src/main/java/org/apache/atlas/catalog/projection/RelationSet.java
new file mode 100644
index 0000000..4adf861
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/projection/RelationSet.java
@@ -0,0 +1,45 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.projection;
+
+import org.apache.atlas.catalog.VertexWrapper;
+
+import java.util.*;
+
+/**
+ * Encapsulates the response of a relation traversal.
+ */
+public class RelationSet {
+
+    private final String m_name;
+    private final Collection<VertexWrapper> m_vertices;
+
+    public RelationSet(String name, Collection<VertexWrapper> vertices) {
+        m_name = name;
+        m_vertices = vertices;
+    }
+
+    public String getName() {
+        return m_name;
+    }
+
+    public Collection<VertexWrapper> getVertices() {
+        return Collections.unmodifiableCollection(m_vertices);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/projection/TagRelation.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/projection/TagRelation.java 
b/catalog/src/main/java/org/apache/atlas/catalog/projection/TagRelation.java
new file mode 100644
index 0000000..a5e15c6
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/projection/TagRelation.java
@@ -0,0 +1,73 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.projection;
+
+import com.tinkerpop.blueprints.Direction;
+import com.tinkerpop.blueprints.Edge;
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.pipes.Pipe;
+import com.tinkerpop.pipes.PipeFunction;
+import com.tinkerpop.pipes.filter.FilterFunctionPipe;
+import org.apache.atlas.catalog.TermVertexWrapper;
+import org.apache.atlas.catalog.VertexWrapper;
+import org.apache.atlas.catalog.definition.EntityTagResourceDefinition;
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.atlas.repository.Constants;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Relation for adjacent Tag vertices.
+ */
+public class TagRelation implements Relation {
+    private static ResourceDefinition resourceDefinition = new 
EntityTagResourceDefinition();
+    @Override
+    public Collection<RelationSet> traverse(VertexWrapper vWrapper) {
+        Vertex v = vWrapper.getVertex();
+        Collection<VertexWrapper> vertices = new ArrayList<>();
+        for (Edge e : v.getEdges(Direction.OUT)) {
+            if 
(e.getLabel().startsWith(v.<String>getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY)))
 {
+                VertexWrapper trait = new 
TermVertexWrapper(e.getVertex(Direction.IN));
+                if (trait.getPropertyKeys().contains("available_as_tag")) {
+                    vertices.add(trait);
+                }
+            }
+        }
+        return Collections.singletonList(new RelationSet("tags", vertices));
+    }
+
+    @Override
+    public Pipe asPipe() {
+        return new FilterFunctionPipe<>(new PipeFunction<Edge, Boolean>() {
+            @Override
+            public Boolean compute(Edge edge) {
+                String name = 
edge.getVertex(Direction.OUT).getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY);
+                VertexWrapper v = new 
TermVertexWrapper(edge.getVertex(Direction.IN));
+                return edge.getLabel().startsWith(name) && 
v.getPropertyKeys().contains("available_as_tag");
+            }
+        });
+    }
+
+    @Override
+    public ResourceDefinition getResourceDefinition() {
+        return resourceDefinition;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/projection/TraitRelation.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/projection/TraitRelation.java 
b/catalog/src/main/java/org/apache/atlas/catalog/projection/TraitRelation.java
new file mode 100644
index 0000000..5f11474
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/projection/TraitRelation.java
@@ -0,0 +1,76 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.projection;
+
+import com.tinkerpop.blueprints.Direction;
+import com.tinkerpop.blueprints.Edge;
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.pipes.Pipe;
+import com.tinkerpop.pipes.PipeFunction;
+import com.tinkerpop.pipes.filter.FilterFunctionPipe;
+import org.apache.atlas.catalog.TermVertexWrapper;
+import org.apache.atlas.catalog.VertexWrapper;
+import org.apache.atlas.catalog.definition.EntityTagResourceDefinition;
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.atlas.repository.Constants;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Trait specific relation.
+ */
+//todo: combine with TagRelation
+public class TraitRelation implements Relation {
+    //todo: for now using entity tag resource definition
+    private static ResourceDefinition resourceDefinition = new 
EntityTagResourceDefinition();
+
+    @Override
+    public Collection<RelationSet> traverse(VertexWrapper vWrapper) {
+        Vertex v = vWrapper.getVertex();
+        Collection<VertexWrapper> vertices = new ArrayList<>();
+        for (Edge e : v.getEdges(Direction.OUT)) {
+            if 
(e.getLabel().startsWith(v.<String>getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY)))
 {
+                VertexWrapper trait = new 
TermVertexWrapper(e.getVertex(Direction.IN));
+                if (! trait.getPropertyKeys().contains("available_as_tag")) {
+                    vertices.add(trait);
+                }
+            }
+        }
+        return Collections.singletonList(new RelationSet("traits", vertices));
+    }
+
+    @Override
+    public Pipe asPipe() {
+        return new FilterFunctionPipe<>(new PipeFunction<Edge, Boolean>() {
+            @Override
+            public Boolean compute(Edge edge) {
+                String type = 
edge.getVertex(Direction.OUT).getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY);
+                VertexWrapper v = new 
TermVertexWrapper(edge.getVertex(Direction.IN));
+                return edge.getLabel().startsWith(type) && ! 
v.getPropertyKeys().contains("available_as_tag");
+            }
+        });
+    }
+
+    @Override
+    public ResourceDefinition getResourceDefinition() {
+        return resourceDefinition;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/AlwaysQueryExpression.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/query/AlwaysQueryExpression.java
 
b/catalog/src/main/java/org/apache/atlas/catalog/query/AlwaysQueryExpression.java
new file mode 100644
index 0000000..d120bc4
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/query/AlwaysQueryExpression.java
@@ -0,0 +1,46 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.query;
+
+import com.tinkerpop.pipes.Pipe;
+import org.apache.atlas.catalog.VertexWrapper;
+
+/**
+ * Query expression which always returns true.
+ */
+public class AlwaysQueryExpression extends BaseQueryExpression {
+    protected AlwaysQueryExpression() {
+        super(null, null, null);
+    }
+
+    @Override
+    public Pipe asPipe() {
+        return null;
+    }
+
+    @Override
+    public boolean evaluate(VertexWrapper vWrapper) {
+        return ! negate;
+    }
+
+    @Override
+    public boolean evaluate(Object value) {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasEntityQuery.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasEntityQuery.java 
b/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasEntityQuery.java
new file mode 100644
index 0000000..e2bc597
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasEntityQuery.java
@@ -0,0 +1,40 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.query;
+
+import com.tinkerpop.gremlin.java.GremlinPipeline;
+import org.apache.atlas.catalog.Request;
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.atlas.repository.Constants;
+
+/**
+ * Entity resource query.
+ */
+public class AtlasEntityQuery extends BaseQuery {
+    public AtlasEntityQuery(QueryExpression queryExpression, 
ResourceDefinition resourceDefinition, Request request) {
+        super(queryExpression, resourceDefinition, request);
+    }
+
+    protected GremlinPipeline getInitialPipeline() {
+        //todo: the property 'entityText' isn't currently indexed
+        //todo: we could use Constants.ENTITY_TYPE_PROPERTY_KEY initially but 
trait instances also contain this property
+        return new 
GremlinPipeline(getGraph()).V().has(Constants.ENTITY_TEXT_PROPERTY_KEY).
+                hasNot(Constants.ENTITY_TYPE_PROPERTY_KEY, "Taxonomy");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasEntityTagQuery.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasEntityTagQuery.java 
b/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasEntityTagQuery.java
new file mode 100644
index 0000000..013bb8a
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasEntityTagQuery.java
@@ -0,0 +1,73 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.query;
+
+import com.tinkerpop.blueprints.Direction;
+import com.tinkerpop.blueprints.Edge;
+import com.tinkerpop.gremlin.java.GremlinPipeline;
+import com.tinkerpop.pipes.PipeFunction;
+import com.tinkerpop.pipes.filter.FilterFunctionPipe;
+import org.apache.atlas.catalog.Request;
+import org.apache.atlas.catalog.TermVertexWrapper;
+import org.apache.atlas.catalog.VertexWrapper;
+import org.apache.atlas.catalog.definition.EntityTagResourceDefinition;
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.atlas.repository.Constants;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Entity Tag resource query.
+ */
+public class AtlasEntityTagQuery extends BaseQuery {
+    private final String guid;
+
+    public AtlasEntityTagQuery(QueryExpression queryExpression, 
ResourceDefinition resourceDefinition, String guid, Request request) {
+        super(queryExpression, resourceDefinition, request);
+        this.guid = guid;
+    }
+
+    @Override
+    protected GremlinPipeline getInitialPipeline() {
+        GremlinPipeline p =  new 
GremlinPipeline(getGraph()).V().has(Constants.GUID_PROPERTY_KEY, guid).outE();
+        //todo: this is basically the same pipeline used in 
TagRelation.asPipe()
+        p.add(new FilterFunctionPipe<>(new PipeFunction<Edge, Boolean>() {
+            @Override
+            public Boolean compute(Edge edge) {
+                String type = 
edge.getVertex(Direction.OUT).getProperty(Constants.ENTITY_TYPE_PROPERTY_KEY);
+                VertexWrapper v = new 
TermVertexWrapper(edge.getVertex(Direction.IN));
+                return edge.getLabel().startsWith(type) && 
v.getPropertyKeys().contains("available_as_tag");
+            }
+        }));
+
+        return p.inV();
+    }
+
+    //todo: duplication of effort with resource definition
+    @Override
+    protected void addHref(Map<String, Object> propertyMap) {
+        Map<String, Object> map = new HashMap<>(propertyMap);
+        map.put(EntityTagResourceDefinition.ENTITY_GUID_PROPERTY, guid);
+        String href = resourceDefinition.resolveHref(map);
+        if (href != null) {
+            propertyMap.put("href", href);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasQuery.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasQuery.java 
b/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasQuery.java
new file mode 100644
index 0000000..af14697
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasQuery.java
@@ -0,0 +1,37 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.query;
+
+import org.apache.atlas.catalog.exception.ResourceNotFoundException;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Query functionality.
+ */
+public interface AtlasQuery {
+    /**
+     * Execute the query.
+     *
+     * @return collection of property maps, one per matching resource
+     * @throws ResourceNotFoundException if an explicitly specified resource 
doesn't exist
+     */
+    Collection<Map<String, Object>> execute() throws ResourceNotFoundException;
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasTaxonomyQuery.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasTaxonomyQuery.java 
b/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasTaxonomyQuery.java
new file mode 100644
index 0000000..787f734
--- /dev/null
+++ 
b/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasTaxonomyQuery.java
@@ -0,0 +1,37 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.query;
+
+import com.tinkerpop.gremlin.java.GremlinPipeline;
+import org.apache.atlas.catalog.Request;
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+
+/**
+ * Taxonomy resource query.
+ */
+public class AtlasTaxonomyQuery extends BaseQuery {
+    public AtlasTaxonomyQuery(QueryExpression queryExpression, 
ResourceDefinition resourceDefinition, Request request) {
+        super(queryExpression, resourceDefinition, request);
+    }
+
+    @Override
+    protected GremlinPipeline getInitialPipeline() {
+        return new GremlinPipeline(getGraph()).V().has("__typeName", 
"Taxonomy");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasTermQuery.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasTermQuery.java 
b/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasTermQuery.java
new file mode 100644
index 0000000..0065101
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/AtlasTermQuery.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.query;
+
+import com.thinkaurelius.titan.core.attribute.Text;
+import com.tinkerpop.gremlin.java.GremlinPipeline;
+import org.apache.atlas.catalog.Request;
+import org.apache.atlas.catalog.TermPath;
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.atlas.repository.Constants;
+
+/**
+ * Term resource query.
+ */
+public class AtlasTermQuery extends BaseQuery {
+    private final TermPath termPath;
+
+    public AtlasTermQuery(QueryExpression queryExpression, ResourceDefinition 
resourceDefinition, TermPath termPath, Request request) {
+        super(queryExpression, resourceDefinition, request);
+        this.termPath = termPath;
+    }
+
+    @Override
+    protected GremlinPipeline getInitialPipeline() {
+        return new GremlinPipeline(getGraph()).V().has("Taxonomy.name", 
termPath.getTaxonomyName()).out().
+                has(Constants.ENTITY_TYPE_PROPERTY_KEY, Text.PREFIX, 
termPath.getFullyQualifiedName());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/BaseQuery.java
----------------------------------------------------------------------
diff --git 
a/catalog/src/main/java/org/apache/atlas/catalog/query/BaseQuery.java 
b/catalog/src/main/java/org/apache/atlas/catalog/query/BaseQuery.java
new file mode 100644
index 0000000..ac8ca6b
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/BaseQuery.java
@@ -0,0 +1,121 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.atlas.catalog.query;
+
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.gremlin.java.GremlinPipeline;
+import com.tinkerpop.pipes.Pipe;
+import org.apache.atlas.catalog.Request;
+import org.apache.atlas.catalog.VertexWrapper;
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.atlas.catalog.exception.ResourceNotFoundException;
+import org.apache.atlas.catalog.projection.Projection;
+import org.apache.atlas.catalog.projection.ProjectionResult;
+import org.apache.atlas.repository.graph.TitanGraphProvider;
+
+import java.util.*;
+
+/**
+ * Base Query implementation.
+ */
+public abstract class BaseQuery implements AtlasQuery {
+    protected final QueryExpression queryExpression;
+    protected final ResourceDefinition resourceDefinition;
+    protected final Request request;
+
+    public BaseQuery(QueryExpression queryExpression, ResourceDefinition 
resourceDefinition, Request request) {
+        this.queryExpression = queryExpression;
+        this.resourceDefinition = resourceDefinition;
+        this.request = request;
+    }
+
+    public Collection<Map<String, Object>> execute() throws 
ResourceNotFoundException {
+        Collection<Map<String, Object>> resultMaps = new ArrayList<>();
+
+        for (Vertex vertex : executeQuery()) {
+            resultMaps.add(processPropertyMap(new VertexWrapper(vertex, 
resourceDefinition)));
+        }
+        return resultMaps;
+    }
+
+    private List<Vertex> executeQuery() {
+        GremlinPipeline pipeline = getInitialPipeline().as("root");
+
+        Pipe adapterPipe = queryExpression.asPipe();
+        //todo: AlwaysQueryAdapter returns null for pipe
+        //todo: Is there a no-op pipe that I could add that wouldn't 
negatively affect performance
+        return adapterPipe == null ?
+                pipeline.toList() :
+                pipeline.add(adapterPipe).back("root").toList();
+    }
+
+    protected abstract GremlinPipeline getInitialPipeline();
+
+    // todo: consider getting
+    protected Map<String, Object> processPropertyMap(VertexWrapper vertex) {
+        Map<String, Object> propertyMap = vertex.getPropertyMap();
+        resourceDefinition.filterProperties(request, propertyMap);
+        addHref(propertyMap);
+
+        return request.getCardinality() == Request.Cardinality.INSTANCE ?
+                applyProjections(vertex, propertyMap) :
+                propertyMap;
+    }
+
+    protected void addHref(Map<String, Object> propertyMap) {
+        String href = resourceDefinition.resolveHref(propertyMap);
+        if (href != null) {
+            propertyMap.put("href", href);
+        }
+    }
+
+    private Map<String, Object> applyProjections(VertexWrapper vertex, 
Map<String, Object> propertyMap) {
+        for (Projection p : resourceDefinition.getProjections().values()) {
+            for (ProjectionResult projectionResult : p.values(vertex)) {
+                if (p.getCardinality() == Projection.Cardinality.MULTIPLE) {
+                    propertyMap.put(projectionResult.getName(), 
projectionResult.getPropertyMaps());
+                } else {
+                    for (Map<String, Object> projectionMap : 
projectionResult.getPropertyMaps()) {
+                        propertyMap.put(projectionResult.getName(), 
projectionMap);
+                    }
+                }
+            }
+        }
+        return propertyMap;
+    }
+
+    protected QueryExpression getQueryExpression() {
+        return queryExpression;
+    }
+
+    protected ResourceDefinition getResourceDefinition() {
+        return resourceDefinition;
+    }
+
+    protected Request getRequest() {
+        return request;
+    }
+
+    //todo: abstract
+    // Underlying method is synchronized and caches the graph in a static field
+    protected TitanGraph getGraph() {
+        return TitanGraphProvider.getGraphInstance();
+    }
+}

Reply via email to