微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在 .NET 中使用 Neo4jClient 返回 Neo4j 路径? 总结技术版本数据结构示例数据密码查询回复Neo4jClient 查询底线

如何解决如何在 .NET 中使用 Neo4jClient 返回 Neo4j 路径? 总结技术版本数据结构示例数据密码查询回复Neo4jClient 查询底线

总结

我正在尝试使用 .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 数据库,其中包含三种类型的节点。为了回答这个问题,我将把它们归结为一个公司示例,因此我们将节点标签称为 EmployeeDepartmentProject。这些节点通过以下方式互连:

  • 每个 Employee 都与 EMPLOYED_BYDepartment 关系。
  • 每个 Project 都与 OWNED_BYDepartment 关系。
  • Employee 可以与 WORKS_ONProject 关系。

示例数据

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

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

A visual representation of the above data.

密码查询

这是我试图在 .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。在我使用标准电影数据库的测试中,我得到了这个:

Results of the query in LinqPad

born 属性是 string 但这是基本级别,使用 int 会导致错误,因为 name not 是 int .

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。