Neo4j Spring用于社交RESTful层的数据POC

时间:2013-01-03 10:35:19

标签: social-networking neo4j aspectj spring-data-neo4j spring-data-mongodb

开始处理新项目...... RESTful 层为社交网络平台提供服务。
Neo4j 是我主要数据存储的明显选择,我之前有机会使用 Neo 但没有利用 Spring Data 能力来映射 POJO 到节点看起来很方便。

目标:

  1. 该图层应提供类似于 Facebook Graph API 的支持,该图形为每个实体/对象相关属性&可以从网址引用的连接。
    FB Graph API

  2. 如果可能的话,我想避免传输对象,这些传输对象将被序列化到域实体或从域实体序列化,并使用我的域pojo作为JSON传输到客户端/从客户端传输。

  3. 例子:

    • HTTP GET /个人资料/ {id} /?fields = ...& connections = ... 响应将个人资料对象包含URL中的请求。

    • HTTP GET /profile/{id}/stories/?fields=..&connections=...&page=..&sort=... 响应将根据请求列出 Story 对象。

    相关版本:

    • Spring Framework 3.1.2
    • Spring Data Neo4j 2.1.0.RC3
    • Spring Data Mongodb 1.1.0.RC1
    • AspectJ 1.6.12
    • 杰克逊 1.8.5

    为简单起见,我们有 个人资料 故事 节点和 角色< / em> 他们之间的关系。

    public abstract class GraphEntity {
    @GraphId
    protected Long id;
    }
    


    配置文件节点

    @NodeEntity
    @Configurable
    public class Profile extends GraphEntity {
    
    // Profile fields
    private String firstName;
    private String lastName;
    
    // Profile connections  
    @RelatedTo(type = "FOLLOW", direction = Direction.OUTGOING)
    private Set<Profile> followThem;
    
    @RelatedTo(type = "BOOKMARK", direction = Direction.OUTGOING)
    private Set<Story> bookmarks;
    
    @Query("START profile=node({self}) match profile-[r:ROLE]->story where r.role = FOUNDER and story.status = PUBLIC")
    private Iterable<Story> published;
    }
    


    故事节点

    @NodeEntity
    @Configurable
    public class Story extends GraphEntity {
    
    // Story fields
    private String title;
    private StoryStatusEnum status = StoryStatusEnum.PRIVATE;
    
    // Story connections
    @RelatedToVia(type = "ROLE", elementClass = Role.class, direction = Direction.INCOMING)
    private Set<Role> roles;
    }
    


    角色关系

    @RelationshipEntity(type = "ROLE")
    public class Role extends GraphEntity {
    
    @StartNode
    private Profile profile;
    @EndNode
    private Story story;
    
    private StoryRoleEnum role;
    }
    


    起初我没有使用 AspectJ 支持,但我发现它对我的用例非常有用,因为它在POJO和实际节点之间生成一个分隔符因此我可以请求根据请求轻松获得属性/连接,Domain Driven Design Approach似乎非常好。

    问题1 - AspectJ:

    假设我要为对象定义默认字段,无论是否在URL中请求,这些字段都将返回给客户端...所以我尝试过 @FETCH 这些字段上的注释,但在使用 AspectJ 时似乎无效。 目前我这样做..

    public Profile(Node n) {
        setPersistentState(n);
        this.id = getId();
        this.firstName = getFirstName();
        this.lastName = getLastName();  
    }
    

    这是实现这一目标的正确方法吗?即使使用 AspectJ ,是否应支持 @FETCH 注释?我很乐意得到关于AspectJ + Neo4j的例子/博客几乎找不到任何东西......

    问题2 - 分页:

    我想在请求特定连接时支持分页,例如

    /个人资料/ {id} / stories / ,如果相关故事如下

    // inside profile node
    @RelatedTo(type = "BOOKMARK", direction = Direction.OUTGOING)
    private Set<Story> bookmarks; 
    


    / profile / {id} / stories / ,如果相关故事如下

     // inside profile node
    @Query("START profile=node({self}) match profile-[r:ROLE]->story where r.role = FOUNDER and story.status = PUBLIC")
    private Iterable<Story> published;
    

    支持分页是否支持 @Query || @RelatedTo || @RelatedToVia 使用 Pageable 界面检索 Page 而不是Set / List / Iterable?根据客户的要求,限制和排序应该是动态的...我可以使用Cypher Query DSL实现这一目标,但更喜欢使用基本的...其他方法将被愉快地接受。

    问题3 - @Query与{self}:

    有点愚蠢的问题,但我无法帮助它:),似乎在节点实体中使用@Query(使用{self}参数}时,返回类型必须是Iterable才有意义.. 我们举个例子......

    // inside profile node
    @Query("START profile=node({self}) match profile-[r:ROLE]->story where r.role = FOUNDER and story.status = PUBLIC")
    private Iterable<Story> published;
    

    请求发布连接时:

        // retrieving the context profile
        Profile profile = profileRepo.findOne(id);
        // getting the publishe stories using AspectJ - will redirect to the backed node
        Iterable<Story> published = profile.getPublished();
        // set the result into the domain object - will throw exception of read only because the type is Iterable
        profile.setPublished(published);
    

    有解决方法吗?这不是在Profile ..中创建另一个属性@Transiant。

    问题4 - 递归关系:

    我在传递/递归关系方面遇到一些问题,在Story中分配新的个人资料角色时,关系实体角色包含 @EndNode 故事,其中包含角色连接。 。其中一个是上面的上下文角色,它永远不会结束:) 有没有办法配置弹簧数据引擎不创建这些永无止境的关系?

    问题5 - 交易:

    也许我之前应该提到它但是我使用的是REST服务器用于Neo4j数据库,从之前的阅读中我了解到交易中没有开箱即用的支持?比如使用嵌入式服务器时 我有以下代码......

        Profile newProfile = new Profile();
        newProfile.getFollowThem().add(otherProfile);
        newProfile.getBookmarks().add(otherStory);
        newProfile.persist(); // or profileRepo.save(newProfile)
    

    使用REST服务器时会在事务中运行吗?这里有一些操作,如果一个失败都失败了吗?

    问题6 - Mongo + Neo4j:

    我需要存储没有关系性质的数据..比如Feed,评论,按摩。
    我想与 MongoDB 集成以存储这些... can我将域pojo字段/连接拆分为mongo / neo4j并支持跨域存储?它会支持AspectJ吗?


    现在就是这样......我欢迎任何有关上述任何方法的评论。谢谢。

1 个答案:

答案 0 :(得分:3)

开始回答,绝不是完整的:

也许升级到.RELEASE版本?

问题1

如果要将AspectJ实体序列化为JSON,则必须排除高级映射生成的内部字段(请参阅此forum discussion)。

当您使用高级映射时,不需要@Fetch,因为无论如何都要从数据库中读取数据。

问题2

对于字段的分页,您可以尝试使用@QueryLIMIT 100 SKIP 10作为固定参数的密码查询。否则,您可以使用存储库/模板来实际使用分页信息填充实体字段中的Collection。

问题3

我不认为@Query的返回类型必须是Iterable它也应该与其他类型(集合或具体类型)一起使用。你遇到了什么问题?

为了创建递归关系 - 尝试首先存储关系对象本身,然后仅存储节点实体。或使用template.createRelationshipBetween(start, end, type, allowDuplicates)创建关系。

问题5

当您在REST上使用SDN时,它可能效果不佳,因为现在底层实现使用RestGraphDatabase进行细粒度操作,而高级映射使用非常细粒度的调用。你有什么理由不想使用嵌入式模式吗?对于REST服务器,我肯定会使用简单映射,并尝试使用cypher来处理读取操作。

使用REST APi,每个http呼叫只有一个tx,拥有更大事务的唯一选择是使用rest-batch-api

在底层rest-graph-database中存在伪事务支持,它在一个批处理休息请求中批处理在“事务”中发出的调用。但是这些调用必须不依赖于tx期间的读取结果,这些只会在tx完成后填充。使用SDN这种方法也存在一些问题,所以我为此禁用了它(它是rest-graphdb的config-option / system-property)。

问题6

目前,MongoDB和Neo4j的跨店支持仅用于JPA /关系商店。我们讨论过一次弹簧数据项目之间的跨存储引用,但没有跟进。