spring-data-neo4j icon indicating copy to clipboard operation
spring-data-neo4j copied to clipboard

One node has two rels, but only return one rel when query all

Open liseri opened this issue 3 years ago • 5 comments

Issue

One node has two rels, but only return one rel when query all.

  • It is normal return, when query one use spring-data-neo4j.
  • It is normal return, when query one use cql in neo4j-browser.
  • It is normal return, when query all use cql in neo4j-browser.
  • ** It is not normal return**, when query all use spring-data-neo4j.

query one Log (It is normal)

the node 20049 has two rels like this RECORD [node<20049>, [relationship<20044>, relationship<20043>], [node<20047>, node<20048>]] as the last line below

...
C: RUN "MATCH (s:hktxjm)
WHERE s.model = 'hktxjmmodel9' and s.idd = 'node-7f532b96-bc60-47d9-a394-ca4b5c97d7d0' AND s.nodeStatu = 'ACTIVE'
optional MATCH (s)-[r]-(t:hktxjm)
WHERE type(r) in ['guanXi']
RETURN s,collect(r), collect(t)
" {} {}
... C: PULL {n=1000}
... S: SUCCESS {t_first=1, fields=["s", "collect(r)", "collect(t)"], qid=0}
... S: RECORD [node<20049>, [relationship<20044>, relationship<20043>], [node<20047>, node<20048>]]
...

query all Log (It is not normal)

the node 20049 has two rels like this RECORD [node<20049>, [relationship<20044>, relationship<20043>], [node<20047>, node<20048>]] as the last line below, **but in return results, only one rel, the outgoingEdges list has only one element **

...
RUN "MATCH (s:hktxjm)
WHERE s.model = $model AND s.nodeStatu = 'ACTIVE' 
WITH s  SKIP $skip LIMIT $limit 
optional MATCH (s)-[r]-(t:hktxjm)
WHERE type(r) in ['guanXi']
RETURN s,collect(r), collect(t)" {0="hktxjmmodel9", limit=100, model="hktxjmmodel9", skip=0} {}
2022-09-14 ... C: PULL {n=1000}
2022-09-14 ... S: SUCCESS {t_first=1, fields=["s", "collect(r)", "collect(t)"], qid=0}
2022-09-14 ... S: RECORD [node<20043>, [relationship<20042>, relationship<20040>, relationship<20041>], [node<20046>, node<20044>, node<20045>]]
2022-09-14 ... S: RECORD [node<20044>, [relationship<20040>], [node<20043>]]
2022-09-14 ... S: RECORD [node<20045>, [relationship<20041>], [node<20043>]]
2022-09-14 ... S: RECORD [node<20046>, [relationship<20042>], [node<20043>]]
2022-09-14 ... S: RECORD [node<20047>, [relationship<20044>], [node<20049>]]
2022-09-14 ... S: RECORD [node<20048>, [relationship<20043>], [node<20049>]]
2022-09-14 ... S: RECORD [node<20049>, [relationship<20044>, relationship<20043>], [node<20047>, node<20048>]]
...

Entity

