You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by ma...@apache.org on 2017/09/17 22:42:40 UTC

[1/3] atlas git commit: ATLAS-2100: UserProfile & SavedSearch API implementation

Repository: atlas
Updated Branches:
  refs/heads/master d67b0498e -> c1f4007a9


http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java
index 52258e3..ad595c8 100644
--- a/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java
+++ b/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java
@@ -19,25 +19,33 @@ package org.apache.atlas.web.rest;
 
 import org.apache.atlas.AtlasErrorCode;
 import org.apache.atlas.SortOrder;
-import org.apache.atlas.exception.AtlasBaseException;
 import org.apache.atlas.discovery.AtlasDiscoveryService;
+import org.apache.atlas.exception.AtlasBaseException;
 import org.apache.atlas.model.discovery.AtlasSearchResult;
 import org.apache.atlas.model.discovery.SearchParameters;
+import org.apache.atlas.model.profile.AtlasUserSavedSearch;
 import org.apache.atlas.utils.AtlasPerfTracer;
 import org.apache.atlas.web.util.Servlets;
 import org.apache.commons.collections.CollectionUtils;
-import org.springframework.stereotype.Service;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
+import org.springframework.stereotype.Service;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
+import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import java.io.IOException;
+import java.util.List;
 
 /**
  * REST interface for data discovery using dsl or full text search
@@ -48,6 +56,9 @@ import javax.ws.rs.QueryParam;
 public class DiscoveryREST {
     private static final Logger PERF_LOG = AtlasPerfTracer.getPerfLogger("rest.DiscoveryREST");
 
+    @Context
+    private HttpServletRequest httpServletRequest;
+
     private final AtlasDiscoveryService atlasDiscoveryService;
 
     @Inject
@@ -57,11 +68,12 @@ public class DiscoveryREST {
 
     /**
      * Retrieve data for the specified DSL
-     * @param query DSL query
-     * @param typeName limit the result to only entities of specified type or its sub-types
+     *
+     * @param query          DSL query
+     * @param typeName       limit the result to only entities of specified type or its sub-types
      * @param classification limit the result to only entities tagged with the given classification or or its sub-types
-     * @param limit limit the result set to only include the specified number of entries
-     * @param offset start offset of the result set (useful for pagination)
+     * @param limit          limit the result set to only include the specified number of entries
+     * @param offset         start offset of the result set (useful for pagination)
      * @return Search results
      * @throws AtlasBaseException
      * @HTTP 200 On successful DSL execution with some results, might return an empty list if execution succeeded
@@ -82,7 +94,7 @@ public class DiscoveryREST {
         try {
             if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                 perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "DiscoveryREST.searchUsingDSL(" + query + "," + typeName
-                                                            +  "," + classification + "," + limit + "," + offset + ")");
+                        + "," + classification + "," + limit + "," + offset + ")");
             }
 
             String queryStr = query == null ? "" : query;
@@ -106,8 +118,9 @@ public class DiscoveryREST {
 
     /**
      * Retrieve data for the specified fulltext query
-     * @param query Fulltext query
-     * @param limit limit the result set to only include the specified number of entries
+     *
+     * @param query  Fulltext query
+     * @param limit  limit the result set to only include the specified number of entries
      * @param offset start offset of the result set (useful for pagination)
      * @return Search results
      * @throws AtlasBaseException
@@ -119,16 +132,16 @@ public class DiscoveryREST {
     @Path("/fulltext")
     @Consumes(Servlets.JSON_MEDIA_TYPE)
     @Produces(Servlets.JSON_MEDIA_TYPE)
-    public AtlasSearchResult searchUsingFullText(@QueryParam("query")  String query,
+    public AtlasSearchResult searchUsingFullText(@QueryParam("query")                  String  query,
                                                  @QueryParam("excludeDeletedEntities") boolean excludeDeletedEntities,
-                                                 @QueryParam("limit")  int    limit,
-                                                 @QueryParam("offset") int    offset) throws AtlasBaseException {
+                                                 @QueryParam("limit")                  int     limit,
+                                                 @QueryParam("offset")                 int     offset) throws AtlasBaseException {
         AtlasPerfTracer perf = null;
 
         try {
             if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                 perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "DiscoveryREST.searchUsingFullText(" + query + "," +
-                                                               limit + "," + offset + ")");
+                        limit + "," + offset + ")");
             }
 
             return atlasDiscoveryService.searchUsingFullTextQuery(query, excludeDeletedEntities, limit, offset);
@@ -139,11 +152,12 @@ public class DiscoveryREST {
 
     /**
      * Retrieve data for the specified fulltext query
-     * @param query Fulltext query
-     * @param typeName limit the result to only entities of specified type or its sub-types
+     *
+     * @param query          Fulltext query
+     * @param typeName       limit the result to only entities of specified type or its sub-types
      * @param classification limit the result to only entities tagged with the given classification or or its sub-types
-     * @param limit limit the result set to only include the specified number of entries
-     * @param offset start offset of the result set (useful for pagination)
+     * @param limit          limit the result set to only include the specified number of entries
+     * @param offset         start offset of the result set (useful for pagination)
      * @return Search results
      * @throws AtlasBaseException
      * @HTTP 200 On successful FullText lookup with some results, might return an empty list if execution succeeded
@@ -165,7 +179,7 @@ public class DiscoveryREST {
         try {
             if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                 perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "DiscoveryREST.searchUsingBasic(" + query + "," +
-                                                    typeName + "," + classification + "," + limit + "," + offset + ")");
+                        typeName + "," + classification + "," + limit + "," + offset + ")");
             }
 
             SearchParameters searchParameters = new SearchParameters();
@@ -184,11 +198,12 @@ public class DiscoveryREST {
 
     /**
      * Retrieve data for the specified attribute search query
-     * @param attrName  Attribute name
+     *
+     * @param attrName        Attribute name
      * @param attrValuePrefix Attibute value to search on
-     * @param typeName limit the result to only entities of specified type or its sub-types
-     * @param limit limit the result set to only include the specified number of entries
-     * @param offset start offset of the result set (useful for pagination)
+     * @param typeName        limit the result to only entities of specified type or its sub-types
+     * @param limit           limit the result set to only include the specified number of entries
+     * @param offset          start offset of the result set (useful for pagination)
      * @return Search results
      * @throws AtlasBaseException
      * @HTTP 200 On successful FullText lookup with some results, might return an empty list if execution succeeded
@@ -226,10 +241,10 @@ public class DiscoveryREST {
 
     /**
      * Attribute based search for entities satisfying the search parameters
+     *
      * @param parameters Search parameters
      * @return Atlas search result
      * @throws AtlasBaseException
-     *
      * @HTTP 200 On successful search
      * @HTTP 400 Tag/Entity doesn't exist or Tag/entity filter is present without tag/type name
      */
