问题描述
总结
我正在尝试使用 .NET 中的 Neo4jClient 包从 Neo4j 获取 path
。我想知道如何反序列化它,同时保持 table
响应中的强大值,但我似乎只能访问 text
响应。
免责声明 - 我见过 this answer,但它已经超过 7 年了,几乎不再相关了——更不用说最终结果看起来令人难以置信。
技术版本
- Neo4j - 4.2.3
- APOC - 4.2.0.1
- Neo4jClient - 4.1.5
- .NET 框架 - 4.7.2
数据结构
我有一个 Neo4j 数据库,其中包含三种类型的节点。为了回答这个问题,我将把它们归结为一个公司示例,因此我们将节点标签称为 Employee
、Department
和 Project
。这些节点通过以下方式互连:
- 每个
Employee
都与EMPLOYED_BY
有Department
关系。 - 每个
Project
都与OWNED_BY
有Department
关系。 -
Employee
可以与WORKS_ON
有Project
关系。
示例数据
为了这个问题,这个示例数据提供了数据结构的基线演示。
(:Employee {name:"Sarah Bradshaw"})
-[:EMPLOYED_BY {startDate:"2020-01-01"}]->
(:Department {name:"Finance"})
<-[:OWNED_BY {startDate:"2020-01-01"}]-
(:Project {name:"Quarterly Earnings"})
<-[:WORKS_ON {startDate:"2020-06-01"}]-
(:Employee {name:"Thomas Mitchell"})
-[:EMPLOYED_BY {startDate:"2019-01-01"}]->
(:Department {name:"Administration"})
密码查询
这是我试图在 .NET 中使用 Neo4jClient 包复制的查询。
MATCH (from:Employee {name:"Sarah Bradshaw"})
MATCH (to:Employee {name:"Thomas Mitchell"})
CALL apoc.algo.dijkstra(from,to,'','d')
YIELD path
RETURN path
回复
表格响应
{
"start": {
"identity": 0,"labels": [ "Employee" ],"properties": {
"name": "Sarah Bradshaw"
}
},"end": {
"identity": 3,"properties": {
"name": "Thomas Mitchell"
}
},"segments": [
{
"start": {
"identity": 0,"properties": {
"name": "Sarah Bradshaw"
}
},"relationship": {
"identity": 0,"start": 0,"end": 1,"type": "EMPLOYED_BY","properties": {
"startDate": "2020-01-01"
}
},"end": {
"identity": 1,"labels": [ "Department" ],"properties": {
"name": "Finance"
}
}
},{
"start": {
"identity": 1,"properties": {
"name": "Finance"
}
},"relationship": {
"identity": 1,"start": 2,"type": "OWNED_BY","end": {
"identity": 2,"labels": [ "Project" ],"properties": {
"name": "Quarterly Earnings"
}
}
},{
"start": {
"identity": 2,"properties": {
"name": "Quarterly Earnings"
}
},"relationship": {
"identity": 2,"start": 3,"end": 2,"type": "WORKS_ON","properties": {
"startDate": "2020-06-01"
}
},"end": {
"identity": 3,"properties": {
"name": "Thomas Mitchell"
}
}
}
],"length": 3.0
}
文本回复
[
{"name":"Sarah Bradshaw"},{"startDate":"2020-01-01"},{"name":"Finance"},{"name":"Quarterly Earnings"},{"startDate":"2020-06-01"},{"name":"Thomas Mitchell"}
]
如您所见,text
响应基本上没有用。不幸的是,这似乎是我能够通过 Neo4jClient 检索到的唯一响应值。
Neo4jClient 查询
这是基于上述查询派生的 Neo4jClient 语法。因为我只能获取 text
响应,所以我将其反序列化为 List
类型的 Datanode
- 一个反映节点结构及其关系的简单模型。
client.Cypher
.Match("(from:Employee {name:\"Sarah Bradshaw\"})")
.Match("(to:Employee {name:\"Thomas Mitchell\"})")
.Call("apoc.algo.dijkstra(from,'d')")
.Yield("path")
.Return<List<Datanode>>("path")
.ResultsAsync
.Result;
底线
虽然这确实给我带来了一些东西,但问题是在 text
响应中没有返回任何使路径相关的东西。我有一组节点和关系,但我不知道它们是如何相互关联的。 table
响应列出了开始节点和结束节点以及那我关心的信息。有什么方法可以让我查询 table
响应而不是 text
响应吗?
解决方法
好的,我已经假设您正在使用 BoltGraphClient
- 与 GraphClient
一样,您几乎被卡住了,因为 REST 端点没有给出您想要的 ID。
PathsResultBolt
中有一个名为 Neo4jClient
的类 - 但是 它给出 System.Object
作为查询结果中每个属性的答案 - 即不太有用,所以你应该试试这个类:
public class PathsResultBolt<TNode,TRel>
{
public PathsResultBolt()
{
Nodes = new List<PathsResultBoltNode<TNode>>();
Relationships = new List<PathsResultBoltRelationship<TRel>>();
}
internal PathsResultBolt(IPath path)
{
Start = new PathsResultBoltNode<TNode>(path.Start);
End = new PathsResultBoltNode<TNode>(path.End);
Relationships = path.Relationships.Select(r => new PathsResultBoltRelationship<TRel>(r)).ToList();
Nodes = path.Nodes.Select(r => new PathsResultBoltNode<TNode>(r)).ToList();
}
[JsonProperty("Start")]
public PathsResultBoltNode<TNode> Start { get; set; }
[JsonProperty("End")]
public PathsResultBoltNode<TNode> End { get; set; }
[JsonIgnore]
public int Length => Relationships.Count();
[JsonProperty("Nodes")]
public List<PathsResultBoltNode<TNode>> Nodes { get; set; }
[JsonProperty("Relationships")]
public List<PathsResultBoltRelationship<TRel>> Relationships { get; set; }
public class PathsResultBoltRelationship<T>
{
public long Id { get; set; }
public string Type { get; set; }
public long StartNodeId { get; set; }
public long EndNodeId { get; set; }
public object this[string key] => Properties[key];
public Dictionary<string,T> Properties { get; set; }
public PathsResultBoltRelationship() { Properties = new Dictionary<string,T>(); }
public PathsResultBoltRelationship(IRelationship relationship)
{
Id = relationship.Id;
StartNodeId = relationship.StartNodeId;
EndNodeId = relationship.EndNodeId;
Type = relationship.Type;
Properties = relationship.Properties.ToDictionary(kvp => kvp.Key,kvp => kvp.Value.As<T>());
}
public bool Equals(PathsResultBoltRelationship<T> other)
{
if (other == null)
return false;
return Id == other.Id
&& StartNodeId == other.StartNodeId
&& EndNodeId == other.EndNodeId
&& Type == other.Type
&& Properties.ContentsEqual(other.Properties);
}
public override bool Equals(object obj)
{
return Equals(obj as PathsResultBoltRelationship<T>);
}
public override int GetHashCode()
{
var hashCode = 2105322407;
hashCode = hashCode * -1521134295 + Id.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Type);
hashCode = hashCode * -1521134295 + StartNodeId.GetHashCode();
hashCode = hashCode * -1521134295 + EndNodeId.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<IReadOnlyDictionary<string,T>>.Default.GetHashCode(Properties);
return hashCode;
}
}
public class PathsResultBoltNode<T>
{
public long Id { get; set; }
public List<string> Labels { get; set; }
public object this[string key] => Properties[key];
public Dictionary<string,T> Properties { get; set; }
public PathsResultBoltNode() { Properties = new Dictionary<string,T>(); }
internal PathsResultBoltNode(INode node)
{
Id = node.Id;
Labels = node.Labels?.ToList();
Properties = node.Properties.ToDictionary(kvp => kvp.Key,kvp => kvp.Value.As<T>());
}
public bool Equals(PathsResultBoltNode<T> other)
{
if (other == null)
return false;
return Id == other.Id
&& Labels.ContentsEqual(other.Labels)
&& Properties.ContentsEqual(other.Properties);
}
public override bool Equals(object obj)
{
return Equals(obj as PathsResultBoltNode<T>);
}
public override int GetHashCode()
{
var hashCode = 1343812023;
hashCode = hashCode * -1521134295 + Id.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<IReadOnlyList<string>>.Default.GetHashCode(Labels);
hashCode = hashCode * -1521134295 + EqualityComparer<IReadOnlyDictionary<string,T>>.Default.GetHashCode(Properties);
return hashCode;
}
}
}
你会像这样使用:
client.Cypher
.Match("(from:Employee {name:\"Sarah Bradshaw\"})")
.Match("(to:Employee {name:\"Thomas Mitchell\"})")
.Call("apoc.algo.dijkstra(from,to,'','d')")
.Yield("path")
.Return<PathsResultBolt<string,string>>("path")
.ResultsAsync
.Result;
你问这个<string,string>
是什么?第一个是节点的属性类型,第二个是关系。
现在。这有点垃圾 - 这归结为我不确定如何使实际对象值正确 objects
工作的事实。因此,目前最好的方法 (!!) 是对 node 和 rels 使用 string
。在我使用标准电影数据库的测试中,我得到了这个:
born
属性是 string
但这是基本级别,使用 int
会导致错误,因为 name
not 是 int .