@Node(primaryLabel="hktxjm")
@Getter
@Setter
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class TxjmNodeEntity extends TxjmCellEntity {
    @Id
    @GeneratedValue
    Long id;

    @NotNull
    @Size(min = 1, max = 50)
//    @Builder.Default
    private String idd;

    @NotNull
    @Size(min = 1, max = 50)
    private String model;

    @NotNull
    @ReadOnlyProperty
    @Size(min = 1, max = 999)
    private String nodeVersion;

    @NotNull
    private TxjmModel.TxjmCellStatu nodeStatu;

    @NotNull
    @Size(min = 1, max = 50)
    private String shape;

    @NotNull
    @Size(min = 1, max = 50)
    private String appType;

    @NotNull
    @Size(min = 1, max = 50)
    private String defaultName;

    @NotNull
    @Size(min = 1, max = 50)
    private String name;

    @Size(max = 100)
    private String description;

    @CompositeProperty
    // @Singular(ignoreNullCollections = true)
    private Map<String, String> datas;

    // @Singular(ignoreNullCollections = true)
    @JsonManagedReference
    @Relationship(type = "guanXi", direction = Relationship.Direction.OUTGOING)
    private List<TxjmEdgeEntity> outgoingEdges;
    ...

import data

you can use this cql to init data

with [{shape:"hktxjm.liuCheng",appType:"fuWuRW",defaultName:"服务任务",name:"测试服务任务2",description:"测试加的内容",`datas.placeholders`:"1",`datas.parentIdd`:"",`datas.extendField`:"其它1",idd:"node-16d062f1-2b70-11ed-ba00-6c4b907924b0",nodeVersion:"1",model:"hktxjmmodel9",nodeStatu:"ACTIVE"},{shape:"hktxjm.zhanLue",appType:"ceDu",defaultName:"测度",name:"测试测度2",description:"测试",`datas.placeholders`:"1",`datas.parentIdd`:"",idd:"node-16d8098e-2b70-11ed-b3be-6c4b907924b0",nodeVersion:"1",model:"hktxjmmodel9",nodeStatu:"ACTIVE"},{shape:"hktxjm.zhanLue",appType:"quDongL",defaultName:"驱动力",name:"测试驱动力2",description:"测试",`datas.placeholders`:"1",`datas.parentIdd`:"",idd:"node-16d089ef-2b70-11ed-9832-6c4b907924b0",nodeVersion:"1",model:"hktxjmmodel9",nodeStatu:"ACTIVE"},{shape:"hktxjm.zhanLue",appType:"liYiXGF",defaultName:"利益相关方",name:"利益相关方",`datas.placeholders`:"1",`datas.parentIdd`:"",`datas.extendField`:"其它1",idd:"node-5c6cb4d3-857c-41d9-908f-4ae68d5da60f",nodeVersion:"1",model:"hktxjmmodel9",nodeStatu:"ACTIVE"},{shape:"hktxjm.zhanLue",appType:"nengLi",defaultName:"能力",name:"能力",`datas.placeholders`:"1",`datas.parentIdd`:"node-5c6cb4d3-857c-41d9-908f-4ae68d5da60f",`datas.extendField`:"其它1",idd:"node-4fef4ea8-25f7-4e9c-8a20-752e99134ec8",nodeVersion:"1",model:"hktxjmmodel9",nodeStatu:"ACTIVE"},{shape:"hktxjm.zhanLue",appType:"nengLi",defaultName:"能力",name:"能力",`datas.placeholders`:"1",`datas.parentIdd`:"node-5c6cb4d3-857c-41d9-908f-4ae68d5da60f",`datas.extendField`:"其它1",idd:"node-9f252924-d128-4d31-b326-0f549452519d",nodeVersion:"1",model:"hktxjmmodel9",nodeStatu:"ACTIVE"},{shape:"hktxjm.zhanLue",appType:"yuanJing",defaultName:"愿景",name:"愿景",`datas.placeholders`:"1",`datas.parentIdd`:"node-5c6cb4d3-857c-41d9-908f-4ae68d5da60f",`datas.extendField`:"其它1",idd:"node-7f532b96-bc60-47d9-a394-ca4b5c97d7d0",nodeVersion:"1",model:"hktxjmmodel9",nodeStatu:"ACTIVE"}] as maps, 'hktxjmmodel9' as model
unwind maps as map
MERGE (s:hktxjm { model:model, idd:map.idd, nodeStatu:'ACTIVE' })
ON CREATE SET s = map, s.model = model
ON MATCH SET s += map
RETURN s

liseri avatar Sep 14 '22 10:09 liseri

I use new data to recheck the issue, also the problem; a->b, a->c, the query all return result is b has incoming a, c has incoming a, but a only has outgoing b, a do not include c.

liseri avatar Sep 14 '22 10:09 liseri

I encountered the same problem,and i think the reason is org.springframework.data.neo4j.core.mapping.DefaultNeo4jEntityConverter.populateFrom(MapAccessor, NodeDescription<?>, PersistentPropertyAccessor<?>, Predicate<Neo4jPersistentProperty>, boolean, Collection<Relationship>, Collection<Node>) avoid unnecessary re-assignment of values

Jafeyyu avatar Sep 15 '22 02:09 Jafeyyu

Could you create a database setup including the relationships? I tried to reproduce it on with the data from #2583 and also added the definition of the incoming relationships to the model. But all seems fine (for samples I thought it was worth looking at them). Would be good to get the problematic subset to figure out the problem.

meistermeier avatar Sep 15 '22 10:09 meistermeier

Sorry, forget to paste relationsips cql; I will give you a new simpler data(only 3 nodes and 2 rels) to test as below create node

with [{shape:"hktxjm.zhanLue",appType:"ceDu",defaultName:"测度",name:"testCeDu1",description:"test",`datas.extendField`:"test",idd:"node-6edd138d-03e6-4b15-8cd2-4c2e930b75f1",nodeVersion:"1",model:"hktxjmmodel9",nodeStatu:"ACTIVE"},{shape:"hktxjm.zhanLue",appType:"ceDu",defaultName:"测度",name:"testCeDu2",description:"test2",`datas.extendField`:"test2",idd:"node-7c067fa8-50f7-47df-b43a-1e41c0c4006d",nodeVersion:"1",model:"hktxjmmodel9",nodeStatu:"ACTIVE"},{shape:"hktxjm.yingYong",appType:"yingYongY",defaultName:"域",name:"testYingYongY1",idd:"node-41498c56-7f82-4444-927a-39e73fc1bfa1",nodeVersion:"1",model:"hktxjmmodel9",nodeStatu:"ACTIVE"}] as maps, 'hktxjmmodel9' as model
unwind maps as map
MERGE (s:hktxjm { model:model, idd:map.idd, nodeStatu:'ACTIVE' })
ON CREATE SET s = map, s.model = model
ON MATCH SET s += map
RETURN s

create relationship

with [{sourceNodeIdd:"node-6edd138d-03e6-4b15-8cd2-4c2e930b75f1",targetNodeIdd:"node-41498c56-7f82-4444-927a-39e73fc1bfa1",edgeIdd:"edge-93e6c4f8-f06e-4136-835a-69d7d5f5696b",edgeMap:{shape:"hktxjm.guanXi",appType:"parent",defaultName:"父",name:"testParent1",idd:"edge-93e6c4f8-f06e-4136-835a-69d7d5f5696b"}},{sourceNodeIdd:"node-7c067fa8-50f7-47df-b43a-1e41c0c4006d",targetNodeIdd:"node-41498c56-7f82-4444-927a-39e73fc1bfa1",edgeIdd:"edge-7503965e-c9fa-4547-aba1-6a6df16c67cd",edgeMap:{shape:"hktxjm.guanXi",appType:"parent",defaultName:"父",name:"testParent2",idd:"edge-7503965e-c9fa-4547-aba1-6a6df16c67cd"}}] as lists, 'hktxjmmodel9' as model
unwind lists as item
MATCH (s:hktxjm { model:model, idd:item.sourceNodeIdd, nodeStatu:'ACTIVE' })
MATCH (t:hktxjm { model:model, idd:item.targetNodeIdd, nodeStatu:'ACTIVE' })
MERGE (s)-[r:guanXi {idd:item.edgeIdd}]->(t)
ON CREATE SET r = item.edgeMap
ON MATCH SET r += item.edgeMap
WITH item,model,s,r,t
MATCH(s2:hktxjm { model:model})-[r2:guanXi {idd:item.edgeIdd}]->(t2:hktxjm { model:model})
WHERE s.idd <> s2.idd or t.idd <> t2.idd
DELETE r2
RETURN s,collect(r),collect(t)

test findone(by node idd=node-41498c56-7f82-4444-927a-39e73fc1bfa1) it is normal has 2 incoming rels the result is :

{
    "cell": "yingYongY",
    "name": "testYingYongY1",
    "description": null,
    "datas": {},
    "idd": "node-41498c56-7f82-4444-927a-39e73fc1bfa1",
    "model": "hktxjmmodel9",
    "nodeVersion": "1",
    "nodeStatu": "ACTIVE",
    "incomingEdges": [
        {
            "cell": "parent",
            "name": "testParent2",
            "description": null,
            "datas": {},
            "idd": "edge-7503965e-c9fa-4547-aba1-6a6df16c67cd",
            "otherSideNode": {
                "cell": "ceDu",
                "name": "testCeDu2",
                "description": "test2",
                "datas": {
                    "extendField": "test2"
                },
                "idd": "node-7c067fa8-50f7-47df-b43a-1e41c0c4006d",
                "model": "hktxjmmodel9",
                "nodeVersion": "1",
                "nodeStatu": "ACTIVE",
                "incomingEdges": null,
                "outgoingEdges": null,
                "banBenEdges": null
            }
        },
        {
            "cell": "parent",
            "name": "testParent1",
            "description": null,
            "datas": {},
            "idd": "edge-93e6c4f8-f06e-4136-835a-69d7d5f5696b",
            "otherSideNode": {
                "cell": "ceDu",
                "name": "testCeDu1",
                "description": "test",
                "datas": {
                    "extendField": "test"
                },
                "idd": "node-6edd138d-03e6-4b15-8cd2-4c2e930b75f1",
                "model": "hktxjmmodel9",
                "nodeVersion": "1",
                "nodeStatu": "ACTIVE",
                "incomingEdges": null,
                "outgoingEdges": null,
                "banBenEdges": null
            }
        }
    ],
    "outgoingEdges": null,
    "banBenEdges": null
}

test findAll, it is not normal, the node(idd=node-41498c56-7f82-4444-927a-39e73fc1bfa1) has only one incoming rel the result is :

{
    "content": [
        {
            "cell": "ceDu",
            "name": "testCeDu1",
            "description": "test",
            "datas": {
                "extendField": "test"
            },
            "idd": "node-6edd138d-03e6-4b15-8cd2-4c2e930b75f1",
            "model": "hktxjmmodel9",
            "nodeVersion": "1",
            "nodeStatu": "ACTIVE",
            "incomingEdges": null,
            "outgoingEdges": [
                {
                    "cell": "parent",
                    "name": "testParent1",
                    "description": null,
                    "datas": {},
                    "idd": "edge-93e6c4f8-f06e-4136-835a-69d7d5f5696b",
                    "otherSideNode": {
                        "cell": "yingYongY",
                        "name": "testYingYongY1",
                        "description": null,
                        "datas": {},
                        "idd": "node-41498c56-7f82-4444-927a-39e73fc1bfa1",
                        "model": "hktxjmmodel9",
                        "nodeVersion": "1",
                        "nodeStatu": "ACTIVE",
                        "incomingEdges": null,
                        "outgoingEdges": null,
                        "banBenEdges": null
                    }
                }
            ],
            "banBenEdges": null
        },
        {
            "cell": "ceDu",
            "name": "testCeDu2",
            "description": "test2",
            "datas": {
                "extendField": "test2"
            },
            "idd": "node-7c067fa8-50f7-47df-b43a-1e41c0c4006d",
            "model": "hktxjmmodel9",
            "nodeVersion": "1",
            "nodeStatu": "ACTIVE",
            "incomingEdges": null,
            "outgoingEdges": [
                {
                    "cell": "parent",
                    "name": "testParent2",
                    "description": null,
                    "datas": {},
                    "idd": "edge-7503965e-c9fa-4547-aba1-6a6df16c67cd",
                    "otherSideNode": {
                        "cell": "yingYongY",
                        "name": "testYingYongY1",
                        "description": null,
                        "datas": {},
                        "idd": "node-41498c56-7f82-4444-927a-39e73fc1bfa1",
                        "model": "hktxjmmodel9",
                        "nodeVersion": "1",
                        "nodeStatu": "ACTIVE",
                        "incomingEdges": null,
                        "outgoingEdges": null,
                        "banBenEdges": null
                    }
                }
            ],
            "banBenEdges": null
        },
        {
            "cell": "yingYongY",
            "name": "testYingYongY1",
            "description": null,
            "datas": {},
            "idd": "node-41498c56-7f82-4444-927a-39e73fc1bfa1",
            "model": "hktxjmmodel9",
            "nodeVersion": "1",
            "nodeStatu": "ACTIVE",
            "incomingEdges": [
                {
                    "cell": "parent",
                    "name": "testParent1",
                    "description": null,
                    "datas": {},
                    "idd": "edge-93e6c4f8-f06e-4136-835a-69d7d5f5696b",
                    "otherSideNode": {
                        "cell": "ceDu",
                        "name": "testCeDu1",
                        "description": "test",
                        "datas": {
                            "extendField": "test"
                        },
                        "idd": "node-6edd138d-03e6-4b15-8cd2-4c2e930b75f1",
                        "model": "hktxjmmodel9",
                        "nodeVersion": "1",
                        "nodeStatu": "ACTIVE",
                        "incomingEdges": null,
                        "outgoingEdges": null,
                        "banBenEdges": null
                    }
                }
            ],
            "outgoingEdges": null,
            "banBenEdges": null
        }
    ],
    "pageable": {
        "sort": {
            "empty": true,
            "sorted": false,
            "unsorted": true
        },
        "offset": 0,
        "pageSize": 20,
        "pageNumber": 0,
        "unpaged": false,
        "paged": true
    },
    "last": true,
    "totalElements": 3,
    "totalPages": 1,
    "size": 20,
    "number": 0,
    "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
    },
    "first": true,
    "numberOfElements": 3,
    "empty": false
}

