- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
在 Neo4j v1.9.x 下,我使用了以下代码。
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 是自动生成的,我可以稍后使用它进行快速查找、其他查询中的起始位等。如下所示:
private Node<Category> CategoryGet(long nodeId)
{
return client.Get<Category>((NodeReference<Category>)nodeId);
}
这使得以下看起来运行良好。
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()。
所以我的问题是您如何获得节点 ID 的自动增量值?
最佳答案
首先,对于 Neo4j 2 及更高版本,您始终需要从引用框架“我将如何在 Cypher 中执行此操作?”开始。然后,也只有那时,您才担心 C#。
现在,提炼您的问题,听起来您的主要目标是创建一个节点,然后返回对它的引用以供进一步工作。
你可以用密码来做到这一点:
CREATE (myNode)
RETURN myNode
在 C# 中,这将是:
var categoryNode = graphClient.Cypher
.Create("(category {cat})")
.WithParams(new { cat })
.Return(cat => cat.Node<Category>())
.Results
.Single();
但是,这仍然不是您在原始 CreateNodeCategory
中所做的 100%方法。您正在数据库中创建节点,为其获取 Neo4j 的内部标识符,然后将该标识符保存回同一节点。基本上,您正在使用 Neo4j 为您生成自动递增的数字。这是功能性的,但不是真正的好方法。我会解释更多...
首先,Neo4j 甚至还给您节点 ID 的概念正在消失。它是一个内部 标识符,实际上恰好是磁盘上的文件偏移量。它可以改变。这是低水平的。如果您稍微考虑一下 SQL,您是否使用 SQL 查询来获取行的文件字节偏移量,然后引用它以供将来更新?答:没有;您编写一个查询,一次性查找和操作该行。
现在,我注意到您已经有了一个 Nguid
节点上的属性。你为什么不能用它作为id?或者,如果名称始终是唯一的,则使用该名称? (与域相关的 ID 总是比魔数(Magic Number)更可取。)如果两者都不合适,您可能需要查看像 SnowMaker 这样的项目。来帮助你。
接下来,我们需要看看索引。您正在使用的索引类型在 2.0 文档中称为 "Legacy Indexing"并错过了一些很酷的 Neo4j 2.0 功能。
对于这个答案的其余部分,我将假设您的 Category
类看起来像这样:
public class Category
{
public Guid UniqueId { get; set; }
public string Name { get; set; }
}
让我们开始使用 label 创建类别节点:
var category = new Category { UnqiueId = Guid.NewGuid(), Name = "Spanners" };
graphClient.Cypher
.Create("(category:Category {category})")
.WithParams(new { category })
.ExecuteWithoutResults();
并且,作为一次性操作,让我们建立一个 schema-based index在 Name
上具有 Category
的任何节点的属性标签:
graphClient.Cypher
.Create("INDEX ON :Category(Name)")
.ExecuteWithoutResults();
现在,我们无需担心手动使索引保持最新。
我们还可以引入索引和unique constraint在 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();
与其查找类别节点,然后再进行另一个查询,不如一次性完成:
var productsInCategory = graphClient.Cypher
.Match("(c:Category)<-[:IN_CATEGORY]-(p:Product)")
.Where((Category c) => c.UniqueId == someGuidVariable)
.Return(p => p.As<Product>())
.Results;
如果你想更新一个类别,也可以一次性完成:
graphClient.Cypher
.Match("(c:Category)")
.Where((Category c) => c.UniqueId == someGuidVariable)
.Update("c = {category}")
.WithParams(new { category })
.ExecuteWithoutResults();
最后,你的 CategoryAdd
当前方法 1) 执行一次数据库命中以查找现有节点,2) 第二次数据库命中以创建新节点,3) 第三次数据库命中以更新其上的 ID。相反,您也可以使用 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();
}
基本上,
不要使用 Neo4j 的内部 ID 作为破解管理您自己身份的方法。 (但他们可能会在未来发布某种形式的自动编号。即使他们这样做了,像电子邮件地址或 SKU 或机场代码或......这样的域身份是首选。你甚至并不总是需要一个 id:你通常可以推断出一个节点基于其在图中的位置。)
一般来说,Node<T>
会随着时间消失。如果您现在使用它,您只是在积累遗留代码。
研究标签和基于模式的索引。它们会让您的生活更轻松。
尝试在一个查询中做事。会快很多。
希望对您有所帮助!
关于c# - 如何在 Neo4j v2 中使用 Neo4jClient 创建节点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19534511/
我有一个 k*n矩阵 X 和 k*k矩阵A。对于X的每一列,我想计算标量 X[:, i].T.dot(A).dot(X[:, i]) (或者,数学上, Xi' * A * Xi )。 目前,我有一个
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我是 VueJS 的新手。我已经使用 vuetify/webpack-ssr 模板创建了一个项目,现在我想创建一个登录页面,但是没有显示表单,控制台给了我以下信息: [Vue warn]: Unkno
我尝试将 value 插入到 C++ vector v 之前的第 i 元素(或元素 (i-1) 之后) )。代码很简单 v.insert(v.begin() + i, value); 我确信当 i 介
我需要显示使用合并排序算法排序的 vector 。然而,当我使用 v.begin() 时,我的 friend 使用 v.data() 来传递 vector 。他的代码运行良好,而我的却不行。请解释。
这是我的命令(url1、url2、url3、url4 是占位符): ffmpeg -i url1 -i url2 -i url3 -i url4 -filter_complex “[1:v:0] [1
我以前用过Vue,我知道怎么用v-for渲染元素序列,v-if或v-show有条件地显示元素,并且 v-model例如,控制段落的内容。 但现在我需要对 DOM 进行更精细的控制: 我有一个range
我正在学习所有权和借用。 borrow1 和borrow2 的区别在于在borrow2 打印时使用了&: fn borrow1(v: &Vec) { println!("{}", &v[10]
我找不到一种方法来选择不同的选项来渲染 v-for 中的文本。是否有可能或者我是否需要以不同的方式构建逻辑来执行类似于下面的代码的操作? // i
Iterable 的三个直接子类型是 Map , Seq , 和 Set .除了性能问题之外,似乎还有一个 Seq是从整数到值的映射,以及 Set是从值到 bool 值的映射(如果值在集合中,则为 t
我想应用一个计算方法,如果键存在则增加值,否则将 1。有 Map map = new HashMap<>(); 我不明白为什么 for (int i = 0; i v != null ? v++ :
标准(IEEE 754/C)是否保证以下代码断言永远不会失败? int main() { for ( /* all possible float / double values */ )
代码由Vue语言编写,使用Element-ui框架, 如果一个对象包含某些内容,则会显示该内容,如果不包含则禁用菜单按钮。 输出应该是这样的: a、b(禁用)、c、d、e 但我的是这样的: a、a(禁
如果我这样做: {{ morevalue }} {{ value }} v-else 中的跨度也会在第二个 V-FOR 上循环,即使它上面没有任何 v-for,为什么? 这是
如果我这样做: {{ morevalue }} {{ value }} v-else 中的跨度也会在第二个 V-FOR 上循环,即使它上面没有任何 v-for,为什么? 这是
我将 Vue.js 与 Vuetify 一起使用,我正在尝试使用 v-data-table 从后端加载菜单列表并使用 对其设置一些权限v-switches 但我在尝试 v-model 数组时遇到问题:
我在 Java 的流式操作中努力维护我想要的数据结构,这很可能是由于缺乏正确的理解和实践。 public class Main { public static void main(String
我正在尝试为匹配中的每个匹配呈现一些 HTML,但是,我不太确定 实际上是正确的。 更具体地说,我不确定我是否可以使用 v-bind:match='match'在与循环相同的元素上 v-for='ma
所以我想知道为什么这个 v-if 和 v-else 语句不起作用,为什么我要以不同的方式解决它。 代码如下 Required: Select a Workflow {{ isChain ?
我有一个 VueJS 组件 ,我在同一个模板中使用了两次来显示两组不同的数据。每个都显示在自己的 使用 v-if 切换的容器在导航选项卡上。 似乎这些组件被实例化为同一个实例。我调用 console
我是一名优秀的程序员,十分优秀!