问题:查询结果Dto中包含复杂对象属性,节点中对应字段存储的为json,在查询使用apoc函数将json转为对象后,疑似替换了本该操作数据,导致字段映射丢失
节点实例:
接收dto
@Data
public class OperationHistoryDto {
@Schema(name = "节点名称")
private String name;
@Schema(name = "节点候选人")
private List<UserBaseInfo> candidate;
@Schema(name = "节点操作人")
private UserBaseInfo operator;
@Schema(name = "操作结果", description = "1-待办,2-同意,3-拒绝,4-转发,5-终止")
private Integer operationResult;
@Schema(name = "操作备注")
private String operationRemark;
@Schema(name = "节点开始时间")
@JsonFormat(pattern = TimeFormat.DATE_TIME)
private LocalDateTime beginTime;
@Schema(name = "节点结束时间")
@JsonFormat(pattern = TimeFormat.DATE_TIME)
private LocalDateTime endTime;
@Schema(name = "节点持续时间")
private String during;
}
UserBaseInfo
@Data
public class UserBaseInfo {
@Schema(name = "唯一标识")
private String id;
@Schema(name = "名称")
private String name;
}
查询
@Query("""
match (:Instance)-[b:BUSINESS{key:$0}]->(i:InstanceNode)
with i, case when $1 is null then b.num else $1 end as length
call apoc.path.expandConfig(i, {
relationshipFilter: "NEXT>",
minLevel: length-1,
maxLevel: length-1,
limit: 1
}) yield path
with nodes(path) as nodes
unwind nodes as n
with n
return n.name as name,
n.status as operationResult,
apoc.convert.fromJsonList(n.operationCandidate) as candidate,
apoc.convert.fromJsonMap(n.operationBy) as operator,
n.operationRemark as operationRemark,
n.beginTime as beginTime, n.endTime as endTime,
n.during as during
""")
List<OperationHistoryDto> queryInstanceOperationHistory(String businessKey, Integer num);
首先根据查询日志,确定cypher是没有问题的,neo4j能查询到正确的数据
o.n.d.i.a.i.InboundMessageDispatcher : [0xd53f5894][localhost:7687][bolt-76] S: SUCCESS {t_first=59, fields=["name", "operationResult", "candidate", "operator", "operationRemark", "beginTime", "endTime", "during"], qid=0}
o.n.d.i.a.i.InboundMessageDispatcher : [0xd53f5894][localhost:7687][bolt-76] S: RECORD ["发起", 2, NULL, {name: "张三", id: "2"}, NULL, 2024-05-04T15:35:32.924799500, 2024-05-04T15:35:32.929782900, "0S"]
o.n.d.i.a.i.InboundMessageDispatcher : [0xd53f5894][localhost:7687][bolt-76] S: RECORD ["中间节点1", 2, [{name: "张三", id: "2"}], {name: "张三", id: "2"}, NULL, 2024-05-04T15:35:32.947727200, 2024-05-04T15:38:54.385267300, "3M21S"]
o.n.d.i.a.i.InboundMessageDispatcher : [0xd53f5894][localhost:7687][bolt-76] S: RECORD ["中间节点2", 2, [{name: "张三", id: "2"}], {name: "张三", id: "2"}, NULL, 2024-05-04T15:38:54.389249100, 2024-05-04T15:49:20.711446100, "10M26S"]
o.n.d.i.a.i.InboundMessageDispatcher : [0xd53f5894][localhost:7687][bolt-76] S: RECORD ["中间节点3", 2, [{name: "张三", id: "2"}], {name: "张三", id: "2"}, NULL, 2024-05-04T15:49:20.734369500, 2024-05-04T15:50:37.812655400, "1M17S"]
o.n.d.i.a.i.InboundMessageDispatcher : [0xd53f5894][localhost:7687][bolt-76] S: RECORD ["中间节点4", 2, [{name: "张三", id: "2"}], {name: "张三", id: "2"}, NULL, 2024-05-04T15:50:37.821625400, 2024-05-04T15:57:07.582434300, "6M29S"]
o.n.d.i.a.i.InboundMessageDispatcher : [0xd53f5894][localhost:7687][bolt-76] S: RECORD ["完成", 2, NULL, {name: "system", id: "system"}, NULL, 2024-05-04T15:57:07.603369, 2024-05-04T15:57:07.775788300, "0S"]
但此时的返回结果中,name被替换成了"张三",beginTime、endTime、during却为空
[
{
"name": "张三",
"candidate": null,
"operator": {
"id": "2",
"name": "张三"
},
"operationResult": 2,
"operationRemark": null,
"beginTime": null,
"endTime": null,
"during": null
},
{
"name": "张三",
"candidate": [
{
"id": "2",
"name": "张三"
}
],
"operator": {
"id": "2",
"name": "张三"
},
"operationResult": 2,
"operationRemark": null,
"beginTime": null,
"endTime": null,
"during": null
},
{
"name": "张三",
"candidate": [
{
"id": "2",
"name": "张三"
}
],
"operator": {
"id": "2",
"name": "张三"
},
"operationResult": 2,
"operationRemark": null,
"beginTime": null,
"endTime": null,
"during": null
},
{
"name": "张三",
"candidate": [
{
"id": "2",
"name": "张三"
}
],
"operator": {
"id": "2",
"name": "张三"
},
"operationResult": 2,
"operationRemark": null,
"beginTime": null,
"endTime": null,
"during": null
},
{
"name": "张三",
"candidate": [
{
"id": "2",
"name": "张三"
}
],
"operator": {
"id": "2",
"name": "张三"
},
"operationResult": 2,
"operationRemark": null,
"beginTime": null,
"endTime": null,
"during": null
},
{
"name": "system",
"candidate": null,
"operator": {
"id": "system",
"name": "system"
},
"operationResult": 2,
"operationRemark": null,
"beginTime": null,
"endTime": null,
"during": null
}
]
注释掉apoc.convert.fromJsonMap(n.operationBy) as operator
后,恢复正常
[
{
"name": "发起",
"candidate": null,
"operator": null,
"operationResult": 2,
"operationRemark": null,
"beginTime": "2024-05-04 15:35:32",
"endTime": "2024-05-04 15:35:32",
"during": "0S"
},
{
"name": "中间节点1",
"candidate": [
{
"id": "2",
"name": "张三"
}
],
"operator": null,
"operationResult": 2,
"operationRemark": null,
"beginTime": "2024-05-04 15:35:32",
"endTime": "2024-05-04 15:38:54",
"during": "3M21S"
},
{
"name": "中间节点2",
"candidate": [
{
"id": "2",
"name": "张三"
}
],
"operator": null,
"operationResult": 2,
"operationRemark": null,
"beginTime": "2024-05-04 15:38:54",
"endTime": "2024-05-04 15:49:20",
"during": "10M26S"
},
{
"name": "中间节点3",
"candidate": [
{
"id": "2",
"name": "张三"
}
],
"operator": null,
"operationResult": 2,
"operationRemark": null,
"beginTime": "2024-05-04 15:49:20",
"endTime": "2024-05-04 15:50:37",
"during": "1M17S"
},
{
"name": "中间节点4",
"candidate": [
{
"id": "2",
"name": "张三"
}
],
"operator": null,
"operationResult": 2,
"operationRemark": null,
"beginTime": "2024-05-04 15:50:37",
"endTime": "2024-05-04 15:57:07",
"during": "6M29S"
},
{
"name": "完成",
"candidate": null,
"operator": null,
"operationResult": 2,
"operationRemark": null,
"beginTime": "2024-05-04 15:57:07",
"endTime": "2024-05-04 15:57:07",
"during": "0S"
}
]
恢复注释的语句后,将Neo4jRepository<InstanceNode,Long> 替换为Neo4jRepository<Instance,Long>,Instance与UserBaseInfo只有一个name属性同名,此时的查询结果,只有name被替换
[
{
"name": "张三",
"candidate": null,
"operator": {
"id": "2",
"name": "张三"
},
"operationResult": 2,
"operationRemark": null,
"beginTime": "2024-05-04 15:35:32",
"endTime": "2024-05-04 15:35:32",
"during": "0S"
},
{
"name": "张三",
"candidate": [
{
"id": "2",
"name": "张三"
}
],
"operator": {
"id": "2",
"name": "张三"
},
"operationResult": 2,
"operationRemark": null,
"beginTime": "2024-05-04 15:35:32",
"endTime": "2024-05-04 15:38:54",
"during": "3M21S"
},
{
"name": "张三",
"candidate": [
{
"id": "2",
"name": "张三"
}
],
"operator": {
"id": "2",
"name": "张三"
},
"operationResult": 2,
"operationRemark": null,
"beginTime": "2024-05-04 15:38:54",
"endTime": "2024-05-04 15:49:20",
"during": "10M26S"
},
{
"name": "张三",
"candidate": [
{
"id": "2",
"name": "张三"
}
],
"operator": {
"id": "2",
"name": "张三"
},
"operationResult": 2,
"operationRemark": null,
"beginTime": "2024-05-04 15:49:20",
"endTime": "2024-05-04 15:50:37",
"during": "1M17S"
},
{
"name": "张三",
"candidate": [
{
"id": "2",
"name": "张三"
}
],
"operator": {
"id": "2",
"name": "张三"
},
"operationResult": 2,
"operationRemark": null,
"beginTime": "2024-05-04 15:50:37",
"endTime": "2024-05-04 15:57:07",
"during": "6M29S"
},
{
"name": "system",
"candidate": null,
"operator": {
"id": "system",
"name": "system"
},
"operationResult": 2,
"operationRemark": null,
"beginTime": "2024-05-04 15:57:07",
"endTime": "2024-05-04 15:57:07",
"during": "0S"
}
]
暂未查询到具体原因,目前只是猜想在Spring Data Neo4j中,如果返回结果中有包含一个完整的对象和其他数据结果,那么Neo4jRepository设置的类所同名的字段,会被替换为该对象同名字段的值
后续的测试
用Neo4jRepository<Instance,Long>尝试返回一个Version节点,Version与Instance没有任何同名的字段:
@Query("""
match (v:Version) return v limit 1
""")
Version v();
发现返回结果各字段都为空,可进一步完善猜想(实力有限,没去深挖底层代码):当Cypher直接返回(包含)一个节点或对象时,Neo4jRepository<X,x>会先用X的属性接收对象中的数据,如果此时还有其他返回的零散字段,将会选择X的同名属性,然后转为方法返回对象