liseri avatar Sep 16 '22 02:09 liseri

@meistermeier hi, Has the problem recurred?

liseri avatar Sep 22 '22 02:09 liseri

Unfortunately to get down to this issue, I was not able to reproduce the problem. Can you please report the SDN version, you are using when observing the issue?

I also ran the following test multiple (a thousand) times for single and all results.

@Test
void testIncomingEdges() {
    //  var entity = repository.findByIdd("node-41498c56-7f82-4444-927a-39e73fc1bfa1");
    var entities = repository.findAll();
    var entity = entities.stream().filter(s -> s.getIdd().equals("node-41498c56-7f82-4444-927a-39e73fc1bfa1")).findFirst().get();
    Assertions.assertThat(entity.getEdgeIncoming()).hasSize(2);
}

meistermeier avatar Sep 26 '22 11:09 meistermeier

@meistermeier Sorry, I not provide sufficient details. the sdn is:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-neo4j</artifactId>
            <version>2.7.2</version>
        </dependency>

The findAll repository code is used custom query:

Optional<TxjmNodeEntity> findOneByModelAndIdd(String model, String idd);

List<TxjmNodeEntity> findAllByModel(String model);

@Transactional(readOnly = true)
    @Query(value = "MATCH (s:hktxjm)\n" +
                   "WHERE s.model = $model AND s.nodeStatu = 'ACTIVE' \n" +
                   "WITH s :#{orderBy(#pageable)} SKIP $skip LIMIT $limit \n" +
                   "optional MATCH (s)-[r]-(t:hktxjm)\n" +
                   "WHERE type(r) in ['guanXi']\n" +
                   "RETURN s,collect(r), collect(t)",
            countQuery = "MATCH (s:hktxjm)\n" +
                    "WHERE s.model = $model AND s.nodeStatu = 'ACTIVE' \n" +
                    "RETURN count(s)"
    )
    Page<TxjmNodeEntity> queryAllNodes(String model, Pageable pageable);

The test code is:

@Test
    void testFindOne() {
        String model = "hktxjmmodel9";
        //findOne
        Optional<TxjmNodeEntity> entityForFindOne = txjmNodeEntityRepository.findOneByModelAndIdd(model,"node-41498c56-7f82-4444-927a-39e73fc1bfa1");
        
        Assertions.assertThat(entityForFindOne.get().getIncomingEdges()).hasSize(2);
    }

    @Test
    void testFindAll() {
        String model = "hktxjmmodel9";
        //findAll
        List<TxjmNodeEntity> entitiesForFindAll = txjmNodeEntityRepository.findAllByModel(model);
        TxjmNodeEntity entityFromFindAll = entitiesForFindAll.stream().filter(s -> s.getIdd().equals("node-41498c56-7f82-4444-927a-39e73fc1bfa1")).findFirst().get();

        Assertions.assertThat(entityFromFindAll.getIncomingEdges()).hasSize(2);
    }

    @Test
    void testCustomQueryAll() {
        String model = "hktxjmmodel9";
        //costum query all
        Page<TxjmNodeEntity> entitiesForCustomQueryAll = txjmNodeEntityRepository.queryAllNodes(model, Pageable.ofSize(20));
        TxjmNodeEntity entityFromCustomQueryAll = entitiesForCustomQueryAll.stream().filter(s -> s.getIdd().equals("node-41498c56-7f82-4444-927a-39e73fc1bfa1")).findFirst().get();

        Assertions.assertThat(entityFromCustomQueryAll.getIncomingEdges()).hasSize(2);
    }

