Neo4jClient:对CRUD API的疑虑

时间:2013-10-21 12:42:47

标签: neo4jclient

我的持久层主要使用Neo4jClient访问Neo4j 1.9.4数据库。更具体地说,要创建节点,我在Neo4jClient的CRUD API中使用IGraphClient#Create()并查询图表我使用了Neo4jClient的Cypher支持。

一切都很顺利,直到我的一位朋友指出,对于每个查询,我基本上都做了两个HTTP请求:

  • 通过节点的唯一ID(不是其节点ID,但由SnowMaker生成的唯一ID)从遗留索​​引获取节点引用的一个请求
  • 从该节点引用开始的一个Cypher查询,它执行实际工作。

对于读取操作,我做了显而易见的事情并将索引查找移到我的Start()调用中,即:

GraphClient.Cypher
    .Start(new { user = Node.ByIndexLookup("User", "Id", userId) })
    // ... the rest of the query ...

另一方面,对于创建操作,我认为这实际上并不可行。我的意思是:Create()方法采用POCO,几个关系实例和几个索引条目,以便在一个事务/ HTTP请求中创建节点,它的关系和索引条目。问题是您传递给关系实例的节点引用:它们来自哪里?从以前的HTTP请求开始,对吗?

我的问题:

  1. 我可以使用CRUD API通过ID查找节点A,从POCO创建节点B,在A和B之间创建关系,并在一个请求中将B的ID添加到传统索引中吗?
  2. 如果没有,有什么替代方案? CRUD API是否被视为遗留代码,我们是否应该采用基于Cypher的Ne​​o4j 2.0方法?
  3. 这种基于Cypher的方法是否意味着我们失去了创建操作的POCO到节点的转换?那很方便。
  4. 此外,可以更新Neo4jClient的文档,因为坦白说,它很差。我确实认识到Readify还提供商业支持,以便解释事情。

    谢谢!

1 个答案:

答案 0 :(得分:8)

我是Neo4jClient的作者。 (那个免费赠送软件的人。)

Q1A:

  

“我可以使用CRUD API通过其ID查找节点A,从POCO创建节点B,在A和B之间创建关系”

Cypher不仅是未来的方式,也是'现在'的方式。

从Cypher开始(为此提供了大量资源):

START user=node:user(Id: 1234)
CREATE user-[:INVITED]->(user2 { Id: 4567, Name: "Jim" })
Return user2

然后将其转换为C#:

graphClient.Cypher
    .Start(new { user = Node.ByIndexLookup("User", "Id", userId) })
    .Create("user-[:INVITED]->(user2 {newUser})")
    .WithParam("newUser", new User { Id = 4567, Name = "Jim" })
    .Return(user2 => user2.Node<User>())
    .Results;

这里有更多类似的例子:https://github.com/Readify/Neo4jClient/wiki/cypher-examples

Q1B:

  

“并在一个请求中将B的ID添加到旧索引中?”

不,Cypher不支持旧版索引。如果你真的想继续使用它们,那么你应该坚持使用CRUD API。没关系:如果要使用旧版索引,请使用旧版API。

Q2。

  

“如果没有,那么替代方案是什么?CRUD API是否被认为是遗留代码,我们是否应该采用基于Cypher的Ne​​o4j 2.0方法?”

这正是你想要做的。 Cypher,带有标签和自动索引:

// One time op to create the index
// Yes, this syntax is a bit clunky in C# for now
graphClient.Cypher
    .Create("INDEX ON :User(Id)")
    .ExecuteWithoutResults();

// Find an existing user, create a new one, relate them,
// and index them, all in a single HTTP call
graphClient.Cypher
    .Match("(user:User)")
    .Where((User user) => user.Id == userId)
    .Create("user-[:INVITED]->(user2 {newUser})")
    .WithParam("newUser", new User { Id = 4567, Name = "Jim" })
    .ExecuteWithoutResults();

此处有更多示例:https://github.com/Readify/Neo4jClient/wiki/cypher-examples

Q3。

  

“这种基于Cypher的方法是否意味着我们失去了创建操作的POCO到节点的转换?这非常方便。”

正确。但这就是我们共同想做的事情,Neo4j的发展方向,以及Neo4jClient的发展方向。

考虑SQL一秒钟(我认为你熟悉的东西)。您是否运行查询以查找节点的内部标识符,包括其在磁盘上的文件偏移量,然后在第二个查询中使用此内部标识符来操作它?不会。您运行一个查询,在一次点击中完成所有操作。

现在,人们喜欢传递Node<T>NodeReference个实例的常见用例是减少查询的重复次数。这是一个合理的问题,但是因为.NET中的流畅查询是不可变的,我们只能构造一个基本查询:

public ICypherFluentQuery FindUserById(long userId)
{
    return graphClient.Cypher
        .Match("(user:User)")
        .Where((User user) => user.Id == userId);
    // Nothing has been executed here: we've just built a query object
}

然后像这样使用它:

public void DeleteUser(long userId)
{
    FindUserById(userId)
        .Delete("user")
        .ExecuteWithoutResults();
}

或者,添加更多Cypher逻辑来删除所有关系:

然后像这样使用它:

public void DeleteUser(long userId)
{
    FindUserById(userId)
        .Match("user-[:?rel]-()")
        .Delete("rel, user")
        .ExecuteWithoutResults();
}

通过这种方式,您可以有效地重复使用引用,但无需首先将它们拉回到线上。