@@ -242,7 +257,7 @@ public class DiscoveryREST {
 
         try {
             if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
-                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "DiscoveryREST.searchWithParameters("+ parameters + ")");
+                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "DiscoveryREST.searchWithParameters(" + parameters + ")");
             }
 
             if (parameters.getLimit() < 0 || parameters.getOffset() < 0) {
@@ -269,15 +284,15 @@ public class DiscoveryREST {
 
     /**
      * Relationship search to search for related entities satisfying the search parameters
-     * @param guid  Attribute name
-     * @param relation relationName
+     *
+     * @param guid            Attribute name
+     * @param relation        relationName
      * @param sortByAttribute sort the result using this attribute name, default value is 'name'
-     * @param sortOrder sorting order
-     * @param limit limit the result set to only include the specified number of entries
-     * @param offset start offset of the result set (useful for pagination)
+     * @param sortOrder       sorting order
+     * @param limit           limit the result set to only include the specified number of entries
+     * @param offset          start offset of the result set (useful for pagination)
      * @return Atlas search result
      * @throws AtlasBaseException
-     *
      * @HTTP 200 On successful search
      * @HTTP 400 guid is not a valid entity type or attributeName is not a valid relationship attribute
      */
@@ -285,13 +300,13 @@ public class DiscoveryREST {
     @Path("relationship")
     @Consumes(Servlets.JSON_MEDIA_TYPE)
     @Produces(Servlets.JSON_MEDIA_TYPE)
-    public AtlasSearchResult searchRelatedEntities(@QueryParam("guid")      String       guid,
-                                                   @QueryParam("relation")  String       relation,
-                                                   @QueryParam("sortBy")    String       sortByAttribute,
-                                                   @QueryParam("sortOrder") SortOrder    sortOrder,
-                                                   @QueryParam("excludeDeletedEntities") boolean excludeDeletedEntities,
-                                                   @QueryParam("limit")     int          limit,
-                                                   @QueryParam("offset")    int          offset) throws AtlasBaseException {
+    public AtlasSearchResult searchRelatedEntities(@QueryParam("guid")                   String    guid,
+                                                   @QueryParam("relation")               String    relation,
+                                                   @QueryParam("sortBy")                 String    sortByAttribute,
+                                                   @QueryParam("sortOrder")              SortOrder sortOrder,
+                                                   @QueryParam("excludeDeletedEntities") boolean   excludeDeletedEntities,
+                                                   @QueryParam("limit")                  int       limit,
+                                                   @QueryParam("offset")                 int       offset) throws AtlasBaseException {
         AtlasPerfTracer perf = null;
 
         try {
@@ -308,7 +323,7 @@ public class DiscoveryREST {
 
     private boolean isEmpty(SearchParameters.FilterCriteria filterCriteria) {
         return filterCriteria == null ||
-               (StringUtils.isEmpty(filterCriteria.getAttributeName()) && CollectionUtils.isEmpty(filterCriteria.getCriterion()));
+                (StringUtils.isEmpty(filterCriteria.getAttributeName()) && CollectionUtils.isEmpty(filterCriteria.getCriterion()));
     }
 
     private String escapeTypeName(String typeName) {
@@ -322,4 +337,68 @@ public class DiscoveryREST {
 
         return ret;
     }
-}
\ No newline at end of file
+
+    /**
+     * @param savedSearch
+     * @throws AtlasBaseException
+     * @throws IOException
+     */
+    @POST
+    @Path("saved")
+    @Consumes(Servlets.JSON_MEDIA_TYPE)
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public void createSavedSearch(AtlasUserSavedSearch savedSearch) throws AtlasBaseException, IOException {
+        savedSearch.setOwnerName(Servlets.getUserName(httpServletRequest));
+
+        atlasDiscoveryService.addSavedSearch(savedSearch);
+    }
+
+    /**
+     * @param savedSearch
+     * @throws AtlasBaseException
+     * @throws IOException
+     */
+    @PUT
+    @Path("saved")
+    @Consumes(Servlets.JSON_MEDIA_TYPE)
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public void updateSavedSearch(AtlasUserSavedSearch savedSearch) throws AtlasBaseException {
+        atlasDiscoveryService.updateSavedSearch(savedSearch);
+    }
+
+    /**
+     * @param searchName Name of the saved search
+     * @return
+     */
+    @GET
+    @Path("saved/{name}")
+    @Consumes(Servlets.JSON_MEDIA_TYPE)
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public AtlasUserSavedSearch getSavedSearch(@QueryParam("user") String userName, @PathParam("name") String searchName) throws AtlasBaseException {
+        userName = StringUtils.isBlank(userName) ? Servlets.getUserName(httpServletRequest) : userName;
+        return atlasDiscoveryService.getSavedSearch(userName, searchName);
+    }
+
+    /**
+     * @return list of all saved searches for given user
+     */
+    @GET
+    @Path("saved")
+    @Consumes(Servlets.JSON_MEDIA_TYPE)
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public List<AtlasUserSavedSearch> getSavedSearches(@QueryParam("user") String userName) throws AtlasBaseException {
+        userName = StringUtils.isBlank(userName) ? Servlets.getUserName(httpServletRequest) : userName;
+        return atlasDiscoveryService.getSavedSearches(userName);
+    }
+
+    /**
+     * @param guid Name of the saved search
+     */
+    @DELETE
+    @Path("saved/{guid}")
+    @Consumes(Servlets.JSON_MEDIA_TYPE)
+    @Produces(Servlets.JSON_MEDIA_TYPE)
+    public void deleteSavedSearch(@PathParam("guid") String guid) throws AtlasBaseException {
+        atlasDiscoveryService.deleteSavedSearch(guid);
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java
index 59ea338..12fd2fc 100644
--- a/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java
+++ b/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java
@@ -19,7 +19,15 @@ package org.apache.atlas.web.rest;
 
 import org.apache.atlas.exception.AtlasBaseException;
 import org.apache.atlas.model.SearchFilter;
-import org.apache.atlas.model.typedef.*;
+import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
+import org.apache.atlas.model.typedef.AtlasClassificationDef;
+import org.apache.atlas.model.typedef.AtlasEntityDef;
+import org.apache.atlas.model.typedef.AtlasEnumDef;
+import org.apache.atlas.model.typedef.AtlasRelationshipDef;
+import org.apache.atlas.model.typedef.AtlasStructDef;
+import org.apache.atlas.model.typedef.AtlasTypeDefHeader;
+import org.apache.atlas.model.typedef.AtlasTypesDef;
+import org.apache.atlas.repository.util.FilterUtil;
 import org.apache.atlas.store.AtlasTypeDefStore;
 import org.apache.atlas.type.AtlasTypeUtil;
 import org.apache.atlas.utils.AtlasPerfTracer;
@@ -386,6 +394,7 @@ public class TypesREST {
             ret.setParam(String.valueOf(key), String.valueOf(httpServletRequest.getParameter(key)));
         }
 
+        FilterUtil.addParamsToHideInternalType(ret);
         return ret;
     }
 }

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/webapp/src/main/java/org/apache/atlas/web/util/Servlets.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/web/util/Servlets.java b/webapp/src/main/java/org/apache/atlas/web/util/Servlets.java
index 4a92763..4707035 100755
--- a/webapp/src/main/java/org/apache/atlas/web/util/Servlets.java
+++ b/webapp/src/main/java/org/apache/atlas/web/util/Servlets.java
@@ -184,7 +184,7 @@ public final class Servlets {
         return httpServletRequest.getLocalName();
     }
 
-    public static String getUserName(HttpServletRequest httpServletRequest) throws IOException {
+    public static String getUserName(HttpServletRequest httpServletRequest) {
         return httpServletRequest.getRemoteUser();
     }
 


[2/3] atlas git commit: ATLAS-2100: UserProfile & SavedSearch API implementation

Posted by ma...@apache.org.
ATLAS-2100: UserProfile & SavedSearch API implementation

Signed-off-by: Madhan Neethiraj <ma...@apache.org>
(cherry picked from commit fd144e018f2af046a4b44760f34c7d95b5948ecf)


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

Branch: refs/heads/master
Commit: ccd121e74204e1d85001be62abf09446d879889d
Parents: d67b049
Author: ashutoshm <am...@hortonworks.com>
Authored: Sat Sep 16 13:46:00 2017 -0700
Committer: Madhan Neethiraj <ma...@apache.org>
Committed: Sun Sep 17 14:47:29 2017 -0700

----------------------------------------------------------------------
 addons/models/0000-Area0/0010-base_model.json   |  87 +++++-
 .../org/apache/atlas/repository/Constants.java  |   1 +
 .../java/org/apache/atlas/AtlasErrorCode.java   |   7 +-
 .../atlas/model/AtlasBaseModelObject.java       |  67 +++++
 .../org/apache/atlas/model/SearchFilter.java    |   1 +
 .../atlas/model/discovery/SearchParameters.java |   5 +-
 .../atlas/model/profile/AtlasUserProfile.java   |  96 +++++++
 .../model/profile/AtlasUserSavedSearch.java     |  99 +++++++
 .../atlas/discovery/AtlasDiscoveryService.java  |  40 +++
 .../atlas/discovery/EntityDiscoveryService.java |  91 +++++--
 .../atlas/repository/graph/GraphHelper.java     |   4 +
 .../ogm/AbstractDataTransferObject.java         |  66 +++++
 .../repository/ogm/AtlasSavedSearchDTO.java     |  97 +++++++
 .../repository/ogm/AtlasUserProfileDTO.java     | 115 ++++++++
 .../atlas/repository/ogm/DTORegistry.java       |  49 ++++
 .../apache/atlas/repository/ogm/DataAccess.java |  90 +++++++
 .../repository/ogm/DataTransferObject.java      |  43 +++
 .../store/graph/v1/AtlasAbstractDefStoreV1.java |  11 +-
 .../graph/v1/AtlasEntityChangeNotifier.java     |   4 +
 .../userprofile/UserProfileService.java         | 150 +++++++++++
 .../atlas/repository/util/FilterUtil.java       |  36 ++-
 .../test/java/org/apache/atlas/TestModules.java |  23 +-
 .../impexp/ZipFileResourceTestUtils.java        |  43 ++-
 .../userprofile/UserProfileServiceTest.java     | 267 +++++++++++++++++++
 .../NotificationEntityChangeListener.java       |   5 +
 .../apache/atlas/web/rest/DiscoveryREST.java    | 157 ++++++++---
 .../org/apache/atlas/web/rest/TypesREST.java    |  11 +-
 .../org/apache/atlas/web/util/Servlets.java     |   2 +-
 28 files changed, 1579 insertions(+), 88 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/addons/models/0000-Area0/0010-base_model.json
----------------------------------------------------------------------
diff --git a/addons/models/0000-Area0/0010-base_model.json b/addons/models/0000-Area0/0010-base_model.json
index 5865fb0..7c7e251 100644
--- a/addons/models/0000-Area0/0010-base_model.json
+++ b/addons/models/0000-Area0/0010-base_model.json
@@ -35,6 +35,12 @@
             ]
         },
         {
+            "name": "__internal",
+            "superTypes": [],
+            "typeVersion": "1.0",
+            "attributeDefs": []
+        },
+        {
             "name": "Asset",
             "superTypes": [],
             "typeVersion": "1.0",
@@ -108,6 +114,85 @@
                     "isUnique": false
                 }
             ]
+        },
+        {
+            "name": "__AtlasUserProfile",
+            "superTypes": [
+                "__internal"
+            ],
+            "typeVersion": "1.0",
+            "attributeDefs": [
+                {
+                    "name": "name",
+                    "typeName": "string",
+                    "cardinality": "SINGLE",
+                    "isIndexable": true,
+                    "isOptional": false,
+                    "isUnique": true
+                },
+                {
+                    "name": "fullName",
+                    "typeName": "string",
+                    "cardinality": "SINGLE",
+                    "isIndexable": false,
+                    "isOptional": true,
+                    "isUnique": false
+                },
+                {
+                    "name": "savedSearches",
+                    "typeName": "array<__AtlasUserSavedSearch>",
+                    "cardinality": "LIST",
+                    "isIndexable": false,
+                    "isOptional": true,
+                    "isUnique": false,
+                    "constraints": [
+                        {
+                            "type": "ownedRef"
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "__AtlasUserSavedSearch",
+            "superTypes": [
+                "__internal"
+            ],
+            "typeVersion": "1.0",
+            "attributeDefs": [
+                {
+                    "name": "name",
+                    "typeName": "string",
+                    "cardinality": "SINGLE",
+                    "isIndexable": false,
+                    "isOptional": false,
+                    "isUnique": false
+                },
+                {
+                    "name": "ownerName",
+                    "typeName": "string",
+                    "cardinality": "SINGLE",
+                    "isIndexable": false,
+                    "isOptional": false,
+                    "isUnique": false
+                },
+                {
+                    "name": "uniqueName",
+                    "typeName": "string",
+                    "cardinality": "SINGLE",
+                    "isIndexable": true,
+                    "isOptional": false,
+                    "isUnique": true
+                },
+                {
+                    "name": "searchParameters",
+                    "typeName": "string",
+                    "cardinality": "SINGLE",
+                    "isIndexable": false,
+                    "isOptional": false,
+                    "isUnique": false
+                }
+            ]
         }
     ],
     "relationshipDefs": [
@@ -150,4 +235,4 @@
           "propagateTags": "NONE"
         }
     ]
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/common/src/main/java/org/apache/atlas/repository/Constants.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/atlas/repository/Constants.java b/common/src/main/java/org/apache/atlas/repository/Constants.java
index 71d0d8b..d5283c1 100644
--- a/common/src/main/java/org/apache/atlas/repository/Constants.java
+++ b/common/src/main/java/org/apache/atlas/repository/Constants.java
@@ -35,6 +35,7 @@ public final class Constants {
      * Entity type name property key.
      */
     public static final String ENTITY_TYPE_PROPERTY_KEY = INTERNAL_PROPERTY_KEY_PREFIX + "typeName";
+    public static final String TYPE_NAME_INTERNAL       = INTERNAL_PROPERTY_KEY_PREFIX + "internal";
 
     /**
      * Entity type's super types property key.

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
index 6979040..d205faf 100644
--- a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
+++ b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
@@ -100,6 +100,7 @@ public enum AtlasErrorCode {
     CLASSIFICATIONDEF_PARENTS_ENTITYTYPES_DISJOINT(400, "ATLAS-400-00-053", "ClassificationDef ‘{0}‘ has supertypes whose entityTypes are disjoint; e.g. 2 supertypes that are not related by inheritance specify different non empty entityType lists. This means the child cannot honour the restrictions specified in both parents."),
     CLASSIFICATIONDEF_ENTITYTYPES_NOT_PARENTS_SUBSET(400, "ATLAS-400-00-054", "ClassificationDef ‘{0}‘ has entityTypes ‘{1}‘ which are not subsets of it's supertypes entityTypes"),
     INVALID_ENTITY_FOR_CLASSIFICATION (400, "ATLAS-400-00-055", "Entity (guid=‘{0}‘,typename=‘{1}‘) cannot be classified by Classification ‘{2}‘, because ‘{1}‘ is not in the ClassificationDef's restrictions."),
+    SAVED_SEARCH_CHANGE_USER(400, "ATLAS-400-00-056", "saved-search {0} can not be moved from user {1} to {2}"),
 
     // All Not found enums go here
     TYPE_NAME_NOT_FOUND(404, "ATLAS-404-00-001", "Given typename {0} was invalid"),
@@ -122,6 +123,7 @@ public enum AtlasErrorCode {
     INSTANCE_ALREADY_EXISTS(409, "ATLAS-409-00-003", "failed to update entity: {0}"),
     RELATIONSHIP_ALREADY_EXISTS(409, "ATLAS-409-00-004", "relationship {0} already exists between entities {1} and {2}"),
     TYPE_HAS_RELATIONSHIPS(409, "ATLAS-409-00-005", "Given type {0} has associated relationshipDefs"),
+    SAVED_SEARCH_ALREADY_EXISTS(409, "ATLAS-409-00-006", "search named {0} already exists for user {1}"),
 
     // All internal errors go here
     INTERNAL_ERROR(500, "ATLAS-500-00-001", "Internal server error {0}"),
@@ -141,7 +143,10 @@ public enum AtlasErrorCode {
     STORM_TOPOLOGY_UTIL(500, "ATLAS-500-00-00E", "StormToplogyUtil: {0}"),
     SQOOP_HOOK(500, "ATLAS-500-00-00F", "SqoopHook: {0}"),
     HIVE_HOOK(500, "ATLAS-500-00-010", "HiveHook: {0}"),
-    HIVE_HOOK_METASTORE_BRIDGE(500, "ATLAS-500-00-011", "HiveHookMetaStoreBridge: {0}");
+    HIVE_HOOK_METASTORE_BRIDGE(500, "ATLAS-500-00-011", "HiveHookMetaStoreBridge: {0}"),
+
+    DATA_ACCESS_SAVE_FAILED(500, "ATLAS-500-00-00B", "Save failed: {0}"),
+    DATA_ACCESS_LOAD_FAILED(500, "ATLAS-500-00-00C", "Load failed: {0}");
 
     private String errorCode;
     private String errorMessage;

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/intg/src/main/java/org/apache/atlas/model/AtlasBaseModelObject.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/model/AtlasBaseModelObject.java b/intg/src/main/java/org/apache/atlas/model/AtlasBaseModelObject.java
new file mode 100644
index 0000000..ea6660d
--- /dev/null
+++ b/intg/src/main/java/org/apache/atlas/model/AtlasBaseModelObject.java
@@ -0,0 +1,67 @@
+/**
+ * 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.model;
+
+import org.codehaus.jackson.annotate.JsonAutoDetect;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.Serializable;
+
+import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE;
+import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.PUBLIC_ONLY;
+
+@JsonAutoDetect(getterVisibility=PUBLIC_ONLY, setterVisibility=PUBLIC_ONLY, fieldVisibility=NONE)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown=true)
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.PROPERTY)
+public abstract class AtlasBaseModelObject implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String guid;
+
+
+    protected AtlasBaseModelObject() {
+    }
+
+
+    public String getGuid() {
+        return this.guid;
+    }
+
+    public void setGuid(String guid) {
+        this.guid = guid;
+    }
+
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("{");
+        sb.append("guid=").append(guid);
+        toString(sb);
+        sb.append("}");
+        return sb.toString();
+    }
+
+    protected abstract StringBuilder toString(StringBuilder sb);
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/intg/src/main/java/org/apache/atlas/model/SearchFilter.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/model/SearchFilter.java b/intg/src/main/java/org/apache/atlas/model/SearchFilter.java
index 7dccf5e..172fd26 100644
--- a/intg/src/main/java/org/apache/atlas/model/SearchFilter.java
+++ b/intg/src/main/java/org/apache/atlas/model/SearchFilter.java
@@ -44,6 +44,7 @@ public class SearchFilter {
     public static final String PARAM_NAME = "name";
     public static final String PARAM_SUPERTYPE = "supertype";
     public static final String PARAM_NOT_SUPERTYPE = "notsupertype";
+    public static final String PARAM_NOT_NAME      = "notname";
 
     /**
      * to specify whether the result should be sorted? If yes, whether asc or desc.

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/intg/src/main/java/org/apache/atlas/model/discovery/SearchParameters.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/model/discovery/SearchParameters.java b/intg/src/main/java/org/apache/atlas/model/discovery/SearchParameters.java
index c79b5b9..a7a71b7 100644
--- a/intg/src/main/java/org/apache/atlas/model/discovery/SearchParameters.java
+++ b/intg/src/main/java/org/apache/atlas/model/discovery/SearchParameters.java
@@ -24,6 +24,7 @@ import org.codehaus.jackson.annotate.JsonIgnoreProperties;
 import org.codehaus.jackson.annotate.JsonValue;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
 
+import java.io.Serializable;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -36,7 +37,9 @@ import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.PUBLIC_ONL
 @JsonAutoDetect(getterVisibility = PUBLIC_ONLY, setterVisibility = PUBLIC_ONLY, fieldVisibility = NONE)
 @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
 @JsonIgnoreProperties(ignoreUnknown = true)
-public class SearchParameters {
+public class SearchParameters implements Serializable {
+    private static final long serialVersionUID = 1L;
+
     private String  query;
     private String  typeName;
     private String  classification;

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/intg/src/main/java/org/apache/atlas/model/profile/AtlasUserProfile.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/model/profile/AtlasUserProfile.java b/intg/src/main/java/org/apache/atlas/model/profile/AtlasUserProfile.java
new file mode 100644
index 0000000..3fd61f3
--- /dev/null
+++ b/intg/src/main/java/org/apache/atlas/model/profile/AtlasUserProfile.java
@@ -0,0 +1,96 @@
+/**
+ * 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.model.profile;
+
+import org.apache.atlas.model.AtlasBaseModelObject;
+import org.codehaus.jackson.annotate.JsonAutoDetect;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.atlas.model.typedef.AtlasBaseTypeDef.dumpObjects;
+import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE;
+import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.PUBLIC_ONLY;
+
+@JsonAutoDetect(getterVisibility = PUBLIC_ONLY, setterVisibility = PUBLIC_ONLY, fieldVisibility = NONE)
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AtlasUserProfile extends AtlasBaseModelObject implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String                     name;
+    private String                     fullName;
+    private List<AtlasUserSavedSearch> savedSearches;
+
+
+    public AtlasUserProfile() {
+        this(null, null);
+    }
+
+    public AtlasUserProfile(String name) {
+        this(name, null);
+    }
+
+    public AtlasUserProfile(String name, String fullName) {
+        this.name          = name;
+        this.fullName      = fullName;
+        this.savedSearches = new ArrayList<>();
+    }
+
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public void setFullName(String fullName) {
+        this.fullName = fullName;
+    }
+
+    public String getFullName() {
+        return this.fullName;
+    }
+
+    public void setSavedSearches(List<AtlasUserSavedSearch> searches) {
+        this.savedSearches = searches;
+    }
+
+    public List<AtlasUserSavedSearch> getSavedSearches() {
+        return savedSearches;
+    }
+
+
+    @Override
+    public StringBuilder toString(StringBuilder sb) {
+        sb.append(", name=").append(name);
+        sb.append(", fullName=").append(fullName);
+        sb.append(", savedSearches=[");
+        if (savedSearches != null) {
+            dumpObjects(savedSearches, sb);
+        }
+        sb.append("]");
+
+        return sb;
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/intg/src/main/java/org/apache/atlas/model/profile/AtlasUserSavedSearch.java
----------------------------------------------------------------------
diff --git a/intg/src/main/java/org/apache/atlas/model/profile/AtlasUserSavedSearch.java b/intg/src/main/java/org/apache/atlas/model/profile/AtlasUserSavedSearch.java
new file mode 100644
index 0000000..b0698fc
--- /dev/null
+++ b/intg/src/main/java/org/apache/atlas/model/profile/AtlasUserSavedSearch.java
@@ -0,0 +1,99 @@
+/**
+ * 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.model.profile;
+
+import org.apache.atlas.model.AtlasBaseModelObject;
+import org.apache.atlas.model.discovery.SearchParameters;
+import org.codehaus.jackson.annotate.JsonAutoDetect;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import java.io.Serializable;
+
+import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE;
+import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.PUBLIC_ONLY;
+
+@JsonAutoDetect(getterVisibility = PUBLIC_ONLY, setterVisibility = PUBLIC_ONLY, fieldVisibility = NONE)
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AtlasUserSavedSearch extends AtlasBaseModelObject implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String           ownerName;
+    private String           name;
+    private SearchParameters searchParameters;
+
+
+    public AtlasUserSavedSearch() {
+        this(null, null, null);
+    }
+
+    public AtlasUserSavedSearch(String name, SearchParameters searchParameters) {
+        this(null, name, searchParameters);
+    }
+
+    public AtlasUserSavedSearch(String ownerName, String name) {
+        this(ownerName, name, null);
+    }
+
+    public AtlasUserSavedSearch(String ownerName, String name, SearchParameters searchParameters) {
+        setOwnerName(ownerName);
+        setName(name);
+        setSearchParameters(searchParameters);
+    }
+
+
+    public String getOwnerName() {
+        return this.ownerName;
+    }
+
+    public void setOwnerName(String ownerName) {
+        this.ownerName = ownerName;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public SearchParameters getSearchParameters() {
+        return searchParameters;
+    }
+
+    public void setSearchParameters(SearchParameters searchParameters) {
+        this.searchParameters = searchParameters;
+    }
+
+
+    @Override
+    public StringBuilder toString(StringBuilder sb) {
+        sb.append(", ownerName=").append(ownerName);
+        sb.append(", name=").append(name);
+        sb.append(", searchParameters=");
+        if (searchParameters == null) {
+            sb.append("null");
+        } else {
+            searchParameters.toString(sb);
+        }
+
+        return sb;
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/main/java/org/apache/atlas/discovery/AtlasDiscoveryService.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/discovery/AtlasDiscoveryService.java b/repository/src/main/java/org/apache/atlas/discovery/AtlasDiscoveryService.java
index 8196a67..baaee85 100644
--- a/repository/src/main/java/org/apache/atlas/discovery/AtlasDiscoveryService.java
+++ b/repository/src/main/java/org/apache/atlas/discovery/AtlasDiscoveryService.java
@@ -23,6 +23,9 @@ import org.apache.atlas.SortOrder;
 import org.apache.atlas.exception.AtlasBaseException;
 import org.apache.atlas.model.discovery.AtlasSearchResult;
 import org.apache.atlas.model.discovery.SearchParameters;
+import org.apache.atlas.model.profile.AtlasUserSavedSearch;
+
+import java.util.List;
 
 public interface AtlasDiscoveryService {
     /**
@@ -79,4 +82,41 @@ public interface AtlasDiscoveryService {
      * @return AtlasSearchResult
      */
     AtlasSearchResult searchRelatedEntities(String guid, String relation, String sortByAttribute, SortOrder sortOrder, boolean excludeDeletedEntities, int limit, int offset) throws AtlasBaseException;
+
+    /**
+     *
+     *
+     * @param savedSearch Search to be saved
+     * @throws AtlasBaseException
+     */
+    void addSavedSearch(AtlasUserSavedSearch savedSearch) throws AtlasBaseException;
+
+    /**
+     *
+     * @param savedSearch Search to be saved
+     * @throws AtlasBaseException
+     */
+    void updateSavedSearch(AtlasUserSavedSearch savedSearch) throws AtlasBaseException;
+
+    /**
+     *
+     * @param userName Name of the user for whom the saved searches are to be retrieved
+     * @return List of saved searches for the user
+     * @throws AtlasBaseException
+     */
+    List<AtlasUserSavedSearch> getSavedSearches(String userName) throws AtlasBaseException;
+
+    /**
+     *
+     * @param userName Name of the user who the search belongs
+     * @param searchName Name of the search to be retrieved
+     * @return Search object identified by the name
+     * @throws AtlasBaseException
+     */
+    AtlasUserSavedSearch getSavedSearch(String userName, String searchName) throws AtlasBaseException;
+
+    /**
+     * @param guid Guid for the saved search
+     */
+    void deleteSavedSearch(String guid) throws AtlasBaseException;
 }

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java b/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java
index 1e68835..ad21ee4 100644
--- a/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java
+++ b/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java
@@ -30,9 +30,9 @@ import org.apache.atlas.model.discovery.AtlasSearchResult.AtlasFullTextResult;
 import org.apache.atlas.model.discovery.AtlasSearchResult.AtlasQueryType;
 import org.apache.atlas.model.discovery.AtlasSearchResult.AttributeSearchResult;
 import org.apache.atlas.model.discovery.SearchParameters;
-import org.apache.atlas.model.instance.AtlasEntity.Status;
 import org.apache.atlas.model.instance.AtlasEntityHeader;
 import org.apache.atlas.model.instance.AtlasObjectId;
+import org.apache.atlas.model.profile.AtlasUserSavedSearch;
 import org.apache.atlas.query.Expressions.AliasExpression;
 import org.apache.atlas.query.Expressions.Expression;
 import org.apache.atlas.query.Expressions.SelectExpression;
@@ -51,9 +51,15 @@ import org.apache.atlas.repository.graphdb.AtlasIndexQuery;
 import org.apache.atlas.repository.graphdb.AtlasIndexQuery.Result;
 import org.apache.atlas.repository.graphdb.AtlasVertex;
 import org.apache.atlas.repository.store.graph.v1.EntityGraphRetriever;
-import org.apache.atlas.type.*;
+import org.apache.atlas.repository.userprofile.UserProfileService;
+import org.apache.atlas.type.AtlasArrayType;
 import org.apache.atlas.type.AtlasBuiltInTypes.AtlasObjectIdType;
+import org.apache.atlas.type.AtlasClassificationType;
+import org.apache.atlas.type.AtlasEntityType;
+import org.apache.atlas.type.AtlasMapType;
 import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
+import org.apache.atlas.type.AtlasType;
+import org.apache.atlas.type.AtlasTypeRegistry;
 import org.apache.atlas.util.AtlasGremlinQueryProvider;
 import org.apache.atlas.util.AtlasGremlinQueryProvider.AtlasGremlinQuery;
 import org.apache.atlas.util.SearchTracker;
@@ -71,24 +77,25 @@ import javax.inject.Inject;
 import javax.script.Bindings;
 import javax.script.ScriptEngine;
 import javax.script.ScriptException;
-import java.util.*;
-
-import static org.apache.atlas.AtlasErrorCode.CLASSIFICATION_NOT_FOUND;
-import static org.apache.atlas.AtlasErrorCode.DISCOVERY_QUERY_FAILED;
-import static org.apache.atlas.AtlasErrorCode.UNKNOWN_TYPENAME;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.atlas.AtlasErrorCode.*;
 import static org.apache.atlas.SortOrder.ASCENDING;
 import static org.apache.atlas.SortOrder.DESCENDING;
-import static org.apache.atlas.model.TypeCategory.ARRAY;
-import static org.apache.atlas.model.TypeCategory.MAP;
-import static org.apache.atlas.model.TypeCategory.OBJECT_ID_TYPE;
+import static org.apache.atlas.model.TypeCategory.*;
 import static org.apache.atlas.model.instance.AtlasEntity.Status.ACTIVE;
 import static org.apache.atlas.model.instance.AtlasEntity.Status.DELETED;
 import static org.apache.atlas.repository.graph.GraphHelper.EDGE_LABEL_PREFIX;
-import static org.apache.atlas.util.AtlasGremlinQueryProvider.AtlasGremlinQuery.BASIC_SEARCH_STATE_FILTER;
-import static org.apache.atlas.util.AtlasGremlinQueryProvider.AtlasGremlinQuery.RELATIONSHIP_SEARCH;
-import static org.apache.atlas.util.AtlasGremlinQueryProvider.AtlasGremlinQuery.RELATIONSHIP_SEARCH_DESCENDING_SORT;
-import static org.apache.atlas.util.AtlasGremlinQueryProvider.AtlasGremlinQuery.RELATIONSHIP_SEARCH_ASCENDING_SORT;
-import static org.apache.atlas.util.AtlasGremlinQueryProvider.AtlasGremlinQuery.TO_RANGE_LIST;
+import static org.apache.atlas.util.AtlasGremlinQueryProvider.AtlasGremlinQuery.*;
 
 @Component
 public class EntityDiscoveryService implements AtlasDiscoveryService {
@@ -105,10 +112,12 @@ public class EntityDiscoveryService implements AtlasDiscoveryService {
     private final int                             maxResultSetSize;
     private final int                             maxTypesLengthInIdxQuery;
     private final int                             maxTagsLengthInIdxQuery;
+    private final UserProfileService              userProfileService;
 
     @Inject
     EntityDiscoveryService(MetadataRepository metadataRepository, AtlasTypeRegistry typeRegistry,
-                           AtlasGraph graph, GraphBackedSearchIndexer indexer, SearchTracker searchTracker) throws AtlasException {
+                           AtlasGraph graph, GraphBackedSearchIndexer indexer, SearchTracker searchTracker,
+                           UserProfileService userProfileService) throws AtlasException {
         this.graph                    = graph;
         this.graphPersistenceStrategy = new DefaultGraphPersistenceStrategy(metadataRepository);
         this.entityRetriever          = new EntityGraphRetriever(typeRegistry);
@@ -119,6 +128,7 @@ public class EntityDiscoveryService implements AtlasDiscoveryService {
         this.maxResultSetSize         = ApplicationProperties.get().getInt(Constants.INDEX_SEARCH_MAX_RESULT_SET_SIZE, 150);
         this.maxTypesLengthInIdxQuery = ApplicationProperties.get().getInt(Constants.INDEX_SEARCH_TYPES_MAX_QUERY_STR_LENGTH, 512);
         this.maxTagsLengthInIdxQuery  = ApplicationProperties.get().getInt(Constants.INDEX_SEARCH_TAGS_MAX_QUERY_STR_LENGTH, 512);
+        this.userProfileService       = userProfileService;
     }
 
     @Override
@@ -791,4 +801,51 @@ public class EntityDiscoveryService implements AtlasDiscoveryService {
     private Set<String> getEntityStates() {
         return new HashSet<>(Arrays.asList(ACTIVE.toString(), DELETED.toString()));
     }
-}
\ No newline at end of file
+
+
+    @Override
+    public void addSavedSearch(AtlasUserSavedSearch savedSearch) throws AtlasBaseException {
+        try {
+            userProfileService.addSavedSearch(savedSearch);
+        } catch (AtlasBaseException e) {
+            LOG.error("addSavedSearch({})", savedSearch, e);
+            throw e;
+        }
+    }
+
+
+    @Override
+    public void updateSavedSearch(AtlasUserSavedSearch savedSearch) throws AtlasBaseException {
+        try {
+            userProfileService.updateSavedSearch(savedSearch);
+        } catch (AtlasBaseException e) {
+            LOG.error("updateSavedSearch({})", savedSearch, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public List<AtlasUserSavedSearch> getSavedSearches(String userName) throws AtlasBaseException {
+        try {
+            return userProfileService.getSavedSearches(userName);
+        } catch (AtlasBaseException e) {
+            LOG.error("getSavedSearches({})", userName, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public AtlasUserSavedSearch getSavedSearch(String userName, String searchName) throws AtlasBaseException {
+        try {
+            return userProfileService.getSavedSearch(userName, searchName);
+        } catch (AtlasBaseException e) {
+            LOG.error("getSavedSearch({}, {})", userName, searchName, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public void deleteSavedSearch(String guid) throws AtlasBaseException {
+        userProfileService.deleteSavedSearch(guid);
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java b/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java
index 020dd45..4cb430d 100755
--- a/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java
+++ b/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java
@@ -1079,6 +1079,10 @@ public final class GraphHelper {
 
     }
 
+    public static boolean isInternalType(String typeName) {
+        return typeName.startsWith(Constants.INTERNAL_PROPERTY_KEY_PREFIX);
+    }
+
     public static void setArrayElementsProperty(IDataType elementType, AtlasVertex instanceVertex, String propertyName, List<Object> values) {
         String actualPropertyName = GraphHelper.encodePropertyKey(propertyName);
         if(GraphHelper.isReference(elementType)) {

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/main/java/org/apache/atlas/repository/ogm/AbstractDataTransferObject.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/repository/ogm/AbstractDataTransferObject.java b/repository/src/main/java/org/apache/atlas/repository/ogm/AbstractDataTransferObject.java
new file mode 100644
index 0000000..f1a8bc9
--- /dev/null
+++ b/repository/src/main/java/org/apache/atlas/repository/ogm/AbstractDataTransferObject.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.repository.ogm;
+
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.AtlasBaseModelObject;
+import org.apache.atlas.model.instance.AtlasEntity;
+import org.apache.atlas.repository.Constants;
+import org.apache.atlas.type.AtlasEntityType;
+import org.apache.atlas.type.AtlasTypeRegistry;
+import org.apache.commons.lang3.StringUtils;
+
+
+public abstract class AbstractDataTransferObject<T extends AtlasBaseModelObject> implements DataTransferObject<T> {
+    private final AtlasTypeRegistry typeRegistry;
+    private final Class<T>          objectType;
+    private final String            entityTypeName;
+
+    protected AbstractDataTransferObject(AtlasTypeRegistry typeRegistry, Class<T> tClass) {
+        this.typeRegistry   = typeRegistry;
+        this.objectType     = tClass;
+        this.entityTypeName = Constants.INTERNAL_PROPERTY_KEY_PREFIX + objectType.getSimpleName();
+    }
+
+    @Override
+    public Class getObjectType() {
+        return objectType;
+    }
+
+    @Override
+    public AtlasEntityType getEntityType() {
+        return typeRegistry.getEntityTypeByName(entityTypeName);
+    }
+
+
+    protected AtlasEntity getDefaultAtlasEntity(T obj) throws AtlasBaseException {
+        AtlasEntity ret = getEntityType().createDefaultValue();
+
+        if (obj != null) {
+            if (StringUtils.isNotEmpty(obj.getGuid())) {
+                ret.setGuid(obj.getGuid());
+            }
+        }
+
+        return ret;
+    }
+
+    public void setGuid(T o, AtlasEntity entity) {
+        o.setGuid(entity.getGuid());
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasSavedSearchDTO.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasSavedSearchDTO.java b/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasSavedSearchDTO.java
new file mode 100644
index 0000000..26eee20
--- /dev/null
+++ b/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasSavedSearchDTO.java
@@ -0,0 +1,97 @@
+/**
+ * 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.repository.ogm;
+
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.discovery.SearchParameters;
+import org.apache.atlas.model.instance.AtlasEntity;
+import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo;
+import org.apache.atlas.model.profile.AtlasUserSavedSearch;
+import org.apache.atlas.type.AtlasType;
+import org.apache.atlas.type.AtlasTypeRegistry;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class AtlasSavedSearchDTO extends AbstractDataTransferObject<AtlasUserSavedSearch> {
+    private static final String PROPERTY_NAME              = "name";
+    private static final String PROPERTY_OWNER_NAME        = "ownerName";
+    private static final String PROPERTY_SEARCH_PARAMETERS = "searchParameters";
+    private static final String PROPERTY_UNIQUE_NAME       = "uniqueName";
+
+    public AtlasSavedSearchDTO(AtlasTypeRegistry typeRegistry) {
+        super(typeRegistry, AtlasUserSavedSearch.class);
+    }
+
+    @Override
+    public AtlasUserSavedSearch from(AtlasEntity entity) {
+        AtlasUserSavedSearch savedSearch = new AtlasUserSavedSearch();
+
+        savedSearch.setGuid(entity.getGuid());
+        savedSearch.setName((String) entity.getAttribute(PROPERTY_NAME));
+        savedSearch.setOwnerName((String) entity.getAttribute(PROPERTY_OWNER_NAME));
+
+        String jsonSearchParams = (String) entity.getAttribute(PROPERTY_SEARCH_PARAMETERS);
+
+        if (StringUtils.isNotEmpty(jsonSearchParams)) {
+            savedSearch.setSearchParameters(AtlasType.fromJson(jsonSearchParams, SearchParameters.class));
+        }
+
+        return savedSearch;
+    }
+
+    @Override
+    public AtlasUserSavedSearch from(AtlasEntityWithExtInfo entityWithExtInfo) {
+        return from(entityWithExtInfo.getEntity());
+    }
+
+    @Override
+    public AtlasEntity toEntity(AtlasUserSavedSearch obj) throws AtlasBaseException {
+        AtlasEntity entity = getDefaultAtlasEntity(obj);
+
+        entity.setAttribute(PROPERTY_NAME, obj.getName());
+        entity.setAttribute(PROPERTY_OWNER_NAME, obj.getOwnerName());
+        entity.setAttribute(PROPERTY_UNIQUE_NAME, getUniqueValue(obj));
+
+        if (obj.getSearchParameters() != null) {
+            entity.setAttribute(PROPERTY_SEARCH_PARAMETERS, AtlasType.toJson(obj.getSearchParameters()));
+        }
+
+        return entity;
+    }
+
+    @Override
+    public AtlasEntityWithExtInfo toEntityWithExtInfo(AtlasUserSavedSearch obj) throws AtlasBaseException {
+        return new AtlasEntityWithExtInfo(toEntity(obj));
+    }
+
+    @Override
+    public Map<String, Object> getUniqueAttributes(AtlasUserSavedSearch obj) {
+        Map<String, Object> ret = new HashMap<>();
+
+        ret.put(PROPERTY_UNIQUE_NAME, getUniqueValue(obj));
+
+        return ret;
+    }
+
+    private String getUniqueValue(AtlasUserSavedSearch obj) {
+        return obj.getOwnerName() + ":" + obj.getName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasUserProfileDTO.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasUserProfileDTO.java b/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasUserProfileDTO.java
new file mode 100644
index 0000000..bcf2b9d
--- /dev/null
+++ b/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasUserProfileDTO.java
@@ -0,0 +1,115 @@
+/**
+ * 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.repository.ogm;
+
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.instance.AtlasEntity;
+import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo;
+import org.apache.atlas.model.instance.AtlasObjectId;
+import org.apache.atlas.model.profile.AtlasUserProfile;
+import org.apache.atlas.model.profile.AtlasUserSavedSearch;
+import org.apache.atlas.type.AtlasTypeRegistry;
+
+import java.util.*;
+
+public class AtlasUserProfileDTO extends AbstractDataTransferObject<AtlasUserProfile> {
+    private final String PROPERTY_USER_NAME      = "name";
+    private final String PROPERTY_FULL_NAME      = "fullName";
+    private final String PROPERTY_SAVED_SEARCHES = "savedSearches";
+
+    private final AtlasSavedSearchDTO savedSearchDTO;
+
+    public AtlasUserProfileDTO(AtlasTypeRegistry typeRegistry, AtlasSavedSearchDTO savedSearchDTO) {
+        super(typeRegistry, AtlasUserProfile.class);
+
+        this.savedSearchDTO = savedSearchDTO;
+    }
+
+    public AtlasUserProfile from(AtlasEntity entity) {
+        AtlasUserProfile profile = new AtlasUserProfile();
+
+        profile.setGuid(entity.getGuid());
+        profile.setName((String) entity.getAttribute(PROPERTY_USER_NAME));
+        profile.setFullName((String) entity.getAttribute(PROPERTY_FULL_NAME));
+
+        return profile;
+    }
+
+    public AtlasUserProfile from(AtlasEntityWithExtInfo entityWithExtInfo) {
+        AtlasUserProfile userProfile = from(entityWithExtInfo.getEntity());
+
+        Object savedSearches = entityWithExtInfo.getEntity().getAttribute(PROPERTY_SAVED_SEARCHES);
+
+        if (savedSearches instanceof Collection) {
+            for (Object o : (Collection) savedSearches) {
+                if (o instanceof AtlasObjectId) {
+                    AtlasObjectId ssObjId  = (AtlasObjectId) o;
+                    AtlasEntity   ssEntity = entityWithExtInfo.getReferredEntity(ssObjId.getGuid());
+
+                    if (ssEntity != null && ssEntity.getStatus() == AtlasEntity.Status.ACTIVE) {
+                        AtlasUserSavedSearch savedSearch = savedSearchDTO.from(ssEntity);
+                        userProfile.getSavedSearches().add(savedSearch);
+                    }
+                }
+            }
+        }
+
+        return userProfile;
+    }
+
+    @Override
+    public AtlasEntity toEntity(AtlasUserProfile obj) throws AtlasBaseException {
+        AtlasEntity entity = getDefaultAtlasEntity(obj);
+
+        entity.setAttribute(PROPERTY_USER_NAME, obj.getName());
+        entity.setAttribute(PROPERTY_FULL_NAME, obj.getFullName());
+
+        return entity;
+    }
+
+    @Override
+    public AtlasEntityWithExtInfo toEntityWithExtInfo(AtlasUserProfile obj) throws AtlasBaseException {
+        AtlasEntity            entity            = toEntity(obj);
+        AtlasEntityWithExtInfo entityWithExtInfo = new AtlasEntityWithExtInfo(entity);
+
+        List<AtlasObjectId> objectIds = new ArrayList<>();
+
+        for (AtlasUserSavedSearch ss : obj.getSavedSearches()) {
+            AtlasEntity ssEntity = savedSearchDTO.toEntity(ss);
+
+            entityWithExtInfo.addReferredEntity(ssEntity);
+
+            objectIds.add(new AtlasObjectId(ssEntity.getGuid(), savedSearchDTO.getEntityType().getTypeName(), savedSearchDTO.getUniqueAttributes(ss)));
+        }
+
+        if (objectIds.size() > 0) {
+            entity.setAttribute(PROPERTY_SAVED_SEARCHES, objectIds);
+        }
+
+        return entityWithExtInfo;
+    }
+
+    @Override
+    public Map<String, Object> getUniqueAttributes(AtlasUserProfile obj) {
+        Map<String, Object> ret = new HashMap<>();
+
+        ret.put(PROPERTY_USER_NAME, obj.getName());
+
+        return ret;
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/main/java/org/apache/atlas/repository/ogm/DTORegistry.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/repository/ogm/DTORegistry.java b/repository/src/main/java/org/apache/atlas/repository/ogm/DTORegistry.java
new file mode 100644
index 0000000..818960d
--- /dev/null
+++ b/repository/src/main/java/org/apache/atlas/repository/ogm/DTORegistry.java
@@ -0,0 +1,49 @@
+/**
+ * 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.atlas.repository.ogm;
+
+import org.apache.atlas.type.AtlasTypeRegistry;
+import org.springframework.stereotype.Component;
+
+import javax.inject.Inject;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+public class DTORegistry {
+    private final Map<Type, DataTransferObject> typeDTOMap = new HashMap<>();
+
+
+    @Inject
+    public DTORegistry(AtlasTypeRegistry typeRegistry) {
+        AtlasSavedSearchDTO savedSearchDTO = new AtlasSavedSearchDTO(typeRegistry);
+        AtlasUserProfileDTO userProfileDTO = new AtlasUserProfileDTO(typeRegistry, savedSearchDTO);
+
+        registerDTO(savedSearchDTO);
+        registerDTO(userProfileDTO);
+    }
+
+    public <T extends DataTransferObject> DataTransferObject get(Type t) {
+        return typeDTOMap.get(t);
+    }
+
+    private void registerDTO(DataTransferObject dto) {
+        typeDTOMap.put(dto.getObjectType(), dto);
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/main/java/org/apache/atlas/repository/ogm/DataAccess.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/repository/ogm/DataAccess.java b/repository/src/main/java/org/apache/atlas/repository/ogm/DataAccess.java
new file mode 100644
index 0000000..bc93cc6
--- /dev/null
+++ b/repository/src/main/java/org/apache/atlas/repository/ogm/DataAccess.java
@@ -0,0 +1,90 @@
+/**
+ * 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.repository.ogm;
+
+import org.apache.atlas.AtlasErrorCode;
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.AtlasBaseModelObject;
+import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo;
+import org.apache.atlas.model.instance.EntityMutationResponse;
+import org.apache.atlas.repository.store.graph.AtlasEntityStore;
+import org.apache.atlas.repository.store.graph.v1.AtlasEntityStream;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import javax.inject.Inject;
+
+
+@Component
+public class DataAccess {
+    private final AtlasEntityStore entityStore;
+    private final DTORegistry      dtoRegistry;
+
+    @Inject
+    public DataAccess(AtlasEntityStore entityStore, DTORegistry dtoRegistry) {
+        this.entityStore = entityStore;
+        this.dtoRegistry = dtoRegistry;
+    }
+
+    public <T extends AtlasBaseModelObject> T save(T obj) throws AtlasBaseException {
+        DataTransferObject<T> dto = (DataTransferObject<T>)dtoRegistry.get(obj.getClass());
+
+        AtlasEntityWithExtInfo entityWithExtInfo      = dto.toEntityWithExtInfo(obj);
+        EntityMutationResponse entityMutationResponse = entityStore.createOrUpdate(new AtlasEntityStream(entityWithExtInfo), false);
+
+        if (hasError(entityMutationResponse)) {
+            throw new AtlasBaseException(AtlasErrorCode.DATA_ACCESS_SAVE_FAILED, obj.toString());
+        }
+
+        return obj;
+    }
+
+    public <T extends AtlasBaseModelObject> T load(T obj) throws AtlasBaseException {
+        DataTransferObject<T>  dto = (DataTransferObject<T>)dtoRegistry.get(obj.getClass());
+
+        AtlasEntityWithExtInfo entityWithExtInfo;
+
+        if (StringUtils.isNotEmpty(obj.getGuid())) {
+            entityWithExtInfo = entityStore.getById(obj.getGuid());
+        } else {
+            entityWithExtInfo = entityStore.getByUniqueAttributes(dto.getEntityType(), dto.getUniqueAttributes(obj));
+        }
+
+        return dto.from(entityWithExtInfo);
+    }
+
+    public void deleteUsingGuid(String guid) throws AtlasBaseException {
+        entityStore.deleteById(guid);
+    }
+
+    public <T extends AtlasBaseModelObject> void delete(T obj) throws AtlasBaseException {
+        T object = load(obj);
+
+        if (object != null) {
+            deleteUsingGuid(object.getGuid());
+        }
+    }
+
+    private boolean hasError(EntityMutationResponse er) {
+        return (er == null ||
+                !((er.getCreatedEntities() != null && er.getCreatedEntities().size() > 0)
+                        || (er.getUpdatedEntities() != null && er.getUpdatedEntities().size() > 0)
+                )
+        );
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/main/java/org/apache/atlas/repository/ogm/DataTransferObject.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/repository/ogm/DataTransferObject.java b/repository/src/main/java/org/apache/atlas/repository/ogm/DataTransferObject.java
new file mode 100644
index 0000000..752df7d
--- /dev/null
+++ b/repository/src/main/java/org/apache/atlas/repository/ogm/DataTransferObject.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.repository.ogm;
+
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.AtlasBaseModelObject;
+import org.apache.atlas.model.instance.AtlasEntity;
+import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo;
+import org.apache.atlas.type.AtlasEntityType;
+
+import java.util.Map;
+
+
+public interface DataTransferObject<T extends AtlasBaseModelObject> {
+    Class getObjectType();
+
+    AtlasEntityType getEntityType();
+
+    T from(AtlasEntity entity);
+
+    T from(AtlasEntityWithExtInfo entityWithExtInfo);
+
+    AtlasEntity toEntity(T obj) throws AtlasBaseException;
+
+    AtlasEntityWithExtInfo toEntityWithExtInfo(T obj) throws AtlasBaseException;
+
+    Map<String, Object> getUniqueAttributes(T obj);
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasAbstractDefStoreV1.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasAbstractDefStoreV1.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasAbstractDefStoreV1.java
index 2bf53a1..02dad46 100644
--- a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasAbstractDefStoreV1.java
+++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasAbstractDefStoreV1.java
@@ -31,7 +31,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.List;
-import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /**
@@ -42,8 +41,10 @@ import java.util.regex.Pattern;
     protected final AtlasTypeDefGraphStoreV1 typeDefStore;
     protected final AtlasTypeRegistry        typeRegistry;
 
-    private static final String  NAME_REGEX         = "[a-zA-Z][a-zA-Z0-9_ ]*";
-    private static final Pattern NAME_PATTERN       = Pattern.compile(NAME_REGEX);
+    private static final String  NAME_REGEX            = "[a-zA-Z][a-zA-Z0-9_ ]*";
+    private static final String  INTERNAL_NAME_REGEX   = "__" + NAME_REGEX;
+    private static final Pattern NAME_PATTERN          = Pattern.compile(NAME_REGEX);
+    private static final Pattern INTERNAL_NAME_PATTERN = Pattern.compile(INTERNAL_NAME_REGEX);
 
     public static final String ALLOW_RESERVED_KEYWORDS = "atlas.types.allowReservedKeywords";
 
@@ -75,9 +76,7 @@ import java.util.regex.Pattern;
     }
 
     public boolean isValidName(String typeName) {
-        Matcher m = NAME_PATTERN.matcher(typeName);
-
-        return m.matches();
+        return NAME_PATTERN.matcher(typeName).matches() || INTERNAL_NAME_PATTERN.matcher(typeName).matches();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasEntityChangeNotifier.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasEntityChangeNotifier.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasEntityChangeNotifier.java
index 6423aea..db47627 100644
--- a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasEntityChangeNotifier.java
+++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasEntityChangeNotifier.java
@@ -227,6 +227,10 @@ public class AtlasEntityChangeNotifier {
         }
 
         for (AtlasEntityHeader atlasEntityHeader : atlasEntityHeaders) {
+            if(GraphHelper.isInternalType(atlasEntityHeader.getTypeName())) {
+                continue;
+            }
+
             String      guid        = atlasEntityHeader.getGuid();
             AtlasVertex atlasVertex = AtlasGraphUtilsV1.findByGuid(guid);
 

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/main/java/org/apache/atlas/repository/userprofile/UserProfileService.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/repository/userprofile/UserProfileService.java b/repository/src/main/java/org/apache/atlas/repository/userprofile/UserProfileService.java
new file mode 100644
index 0000000..766d1c7
--- /dev/null
+++ b/repository/src/main/java/org/apache/atlas/repository/userprofile/UserProfileService.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.repository.userprofile;
+
+import org.apache.atlas.AtlasErrorCode;
+import org.apache.atlas.annotation.AtlasService;
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.profile.AtlasUserProfile;
+import org.apache.atlas.model.profile.AtlasUserSavedSearch;
+import org.apache.atlas.repository.ogm.DataAccess;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import java.util.List;
+
+@AtlasService
+public class UserProfileService {
+    private static final Logger LOG = LoggerFactory.getLogger(UserProfileService.class);
+
+    private final DataAccess dataAccess;
+
+    @Inject
+    public UserProfileService(DataAccess dataAccess) {
+        this.dataAccess = dataAccess;
+    }
+
+    public void saveUserProfile(AtlasUserProfile profile) throws AtlasBaseException {
+        dataAccess.save(profile);
+    }
+
+    public AtlasUserProfile getUserProfile(String userName) throws AtlasBaseException {
+        AtlasUserProfile profile = new AtlasUserProfile(userName);
+
+        return dataAccess.load(profile);
+    }
+
+    public AtlasUserProfile addSavedSearch(AtlasUserSavedSearch savedSearch) throws AtlasBaseException {
+        String userName = savedSearch.getOwnerName();
+        AtlasUserProfile userProfile = null;
+
+        try {
+            userProfile = getUserProfile(userName);
+        } catch (AtlasBaseException excp) {
+            // ignore
+        }
+
+        if (userProfile == null) {
+            userProfile = new AtlasUserProfile(userName);
+        }
+
+        checkIfQueryAlreadyExists(savedSearch, userProfile);
+        userProfile.getSavedSearches().add(savedSearch);
+        return dataAccess.save(userProfile);
+    }
+
+    private void checkIfQueryAlreadyExists(AtlasUserSavedSearch savedSearch, AtlasUserProfile userProfile) throws AtlasBaseException {
+        for (AtlasUserSavedSearch exisingSearch : userProfile.getSavedSearches()) {
+            if (StringUtils.equals(exisingSearch.getOwnerName(), savedSearch.getOwnerName()) &&
+                    StringUtils.equals(exisingSearch.getName(), savedSearch.getName())) {
+                throw new AtlasBaseException(AtlasErrorCode.SAVED_SEARCH_ALREADY_EXISTS, savedSearch.getName(), savedSearch.getOwnerName());
+            }
+        }
+    }
+
+    public AtlasUserSavedSearch updateSavedSearch(AtlasUserSavedSearch savedSearch) throws AtlasBaseException {
+        if (StringUtils.isBlank(savedSearch.getGuid())) {
+            throw new AtlasBaseException(AtlasErrorCode.INVALID_OBJECT_ID, savedSearch.getGuid());
+        }
+
+        AtlasUserSavedSearch existingSearch = getSavedSearch(savedSearch.getGuid());
+
+        // ownerName change is not allowed
+        if (!StringUtils.equals(existingSearch.getOwnerName(), savedSearch.getOwnerName())) {
+            throw new AtlasBaseException(AtlasErrorCode.SAVED_SEARCH_CHANGE_USER, existingSearch.getName(), existingSearch.getOwnerName(), savedSearch.getOwnerName());
+        }
+
+        // if renamed, ensure that there no other search with the new name exists
+        if (!StringUtils.equals(existingSearch.getName(), savedSearch.getName())) {
+            AtlasUserProfile userProfile = getUserProfile(existingSearch.getOwnerName());
+
+            // check if a another search with this name already exists
+            for (AtlasUserSavedSearch search : userProfile.getSavedSearches()) {
+                if (StringUtils.equals(search.getName(), savedSearch.getName())) {
+                    if (!StringUtils.equals(search.getGuid(), savedSearch.getGuid())) {
+                        throw new AtlasBaseException(AtlasErrorCode.SAVED_SEARCH_ALREADY_EXISTS, savedSearch.getName(), savedSearch.getOwnerName());
+                    }
+                }
+            }
+        }
+
+        return dataAccess.save(savedSearch);
+    }
+
+    public List<AtlasUserSavedSearch> getSavedSearches(String userName) throws AtlasBaseException {
+        AtlasUserProfile profile = null;
+
+        try {
+            profile = getUserProfile(userName);
+        } catch (AtlasBaseException excp) {
+            // ignore
+        }
+
+        return (profile != null) ? profile.getSavedSearches() : null;
+    }
+
+    public AtlasUserSavedSearch getSavedSearch(String userName, String searchName) throws AtlasBaseException {
+        AtlasUserSavedSearch ss = new AtlasUserSavedSearch(userName, searchName);
+
+        return dataAccess.load(ss);
+    }
+
+    public AtlasUserSavedSearch getSavedSearch(String guid) throws AtlasBaseException {
+        AtlasUserSavedSearch ss = new AtlasUserSavedSearch();
+
+        ss.setGuid(guid);
+
+        return dataAccess.load(ss);
+    }
+
+    public void deleteUserProfile(String userName) throws AtlasBaseException {
+        AtlasUserProfile profile = getUserProfile(userName);
+
+        dataAccess.deleteUsingGuid(profile.getGuid());
+    }
+
+    public void deleteSavedSearch(String guid) throws AtlasBaseException {
+        dataAccess.deleteUsingGuid(guid);
+    }
+
+    public void deleteSearchBySearchName(String userName, String searchName) throws AtlasBaseException {
+        dataAccess.delete(new AtlasUserSavedSearch(userName, searchName));
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/main/java/org/apache/atlas/repository/util/FilterUtil.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/repository/util/FilterUtil.java b/repository/src/main/java/org/apache/atlas/repository/util/FilterUtil.java
index 44d6051..7057ace 100644
--- a/repository/src/main/java/org/apache/atlas/repository/util/FilterUtil.java
+++ b/repository/src/main/java/org/apache/atlas/repository/util/FilterUtil.java
@@ -6,9 +6,9 @@
  * 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.
@@ -19,12 +19,11 @@ package org.apache.atlas.repository.util;
 
 import org.apache.atlas.model.SearchFilter;
 import org.apache.atlas.model.TypeCategory;
-import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
-import org.apache.atlas.model.typedef.AtlasClassificationDef;
-import org.apache.atlas.model.typedef.AtlasEntityDef;
+import org.apache.atlas.repository.Constants;
 import org.apache.atlas.type.AtlasClassificationType;
 import org.apache.atlas.type.AtlasEntityType;
 import org.apache.atlas.type.AtlasType;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.Predicate;
 import org.apache.commons.collections.PredicateUtils;
 import org.apache.commons.collections.functors.NotPredicate;
@@ -38,10 +37,11 @@ public class FilterUtil {
     public static Predicate getPredicateFromSearchFilter(SearchFilter searchFilter) {
         List<Predicate> predicates = new ArrayList<>();
 
-        final String type         = searchFilter.getParam(SearchFilter.PARAM_TYPE);
-        final String name         = searchFilter.getParam(SearchFilter.PARAM_NAME);
-        final String supertype    = searchFilter.getParam(SearchFilter.PARAM_SUPERTYPE);
-        final String notSupertype = searchFilter.getParam(SearchFilter.PARAM_NOT_SUPERTYPE);
+        final String       type         = searchFilter.getParam(SearchFilter.PARAM_TYPE);
+        final String       name         = searchFilter.getParam(SearchFilter.PARAM_NAME);
+        final String       supertype    = searchFilter.getParam(SearchFilter.PARAM_SUPERTYPE);
+        final String       notSupertype = searchFilter.getParam(SearchFilter.PARAM_NOT_SUPERTYPE);
+        final List<String> notNames     = searchFilter.getParams(SearchFilter.PARAM_NOT_NAME);
 
         // Add filter for the type/category
         if (StringUtils.isNotBlank(type)) {
@@ -63,6 +63,13 @@ public class FilterUtil {
             predicates.add(new NotPredicate(getSuperTypePredicate(notSupertype)));
         }
 
+        // Add filter for the type negation
+        if (CollectionUtils.isNotEmpty(notNames)) {
+            for (String notName : notNames) {
+                predicates.add(new NotPredicate(getNamePredicate(notName)));
+            }
+        }
+
         return PredicateUtils.allPredicate(predicates);
     }
 
@@ -91,8 +98,8 @@ public class FilterUtil {
 
             @Override
             public boolean evaluate(Object o) {
-                return (isClassificationType(o) && ((AtlasClassificationType) o).getAllSuperTypes().contains(supertype))||
-                       (isEntityType(o) && ((AtlasEntityType)o).getAllSuperTypes().contains(supertype));
+                return (isClassificationType(o) && ((AtlasClassificationType) o).getAllSuperTypes().contains(supertype)) ||
+                       (isEntityType(o) && ((AtlasEntityType) o).getAllSuperTypes().contains(supertype));
             }
         };
     }
@@ -102,7 +109,7 @@ public class FilterUtil {
             @Override
             public boolean evaluate(Object o) {
                 if (o instanceof AtlasType) {
-                    AtlasType atlasType = (AtlasType)o;
+                    AtlasType atlasType = (AtlasType) o;
 
                     switch (type.toUpperCase()) {
                         case "CLASS":
@@ -126,4 +133,9 @@ public class FilterUtil {
             }
         };
     }
+
+    public static void addParamsToHideInternalType(SearchFilter searchFilter) {
+        searchFilter.setParam(SearchFilter.PARAM_NOT_NAME, Constants.TYPE_NAME_INTERNAL);
+        searchFilter.setParam(SearchFilter.PARAM_NOT_SUPERTYPE, Constants.TYPE_NAME_INTERNAL);
+    }
 }

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/test/java/org/apache/atlas/TestModules.java
----------------------------------------------------------------------
diff --git a/repository/src/test/java/org/apache/atlas/TestModules.java b/repository/src/test/java/org/apache/atlas/TestModules.java
index e107556..144f618 100644
--- a/repository/src/test/java/org/apache/atlas/TestModules.java
+++ b/repository/src/test/java/org/apache/atlas/TestModules.java
@@ -24,7 +24,13 @@ import com.google.inject.Singleton;
 import com.google.inject.matcher.Matchers;
 import com.google.inject.multibindings.Multibinder;
 import org.apache.atlas.annotation.GraphTransaction;
-import org.apache.atlas.discovery.*;
+import org.apache.atlas.discovery.AtlasDiscoveryService;
+import org.apache.atlas.discovery.AtlasLineageService;
+import org.apache.atlas.discovery.DataSetLineageService;
+import org.apache.atlas.discovery.DiscoveryService;
+import org.apache.atlas.discovery.EntityDiscoveryService;
+import org.apache.atlas.discovery.EntityLineageService;
+import org.apache.atlas.discovery.LineageService;
 import org.apache.atlas.discovery.graph.GraphBackedDiscoveryService;
 import org.apache.atlas.graph.GraphSandboxUtil;
 import org.apache.atlas.listener.EntityChangeListener;
@@ -40,8 +46,18 @@ import org.apache.atlas.repository.graph.HardDeleteHandler;
 import org.apache.atlas.repository.graph.SoftDeleteHandler;
 import org.apache.atlas.repository.graphdb.AtlasGraph;
 import org.apache.atlas.repository.impexp.ExportService;
-import org.apache.atlas.repository.store.graph.*;
-import org.apache.atlas.repository.store.graph.v1.*;
+import org.apache.atlas.repository.store.graph.AtlasEntityStore;
+import org.apache.atlas.repository.store.graph.AtlasRelationshipStore;
+import org.apache.atlas.repository.store.graph.BulkImporter;
+import org.apache.atlas.repository.store.graph.v1.AtlasEntityChangeNotifier;
+import org.apache.atlas.repository.store.graph.v1.AtlasEntityStoreV1;
+import org.apache.atlas.repository.store.graph.v1.AtlasTypeDefGraphStoreV1;
+import org.apache.atlas.repository.store.graph.v1.AtlasRelationshipStoreV1;
+import org.apache.atlas.repository.store.graph.v1.BulkImporterImpl;
+import org.apache.atlas.repository.store.graph.v1.DeleteHandlerV1;
+import org.apache.atlas.repository.store.graph.v1.EntityGraphMapper;
+import org.apache.atlas.repository.store.graph.v1.HardDeleteHandlerV1;
+import org.apache.atlas.repository.store.graph.v1.SoftDeleteHandlerV1;
 import org.apache.atlas.repository.typestore.GraphBackedTypeStore;
 import org.apache.atlas.repository.typestore.ITypeStore;
 import org.apache.atlas.repository.typestore.StoreBackedTypeCache;
@@ -101,6 +117,7 @@ public class TestModules {
 
         @Override
         protected void configure() {
+
             GraphSandboxUtil.create();
 
             bindAuditRepository(binder());

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/test/java/org/apache/atlas/repository/impexp/ZipFileResourceTestUtils.java
----------------------------------------------------------------------
diff --git a/repository/src/test/java/org/apache/atlas/repository/impexp/ZipFileResourceTestUtils.java b/repository/src/test/java/org/apache/atlas/repository/impexp/ZipFileResourceTestUtils.java
index f0dab47..8abbb9f 100644
--- a/repository/src/test/java/org/apache/atlas/repository/impexp/ZipFileResourceTestUtils.java
+++ b/repository/src/test/java/org/apache/atlas/repository/impexp/ZipFileResourceTestUtils.java
@@ -38,6 +38,7 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -70,13 +71,43 @@ public class ZipFileResourceTestUtils {
     }
 
     public static String getModelJson(String fileName) throws IOException {
-        final String userDir = System.getProperty("user.dir");
-        String filePath = userDir + "/../addons/models/" + fileName;
-        File f = new File(filePath);
-        String s = FileUtils.readFileToString(f);
-        assertFalse(StringUtils.isEmpty(s), "Model file read correctly!");
+        String  ret                 = null;
+        File   topModelsDir         = new File(System.getProperty("user.dir") + "/../addons/models");
+        File[] topModelsDirContents = topModelsDir.exists() ? topModelsDir.listFiles() : null;
 
-        return s;
+        assertTrue(topModelsDirContents != null, topModelsDir.getAbsolutePath() + ": unable to find/read directory");
+
+        Arrays.sort(topModelsDirContents);
+
+        for (File modelDir : topModelsDirContents) {
+            if (modelDir.exists() && modelDir.isDirectory()) {
+                ret = getFileContents(modelDir, fileName);
+
+                if (ret != null) {
+                    break;
+                }
+            }
+        }
+
+        if (ret == null) {
+            ret = getFileContents(topModelsDir, fileName);
+        }
+
+        assertTrue(ret != null, fileName + ": unable to find model file");
+
+        return ret;
+    }
+
+    public static String getFileContents(File dir, String fileName) throws IOException {
+        if (dir.exists() && dir.isDirectory()) {
+            File file = new File(dir, fileName);
+
+            if (file.exists() && file.isFile()) {
+                return FileUtils.readFileToString(file);
+            }
+        }
+
+        return null;
     }
 
     public static String getModelJsonFromResources(String fileName) throws IOException {

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/repository/src/test/java/org/apache/atlas/repository/userprofile/UserProfileServiceTest.java
----------------------------------------------------------------------
diff --git a/repository/src/test/java/org/apache/atlas/repository/userprofile/UserProfileServiceTest.java b/repository/src/test/java/org/apache/atlas/repository/userprofile/UserProfileServiceTest.java
new file mode 100644
index 0000000..4e83296
--- /dev/null
+++ b/repository/src/test/java/org/apache/atlas/repository/userprofile/UserProfileServiceTest.java
@@ -0,0 +1,267 @@
+/**
+ * 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.repository.userprofile;
+
+import org.apache.atlas.AtlasErrorCode;
+import org.apache.atlas.TestModules;
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.SearchFilter;
+import org.apache.atlas.model.discovery.SearchParameters;
+import org.apache.atlas.model.profile.AtlasUserProfile;
+import org.apache.atlas.model.profile.AtlasUserSavedSearch;
+import org.apache.atlas.model.typedef.AtlasTypesDef;
+import org.apache.atlas.repository.util.FilterUtil;
+import org.apache.atlas.store.AtlasTypeDefStore;
+import org.apache.atlas.type.AtlasType;
+import org.apache.atlas.type.AtlasTypeRegistry;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import javax.inject.Inject;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.atlas.repository.impexp.ZipFileResourceTestUtils.loadModelFromJson;
+import static org.testng.Assert.*;
+
+@Guice(modules = TestModules.TestOnlyModule.class)
+public class UserProfileServiceTest {
+    private UserProfileService userProfileService;
+    private AtlasTypeDefStore  typeDefStore;
+    private int                max_searches = 4;
+
+    @Inject
+    public void UserProfileServiceTest(AtlasTypeRegistry  typeRegistry,
+                                       AtlasTypeDefStore  typeDefStore,
+                                       UserProfileService userProfileService) throws IOException, AtlasBaseException {
+        this.typeDefStore       = typeDefStore;
+        this.userProfileService = userProfileService;
+
+        loadModelFromJson("0010-base_model.json", typeDefStore, typeRegistry);
+    }
+
+    @Test
+    public void filterInternalType() throws AtlasBaseException {
+        SearchFilter searchFilter = new SearchFilter();
+        AtlasTypesDef filteredTypeDefs = typeDefStore.searchTypesDef(searchFilter);
+        int maxTypeDefs = filteredTypeDefs.getEntityDefs().size();
+
+        FilterUtil.addParamsToHideInternalType(searchFilter);
+        filteredTypeDefs = typeDefStore.searchTypesDef(searchFilter);
+
+        assertNotNull(filteredTypeDefs);
+        assertEquals(filteredTypeDefs.getEntityDefs().size(), maxTypeDefs - 3);
+    }
+
+    @Test
+    public void createsNewProfile() throws AtlasBaseException {
+        int i = 0;
+        assertSaveLoadUserProfile(i++);
+        assertSaveLoadUserProfile(i);
+    }
+
+    @Test(dependsOnMethods = { "createsNewProfile", "savesQueryForAnNonExistentUser" }, expectedExceptions = AtlasBaseException.class)
+    public void savesAlreadyExistingQueryForAnExistingUser() throws AtlasBaseException {
+        SearchParameters expectedSearchParameter = getActualSearchParameters();
+
+        for (int i = 0; i < 2; i++) {
+            String userName = getIndexBasedUserName(i);
+
+            for (int j = 0; j < max_searches; j++) {
+                String queryName = getIndexBasedQueryName(j);
+                AtlasUserProfile actual = userProfileService.addSavedSearch(new AtlasUserSavedSearch(userName, queryName, expectedSearchParameter));
+                assertNotNull(actual);
+            }
+        }
+    }
+
+    @Test(dependsOnMethods = { "createsNewProfile", "savesQueryForAnNonExistentUser", "savesAlreadyExistingQueryForAnExistingUser" })
+    public void savesExistingQueryForAnExistingUser() throws AtlasBaseException {
+        SearchParameters expectedSearchParameter = getActualSearchParameters();
+
+        for (int i = 0; i < 2; i++) {
+            String userName = getIndexBasedUserName(i);
+
+            for (int j = 4; j < max_searches + 6; j++) {
+                String queryName = getIndexBasedQueryName(j);
+                AtlasUserProfile actual = userProfileService.addSavedSearch(new AtlasUserSavedSearch(userName, queryName, expectedSearchParameter));
+                assertNotNull(actual);
+
+                AtlasUserSavedSearch savedSearch = userProfileService.getSavedSearch(userName, queryName);
+                assertNotNull(savedSearch);
+                assertEquals(savedSearch.getSearchParameters(), expectedSearchParameter);
+            }
+        }
+    }
+
+    private SearchParameters getActualSearchParameters() {
+        SearchParameters sp = new SearchParameters();
+        sp.setClassification("test-classification");
+        sp.setQuery("g.v().has('__guid').__guid.toList()");
+        sp.setLimit(10);
+        sp.setTypeName("some-type");
+
+        return sp;
+    }
+
+    @Test(dependsOnMethods = "createsNewProfile")
+    public void savesQueryForAnNonExistentUser() throws AtlasBaseException {
+        String expectedUserName = "firstXYZ";
+        String expectedQueryName = "testQuery";
+        SearchParameters expectedSearchParam = getActualSearchParameters();
+
+        AtlasUserProfile actual = userProfileService.addSavedSearch(new AtlasUserSavedSearch(expectedUserName, expectedQueryName, expectedSearchParam));
+        assertEquals(actual.getName(), expectedUserName);
+        assertEquals(actual.getSavedSearches().size(), 1);
+        assertEquals(actual.getSavedSearches().get(0).getName(), expectedQueryName);
+    }
+
+    @Test(dependsOnMethods = "createsNewProfile")
+    public void savesMultipleQueriesForUser() throws AtlasBaseException {
+        final String userName = getIndexBasedUserName(0);
+        createUserWithSavedQueries(userName);
+    }
+
+    private void createUserWithSavedQueries(String userName) throws AtlasBaseException {
+        SearchParameters actualSearchParameter = getActualSearchParameters();
+
+        saveQueries(userName, actualSearchParameter);
+        for (int i = 0; i < max_searches; i++) {
+            AtlasUserSavedSearch savedSearch = userProfileService.getSavedSearch(userName, getIndexBasedQueryName(i));
+            assertEquals(savedSearch.getName(), getIndexBasedQueryName(i));
+            assertEquals(savedSearch.getSearchParameters(), actualSearchParameter);
+        }
+    }
+
+    private void saveQueries(String userName, SearchParameters sp) throws AtlasBaseException {
+        for (int i = 0; i < max_searches; i++) {
+            userProfileService.addSavedSearch(new AtlasUserSavedSearch(userName, getIndexBasedQueryName(i), sp));
+        }
+    }
+
+    @Test(dependsOnMethods = {"createsNewProfile", "savesMultipleQueriesForUser"})
+    public void verifyQueryNameListForUser() throws AtlasBaseException {
+        final String userName = getIndexBasedUserName(0);
+
+        List<AtlasUserSavedSearch> list = userProfileService.getSavedSearches(userName);
+        List<String> names = getIndexBasedQueryNamesList();
+        for (int i = 0; i < names.size(); i++) {
+            assertTrue(names.contains(list.get(i).getName()), list.get(i).getName() + " failed!");
+        }
+    }
+
+    @Test(dependsOnMethods = {"createsNewProfile", "savesMultipleQueriesForUser"}, enabled = false)
+    public void verifyQueryConversionFromJSON() throws AtlasBaseException {
+        List<AtlasUserSavedSearch> list = userProfileService.getSavedSearches("first-0");
+
+        for (int i = 0; i < max_searches; i++) {
+            SearchParameters sp = list.get(i).getSearchParameters();
+            String json = AtlasType.toJson(sp);
+            assertEquals(AtlasType.toJson(getActualSearchParameters()).replace("\n", "").replace(" ", ""), json);
+        }
+    }
+
+    @Test(dependsOnMethods = {"createsNewProfile", "savesMultipleQueriesForUser"})
+    public void updateSearch() throws AtlasBaseException {
+        final String queryName = getIndexBasedQueryName(0);
+        String userName = getIndexBasedUserName(0);
+        AtlasUserSavedSearch expected = userProfileService.getSavedSearch(userName, queryName);
+        assertNotNull(expected);
+
+        SearchParameters sp = expected.getSearchParameters();
+        sp.setClassification("new-classification");
+
+        AtlasUserSavedSearch actual = userProfileService.updateSavedSearch(expected);
+
+        assertNotNull(actual);
+        assertNotNull(actual.getSearchParameters());
+        assertEquals(actual.getSearchParameters().getClassification(), expected.getSearchParameters().getClassification());
+    }
+
+    @Test(dependsOnMethods = {"createsNewProfile", "savesMultipleQueriesForUser", "verifyQueryNameListForUser"}, expectedExceptions = AtlasBaseException.class)
+    public void deleteUsingGuid() throws AtlasBaseException {
+        final String queryName = getIndexBasedQueryName(1);
+        String userName = getIndexBasedUserName(0);
+
+        AtlasUserSavedSearch expected = userProfileService.getSavedSearch(userName, queryName);
+        assertNotNull(expected);
+
+        userProfileService.deleteSavedSearch(expected.getGuid());
+        userProfileService.getSavedSearch(userName, queryName);
+    }
+
+    @Test(dependsOnMethods = {"createsNewProfile", "savesMultipleQueriesForUser", "verifyQueryNameListForUser"})
+    public void deleteSavedQuery() throws AtlasBaseException {
+        final String userName = getIndexBasedUserName(0);
+        AtlasUserProfile expected = userProfileService.getUserProfile(userName);
+        assertNotNull(expected);
+
+        int new_max_searches = expected.getSavedSearches().size();
+        String queryNameToBeDeleted = getIndexBasedQueryName(max_searches - 2);
+        userProfileService.deleteSearchBySearchName(userName, queryNameToBeDeleted);
+
+        List<AtlasUserSavedSearch> savedSearchList = userProfileService.getSavedSearches(userName);
+        assertEquals(savedSearchList.size(), new_max_searches - 1);
+    }
+
+    @Test(dependsOnMethods = {"createsNewProfile", "savesMultipleQueriesForUser", "verifyQueryNameListForUser"})
+    void deleteUser() throws AtlasBaseException {
+        String userName = getIndexBasedUserName(1);
+
+        userProfileService.deleteUserProfile(userName);
+        try {
+            userProfileService.getUserProfile(userName);
+        }
+        catch(AtlasBaseException ex) {
+            assertEquals(ex.getAtlasErrorCode().name(), AtlasErrorCode.INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND.name());
+        }
+    }
+
+    private void assertSaveLoadUserProfile(int i) throws AtlasBaseException {
+        String s = String.valueOf(i);
+        AtlasUserProfile expected = getAtlasUserProfile(i);
+        userProfileService.saveUserProfile(expected);
+
+        AtlasUserProfile actual = userProfileService.getUserProfile(expected.getName());
+        assertNotNull(actual);
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getFullName(), actual.getFullName());
+    }
+
+    public static AtlasUserProfile getAtlasUserProfile(Integer s) {
+        return new AtlasUserProfile(getIndexBasedUserName(s), String.format("first-%s last-%s", s, s));
+    }
+
+    private static String getIndexBasedUserName(Integer i) {
+        return String.format("first-%s", i.toString());
+    }
+
+    private static String getIndexBasedQueryName(Integer i) {
+        return String.format("testQuery-%s", i.toString());
+    }
+
+    public List<String> getIndexBasedQueryNamesList() {
+        List<String> list = new ArrayList<>();
+        for (int i = 0; i < max_searches; i++) {
+            list.add(getIndexBasedQueryName(i));
+        }
+
+        return list;
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/ccd121e7/webapp/src/main/java/org/apache/atlas/notification/NotificationEntityChangeListener.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/notification/NotificationEntityChangeListener.java b/webapp/src/main/java/org/apache/atlas/notification/NotificationEntityChangeListener.java
index 6eadc96..604a19e 100644
--- a/webapp/src/main/java/org/apache/atlas/notification/NotificationEntityChangeListener.java
+++ b/webapp/src/main/java/org/apache/atlas/notification/NotificationEntityChangeListener.java
@@ -23,6 +23,7 @@ import org.apache.atlas.AtlasException;
 import org.apache.atlas.listener.EntityChangeListener;
 import org.apache.atlas.notification.entity.EntityNotification;
 import org.apache.atlas.notification.entity.EntityNotificationImpl;
+import org.apache.atlas.repository.graph.GraphHelper;
 import org.apache.atlas.typesystem.IReferenceableInstance;
 import org.apache.atlas.typesystem.IStruct;
 import org.apache.atlas.typesystem.ITypedReferenceableInstance;
@@ -165,6 +166,10 @@ public class NotificationEntityChangeListener implements EntityChangeListener {
         List<EntityNotification> messages = new LinkedList<>();
 
         for (IReferenceableInstance entityDefinition : entityDefinitions) {
+            if(GraphHelper.isInternalType(entityDefinition.getTypeName())) {
+                continue;
+            }
+
             Referenceable       entity                  = new Referenceable(entityDefinition);
             Map<String, Object> attributesMap           = entity.getValuesMap();
             List<String>        entityNotificationAttrs = getNotificationAttributes(entity.getTypeName());


[3/3] atlas git commit: ATLAS-2141: edit/disassociate tag results in NPE

Posted by ma...@apache.org.
ATLAS-2141: edit/disassociate tag results in NPE

(cherry picked from commit 4b9d2670709df52e0d983587832fe6256220691b)


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

Branch: refs/heads/master
Commit: c1f4007a9d9f85f888a5c9164e11937025157edb
Parents: ccd121e
Author: Madhan Neethiraj <ma...@apache.org>
Authored: Sun Sep 17 10:37:26 2017 -0700
Committer: Madhan Neethiraj <ma...@apache.org>
Committed: Sun Sep 17 14:51:49 2017 -0700

----------------------------------------------------------------------
 .../org/apache/atlas/repository/graph/GraphHelper.java |  6 +++++-
 .../store/graph/v1/AtlasEntityChangeNotifier.java      | 13 ++-----------
 .../notification/NotificationEntityChangeListener.java |  4 +++-
 3 files changed, 10 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/atlas/blob/c1f4007a/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java b/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java
index 4cb430d..639077d 100755
--- a/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java
+++ b/repository/src/main/java/org/apache/atlas/repository/graph/GraphHelper.java
@@ -1079,8 +1079,12 @@ public final class GraphHelper {
 
     }
 
+    public static boolean isInternalType(AtlasVertex vertex) {
+        return vertex != null && isInternalType(getTypeName(vertex));
+    }
+
     public static boolean isInternalType(String typeName) {
-        return typeName.startsWith(Constants.INTERNAL_PROPERTY_KEY_PREFIX);
+        return typeName != null && typeName.startsWith(Constants.INTERNAL_PROPERTY_KEY_PREFIX);
     }
 
     public static void setArrayElementsProperty(IDataType elementType, AtlasVertex instanceVertex, String propertyName, List<Object> values) {

http://git-wip-us.apache.org/repos/asf/atlas/blob/c1f4007a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasEntityChangeNotifier.java
----------------------------------------------------------------------
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasEntityChangeNotifier.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasEntityChangeNotifier.java
index db47627..7b349c4 100644
--- a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasEntityChangeNotifier.java
+++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/AtlasEntityChangeNotifier.java
@@ -227,14 +227,10 @@ public class AtlasEntityChangeNotifier {
         }
 
         for (AtlasEntityHeader atlasEntityHeader : atlasEntityHeaders) {
-            if(GraphHelper.isInternalType(atlasEntityHeader.getTypeName())) {
-                continue;
-            }
-
             String      guid        = atlasEntityHeader.getGuid();
             AtlasVertex atlasVertex = AtlasGraphUtilsV1.findByGuid(guid);
 
-            if(atlasVertex == null) {
+            if(atlasVertex == null || GraphHelper.isInternalType(atlasVertex)) {
                 continue;
             }
 
@@ -262,12 +258,7 @@ public class AtlasEntityChangeNotifier {
         }
 
         AtlasVertex atlasVertex = AtlasGraphUtilsV1.findByGuid(entityId);
-        if(atlasVertex == null) {
-            return;
-        }
-
-        if (atlasVertex == null) {
-            LOG.warn("updateFullTextMapping(): no entity exists with guid {}", entityId);
+        if(atlasVertex == null || GraphHelper.isInternalType(atlasVertex)) {
             return;
         }
 

http://git-wip-us.apache.org/repos/asf/atlas/blob/c1f4007a/webapp/src/main/java/org/apache/atlas/notification/NotificationEntityChangeListener.java
----------------------------------------------------------------------
diff --git a/webapp/src/main/java/org/apache/atlas/notification/NotificationEntityChangeListener.java b/webapp/src/main/java/org/apache/atlas/notification/NotificationEntityChangeListener.java
index 604a19e..53acf56 100644
--- a/webapp/src/main/java/org/apache/atlas/notification/NotificationEntityChangeListener.java
+++ b/webapp/src/main/java/org/apache/atlas/notification/NotificationEntityChangeListener.java
@@ -187,7 +187,9 @@ public class NotificationEntityChangeListener implements EntityChangeListener {
             messages.add(notification);
         }
 
-        notificationInterface.send(NotificationInterface.NotificationType.ENTITIES, messages);
+        if (!messages.isEmpty()) {
+            notificationInterface.send(NotificationInterface.NotificationType.ENTITIES, messages);
+        }
     }
 
     private List<String> getNotificationAttributes(String entityType) {