The test result is :

image

liseri avatar Sep 27 '22 08:09 liseri

That's interesting, I tried to reproduce it but still no chance. I created a (non-)reproducer https://github.com/meistermeier/neo4j-issues-examples/tree/master/gh-2592 It might be worth a look what I am doing different/wrong.

meistermeier avatar Sep 29 '22 08:09 meistermeier

@meistermeier hi, i add a new test fun in https://github.com/meistermeier/neo4j-issues-examples/pull/4, it is fail. I want to try new snapshot version you mentioned in #2600 ,but I cannot get the 6.3.4 version.

liseri avatar Sep 30 '22 02:09 liseri

The version is 6.3.4-GH-2600-SNAPSHOT not just 6.3.4. Also you need to define Spring's snapshot (and milestone) repositories.

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-neo4j</artifactId>
    <version>6.3.4-GH-2600-SNAPSHOT</version>
</dependency>
<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/snapshot</url>
        <releases>
            <enabled>false</enabled>
        </releases>
    </repository>
</repositories>

with this in place, the test runs smoothly.

meistermeier avatar Sep 30 '22 10:09 meistermeier

@meistermeier Hi, The test passed when I use the new snapshot version you provided. Thanks a lot. Looking forward to the release of the new Release version.

liseri avatar Oct 08 '22 02:10 liseri

Thank you very much for your feedback. I will close this issue now and keep #2600 to have a single reference issue for the fix.

meistermeier avatar Oct 08 '22 05:10 meistermeier