繁体   English   中英

如何在 .NET 中使用 Neo4jClient 返回 Neo4j 路径?

[英]How do I return a Neo4j path with Neo4jClient in .NET?

概括

我正在尝试使用 .NET 中的 Neo4jClient package 从 Neo4j 获取path 我想知道如何反序列化它,同时保持table响应中的强大值,但我似乎只能访问text响应。

免责声明- 我已经看到了这个答案,但它已经超过 7 年了,几乎不再相关——更不用说最终结果看起来令人难以置信的复杂。

技术版本

  • Neo4j - 4.2.3
  • APOC - 4.2.0.1
  • Neo4jClient - 4.1.5
  • .NET 框架- 4.7.2

数据结构

我有一个 Neo4j 数据库,其中包含三种类型的节点。 为了这个问题,我将把它们简化为一个公司示例,因此我们将节点标签称为EmployeeDepartmentProject 这些节点通过以下方式互连:

  • 每个Employee都与DepartmentEMPLOYED_BY关系。
  • 每个ProjectDepartment都有OWNED_BY关系。
  • Employee可以与Project具有WORKS_ON关系。

样本数据

为了这个问题,这个样本数据提供了数据结构的基线演示。

(: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"})

上述数据的可视化表示。

Cypher查询

这是我尝试使用 Neo4jClient package 在 .NET 中复制的查询。

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,
        "labels": [ "Employee" ],
        "properties": {
            "name": "Thomas Mitchell"
        }
    },
    "segments": [
        {
            "start": {
                "identity": 0,
                "labels": [ "Employee" ],
                "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,
                "labels": [ "Department" ],
                "properties": {
                    "name": "Finance"
                }
            },
            "relationship": {
                "identity": 1,
                "start": 2,
                "end": 1,
                "type": "OWNED_BY",
                "properties": {
                    "startDate": "2020-01-01"
                }
            },
            "end": {
                "identity": 2,
                "labels": [ "Project" ],
                "properties": {
                    "name": "Quarterly Earnings"
                }
            }
        },
        {
            "start": {
                "identity": 2,
                "labels": [ "Project" ],
                "properties": {
                    "name": "Quarterly Earnings"
                }
            },
            "relationship": {
                "identity": 2,
                "start": 3,
                "end": 2,
                "type": "WORKS_ON",
                "properties": {
                    "startDate": "2020-06-01"
                }
            },
            "end": {
                "identity": 3,
                "labels": [ "Employee" ],
                "properties": {
                    "name": "Thomas Mitchell"
                }
            }
        }
    ],
    "length": 3.0
}

文本回复

[
    {"name":"Sarah Bradshaw"},
    {"startDate":"2020-01-01"},
    {"name":"Finance"},
    {"name":"Finance"},
    {"startDate":"2020-01-01"},
    {"name":"Quarterly Earnings"},
    {"name":"Quarterly Earnings"},
    {"startDate":"2020-06-01"},
    {"name":"Thomas Mitchell"}
]

如您所见, text响应基本上是无用的。 不幸的是,这似乎是我能够通过 Neo4jClient 检索的唯一响应值。

Neo4jClient 查询

这是基于上述查询的派生 Neo4jClient 语法。 因为我只能获取text响应,所以我将其反序列化为DataNode类型的List ——一个简单的 model,它反映了节点的结构及其关系。

client.Cypher
.Match("(from:Employee {name:\"Sarah Bradshaw\"})")
.Match("(to:Employee {name:\"Thomas Mitchell\"})")
.Call("apoc.algo.dijkstra(from, to, '', 'd')")
.Yield("path")
.Return<List<DataNode>>("path")
.ResultsAsync
.Result;

底线

虽然这确实让我有所收获,但问题是在text响应中没有返回任何使路径相关的东西。 我有一组节点和关系,但我不知道它们是如何互连的。 table响应列出了开始节点和结束节点,这就是我关心的信息。 我有什么办法可以查询table响应而不是text响应?

好的,我假设您正在使用BoltGraphClient - 与GraphClient一样,您几乎被卡住了,因为 REST 端点没有给出您所追求的 ID。

Neo4jClient 中有一个名为PathsResultBoltNeo4jClient -它给出System.Object作为查询结果中每个属性的答案 - 这不太有用,所以你应该试试这个 ZA2F2ED4FDC4EBC2CBB4ZC21:

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>是什么? 第一个是节点的属性类型,第二个是关系。

现在。 这有点垃圾 - 这取决于我不确定如何让实际的 object 值成为有效的正确objects 所以最好的方法(!!)是目前对 node 和 rels 都使用string 在我使用标准电影数据库的测试中,我得到了这个:

LinqPad 中的查询结果

born属性是一个string ,但这是基本级别,使用int会导致错误,因为name不是int。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM