如何在 Neo4j v2 中使用 Neo4jClient 创建节点? [英] How to Create a Node with Neo4jClient in Neo4j v2?

查看:17
本文介绍了如何在 Neo4j v2 中使用 Neo4jClient 创建节点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Neo4j v1.9.x 下,我使用了以下代码.

Under Neo4j v1.9.x, I used the following sort of code.

private Category CreateNodeCategory(Category cat)
{
        var node = client.Create(cat,
            new IRelationshipAllowingParticipantNode<Category>[0],
            new[]
            {
                new IndexEntry(NeoConst.IDX_Category)
                {
                    { NeoConst.PRP_Name, cat.Name },
                    { NeoConst.PRP_Guid, cat.Nguid.ToString() }
                }
            });
        cat.Nid = node.Id;
        client.Update<Category>(node, cat);
        return cat;
}

原因是节点 ID 是自动生成的,我可以稍后使用它进行快速查找、其他查询中的起始位等.如下所示:

The reason being that the Node Id was auto generated and I could use it later for a quick look up, start bits in other queries, etc. Like the following:

    private Node<Category> CategoryGet(long nodeId)
    {
        return client.Get<Category>((NodeReference<Category>)nodeId);
    }

这使得以下看起来运行良好.

This enables the following which appeared to work well.

    public Category CategoryAdd(Category cat)
    {
        cat = CategoryFind(cat);
        if (cat.Nid != 0) { return cat; }
        return CreateNodeCategory(cat);
    }

    public Category CategoryFind(Category cat)
    {
        if (cat.Nid != 0) { return cat; }
        var node = client.Cypher.Start(new { 
    n = Node.ByIndexLookup(NeoConst.IDX_Category, NeoConst.PRP_Name, cat.Name)})
            .Return<Node<Category>>("n")
            .Results.FirstOrDefault();
        if (node != null) { cat = node.Data; }
        return cat;
    }

现在 cypher Wiki、示例和坏习惯建议在所有 CRUD 中使用 .ExecuteWithoutResults().

Now the cypher Wiki, examples and bad-habits recommend using the .ExecuteWithoutResults() in all the CRUD.

所以我的问题是如何为节点 ID 设置自动增量值?

So the question I have is how do you have an Auto Increment value for the node ID?

推荐答案

首先,对于 Neo4j 2 及更高版本,您总是需要从参考框架我将如何在 Cypher 中做到这一点?"开始.然后,只有这样,你才会担心 C#.

First up, for Neo4j 2 and onwards, you always need to start with the frame of reference "how would I do this in Cypher?". Then, and only then, do you worry about the C#.

现在,提炼你的问题,听起来你的主要目标是创建一个节点,然后返回对它的引用以供进一步工作.

Now, distilling your question, it sounds like your primary goal is to create a node, and then return a reference to it for further work.

您可以在密码中执行此操作:

You can do this in cypher with:

CREATE (myNode)
RETURN myNode

在 C# 中,这将是:

In C#, this would be:

var categoryNode = graphClient.Cypher
    .Create("(category {cat})")
    .WithParams(new { cat })
    .Return(cat => cat.Node<Category>())
    .Results
    .Single();

但是,这仍然不是您在原始 CreateNodeCategory 方法中所做的 100%.您正在数据库中创建节点,获取 Neo4j 的内部标识符,然后将该标识符保存回同一个节点.基本上,您正在使用 Neo4j 为您生成自动递增的数字.这是功能性的,但不是一个很好的方法.我会解释更多...

However, this still isn't 100% what you were doing in your original CreateNodeCategory method. You are creating the node in the DB, getting Neo4j's internal identifier for it, then saving that identifier back into the same node. Basically, you're using Neo4j to generate auto-incrementing numbers for you. That's functional, but not really a good approach. I'll explain more ...

首先,Neo4j 甚至给你节点 id 的概念正在消失.它是一个内部标识符,实际上恰好是磁盘上的文件偏移量.它可以改变.它是低水平的.如果您考虑一下 SQL,您是否使用 SQL 查询来获取行的文件字节偏移量,然后将其引用以备将来更新?答:没有;您编写了一个查询,可以一次查找并操作该行.

First up, the concept of Neo4j even giving you the node id back is going away. It's an internal identifier that actually happens to be a file offset on disk. It can change. It is low level. If you think about SQL for a second, do you use a SQL query to get the file byte offset of a row, then reference that for future updates? A: No; you write a query that finds and manipulates the row all in one hit.

现在,我注意到您已经在节点上拥有 Nguid 属性.为什么你不能用它作为id?或者,如果名称始终是唯一的,请使用它?(域相关的 id 总是比幻数更可取.)如果两者都不合适,你可能想看看像 SnowMaker 这样的项目 来帮助你.

