如何优化节点关系的创建?

时间:2014-04-30 06:44:15

标签: neo4j neo4jclient

我创建了大约200 000个节点。我想根据facebook好友列表创建它们之间的关系。

首先我获得父节点引用

var userquery = client.Cypher
    .Match("(n:User)")
    .Where("n.UserID=" + _ui.UserID)
    .Return<Node<UserInfo>>("n")
    .Results
    .Single();

然后根据facebook朋友列表推荐朋友

var friendquery1 = client.Cypher
    .Match("(n:User)")
    .Where("n.ThirdPartyFriendsIds In[" + _ui.ThirdPartyFriendsIds + "]")
    .Return<Node<UserInfo>>("n");

然后用我得到的参考创建关系

var friendquery = friendquery1.Results;

foreach(Node<UserInfo> friendnode in friendquery)
{
    client.CreateRelationship(userquery.Reference, new UserRelationship (friendnode.Reference));
}

任何人都可以帮助我优化这一点,创建关系需要相当长的时间

1 个答案:

答案 0 :(得分:1)

你可以在这里改进很多东西。

注射风险和参数

不要永远在您的查询中编写代码,如下所示:

.Where("n.UserID=" + _ui.UserID)

这是a)对性能不利,因为它会破坏所有查询计划缓存,以及b)等待发生的主要安全漏洞。它被称为“注射”攻击。您可以查找“SQL注入”以获取有关它的大量信息,但同样的风险适用于Cypher。这也是对性能的影响。

请改为:

.Where((User n) => n.UserID == _ui.UserID)

如果你不能使用像这样的lambda表达式建模它,你可以使用参数:

.Where("n.UserID = {userId}")
.WithParams(new { userId = _ui.UserID })

节点参考

请勿使用Node<T>,例如Node<UserInfo>>。 Neo4j 2.0不推荐使用原始节点ID的概念,并且随着时间的推移逐步淘汰。这有很多原因,还有很多关于它的文章,所以我不会在这里复制。

如果您需要数据,请使用UserInfo。如果您需要对该节点执行某些操作,请在Cypher查询中添加更多子句。

避免使用IGraphClient.Cypher以外的任何内容,例如IGraphClient.CreateRelationship

所有其他方法都使用Neo4j REST API,逐渐被Cypher调用取代。

在这种情况下,您的性​​能问题是因为您正在为要创建的每个关系运行整个网络请求和数据库事务。这很慢。

相反,请在一个Cypher查询中尽可能多地做。

全部放在一起

让我们忽略C#并回到Cypher一秒钟。

找到用户:

MATCH (user:User)
WHERE user.UserID = {userId}

找到所有朋友:

MATCH (friend:User)
WHERE friend.ThirdPartyFriendsId IN {thirdPartyFriendIds}

创建关系,只有当它尚不存在时(因此您可以多次运行查询而不创建重复项):

CREATE UNIQUE user-[:FRIENDS_WITH]->friend

现在让我们把所有这些放在一起:

MATCH (user:User)
WHERE user.UserID = {userId}
MATCH (friend:User)
WHERE friend.ThirdPartyFriendsId IN {thirdPartyFriendIds}
CREATE UNIQUE user-[:FRIENDS_WITH]->friend

现在让我们将其转换为C#:

client.Cypher
    .Match("(user:User)")
    .Where((User user) => user.UserID == _ui.UserID)
    .Match("(friend:User)")
    .Where("friend.ThirdPartyFriendId IN {thirdPartyFriendIds}")
    .WithParams(new { thirdPartyFriendIds = _ui.ThirdPartyFriendsIds })
    .ExecuteWithoutResults();

请注意,我们最后必须致电ExecuteWithoutResults,因为我们不会返回任何结果。 (我们不需要退货。)

免责声明:我刚刚在答案窗口输入所有内容,因此可能会出现轻微错误。不要复制和粘贴我的代码。遵循原则。

更快

到目前为止,这种方法仍然要求您为每个用户运行一次。

这样的事情会一次性创建所有用户之间的关系。 (但可能需要很长时间,具体取决于您拥有多少用户。)

MATCH (user:User)
MATCH (friend:User)
WHERE friend.ThirdPartyFriendId IN user.ThirdPartyFriendsIds
CREATE UNIQUE user-[:FRIENDS_WITH]->friend

关键原则

告诉Neo4j你要做的事情总是在做什么,而不是在它上面低声说出许多微小的命令。当知道一大堆要做的事情时,它会做得更好。

永远不要通过将动态字符串值连接到它们来构造查询。除了非常不安全之外,它会很慢,因为你每次都会编译查询,这很昂贵。

在转到C#之前,始终从Cypher开始。您将获得更多帮助,因为任何使用Neo4j的人都可以帮助而不仅仅是.NET用户,并且您将获得更好的结果,因为您将执行更多基于批处理的操作。