Neo4jClient:对 CRUD API 的质疑

Posted

技术标签:

【中文标题】Neo4jClient:对 CRUD API 的质疑【英文标题】:Neo4jClient: doubts about CRUD API 【发布时间】:2013-10-21 12:42:47 【问题描述】:

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

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

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

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

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

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

我的问题:

    我可以使用 CRUD API 通过其 ID 查找节点 A、从 POCO 创建节点 B、在 A 和 B 之间创建关系并将 B 的 ID 添加到旧索引中吗? 如果不是,还有什么替代方法? CRUD API 是否被视为遗留代码,我们是否应该转向基于 Cypher 的 Neo4j 2.0 方法? 这种基于 Cypher 的方法是否意味着我们失去了创建操作的 POCO 到节点的转换?非常方便。

此外,Neo4jClient 的文档是否可以更新,因为坦率地说,它很差。我确实意识到 Readify 还提供商业支持,这样可以解释一些事情。

谢谢!

【问题讨论】:

【参考方案1】:

我是 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。

第二季度。

“如果不是,还有什么替代方法?CRUD API 是否被视为遗留代码,我们是否应该转向基于 Cypher 的 Neo4j 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

第三季度。

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

正确。但这就是我们所有人都想做的事情,Neo4j 的发展方向,Neo4jClient 的发展方向。

想一想 SQL(我假设你很熟悉)。您是否运行查询以查找节点的内部标识符,包括其在磁盘上的文件偏移量,然后在第二个查询中使用此内部标识符来操作它?不会。您只需运行一个查询即可一次性完成所有操作。

现在,人们喜欢传递 Node&lt;T&gt;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();

通过这种方式,您可以有效地重复使用引用,而不必从一开始就将它们拉回来。

【讨论】:

非常感谢您的回答。通过“POCO 到节点的翻译”,我实际上的意思是您没有拼写所有内容并在查询中写入,例如,... create (user Id=1, FirstName='John', LastName='Doe', BirthDate=... 作为字符串,但您可以使用带有属性的普通对象。但我可以看到上面的“newUser”参数涵盖了这一点。 啊,是的,我现在看到我阅读您的 Q3 的顺序与您的意图相反。唉,看来你现在已经完成了你现在需要的一切。密码所有的东西! :)

以上是关于Neo4jClient:对 CRUD API 的质疑的主要内容,如果未能解决你的问题,请参考以下文章

C# Neo4jClient 任务取消异常

Neo4jClient - 从 Neo4jClient 中创建索引?

使用 neo4jclient 确保唯一节点

Neo4jClient "CASE WHEN" 连同返回

ASP.NET 和 Neo4jClient - 在哪里存储连接?

Neo4jClient - 如何检查属性是不是存在