Now, I notice that you already have an Nguid property on the nodes. Why can't you use that as the id? Or if the name is always unique, use that? (Domain relevant ids are always preferable to magic numbers.) If neither are appropriate, you might want to look at a project like SnowMaker to help you out.

接下来,我们需要看看索引.您使用的索引类型在 2.0 文档中称为 "Legacy Indexing" 并错过了一些很酷的 Neo4j 2.0 功能.

Next, we need to look at indexing. The type of indexing that you're using is referred to in the 2.0 docs as "Legacy Indexing" and misses out on some of the cool Neo4j 2.0 features.

对于这个答案的其余部分,我将假设您的 Category 类如下所示:

For the rest of this answer, I'm going to assume your Category class looks like this:

public class Category
{
    public Guid UniqueId { get; set; }
    public string Name { get; set; }
}

让我们从创建带有 标签的类别节点开始:

Let's start by creating our category node with a label:

var category = new Category { UnqiueId = Guid.NewGuid(), Name = "Spanners" };
graphClient.Cypher
    .Create("(category:Category {category})")
    .WithParams(new { category })
    .ExecuteWithoutResults();

而且,作为一次性操作,让我们建立一个 schema-基于具有 Category 标签的任何节点的 Name 属性的索引:

And, as a one-time operation, let's establish a schema-based index on the Name property of any nodes with the Category label:

graphClient.Cypher
    .Create("INDEX ON :Category(Name)")
    .ExecuteWithoutResults();

现在,我们无需担心手动更新索引.

Now, we don't need to worry about manually keeping indexes up to date.

我们还可以在 唯一约束>唯一标识:

We can also introduce an index and unique constraint on UniqueId:

graphClient.Cypher
    .Create("CONSTRAINT ON (category:Category) ASSERT category.UniqueId IS UNIQUE")
    .ExecuteWithoutResults();

查询现在非常简单:

graphClient.Cypher
    .Match("(c:Category)")
    .Where((Category c) => c.UniqueId == someGuidVariable)
    .Return(c => c.As<Category>())
    .Results
    .Single();

与其查找类别节点,然后再执行另一个查询,只需一次性完成:

Rather than looking up a category node, to then do another query, just do it all in one go:

var productsInCategory = graphClient.Cypher
    .Match("(c:Category)<-[:IN_CATEGORY]-(p:Product)")
    .Where((Category c) => c.UniqueId == someGuidVariable)
    .Return(p => p.As<Product>())
    .Results;

如果您想更新一个类别,也可以一次性完成:

If you want to update a category, do that in one go as well:

graphClient.Cypher
    .Match("(c:Category)")
    .Where((Category c) => c.UniqueId == someGuidVariable)
    .Update("c = {category}")
    .WithParams(new { category })
    .ExecuteWithoutResults();

最后,您的 CategoryAdd 方法当前 1) 执行一次 DB 命中以查找现有节点,2) 第二次 DB 命中以创建新节点,3) 第三次 DB 命中以更新 ID在上面.相反,您也可以使用 MERGE 将所有这些压缩为单个调用 关键字:

Finally, your CategoryAdd method currently 1) does one DB hit to find an existing node, 2) a second DB hit to create a new one, 3) a third DB hit to update the ID on it. Instead, you can compress all of this to a single call too using the MERGE keyword:

public Category GetOrCreateCategoryByName(string name)
{
    return graphClient.Cypher
        .WithParams(new {
            name,
            newIdIfRequired = Guid.NewGuid()
        })
        .Merge("(c:Category { Name = {name})")
        .OnCreate("c")
        .Set("c.UniqueId = {newIdIfRequired}")
        .Return(c => c.As<Category>())
        .Results
        .Single();
}

基本上,

  1. 不要使用 Neo4j 的内部 ID 作为破解管理您自己身份的方法.(但他们可能会在未来发布某种形式的自动编号.即使他们这样做,域标识,如电子邮件地址或 SKU 或机场代码或......是首选.您甚至并不总是需要一个 id:您通常可以推断出一个节点基于其在图中的位置.)

  1. Don't use Neo4j's internal ids as a way to hack around managing your own identities. (But they may release some form of autonumbering in the future. Even if they do, domain identities like email addresses or SKUs or airport codes or ... are preferred. You don't even always need an id: you can often infer a node based on its position in the graph.)

通常,Node 会随着时间的推移而消失.如果您现在使用它,您只会累积遗留代码.

Generally, Node<T> will disappear over time. If you use it now, you're just accruing legacy code.

研究标签和基于架构的索引.它们会让您的生活更轻松.

Look into labels and schema-based indexing. They will make your life easier.

尝试在一个查询中执行操作.它会快得多.

Try and do things in the one query. It will be much faster.

希望有帮助!

这篇关于如何在 Neo4j v2 中使用 Neo4jClient 创建节点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