Skip to content

Spring-data-neo4j Custom Query Mapping return incomplete Relationships. #2600

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Qingsx opened this issue Sep 29, 2022 · 10 comments
Closed

Spring-data-neo4j Custom Query Mapping return incomplete Relationships. #2600

Qingsx opened this issue Sep 29, 2022 · 10 comments
Assignees
Labels
status: feedback-provided Feedback has been provided

Comments

@Qingsx
Copy link

Qingsx commented Sep 29, 2022

graph like this.

g

domain like this .

@Getter
@Setter
@Node("human")
public class Human {
    @Id
    private String name;
    private LocalDateTime birth;
    @Relationship(type = "CALL_TO", direction = Relationship.Direction.OUTGOING)
    private List<Link> callList;
    @Relationship(type = "CALL_TO", direction = Relationship.Direction.INCOMING)
    private List<Link> beCallList;


    public Human() {
        callList = new ArrayList<>();
        beCallList = new ArrayList<>();
    }
    @Override
    public String toString() {
        return "Human{" +
                "name='" + name + '\'' +
                ", birth=" + birth +
                ", callList=" + callList.size() +
                ", beCallList=" + beCallList.size() +
                '}';
    }
}
`

init databases like this
//    @Bean
CommandLineRunner initData(PersonRepository personRepository) {
    return args -> {

        List<Human> humans = genEmployee(4);
        Iterable<Human> humanListSave = personRepository.saveAll(humans);

        Human boss = new Human();
        boss.setBirth(LocalDateTime.now());
        boss.setName("boss");

        for (int i = 0; i < 2; i++) {
            for (Human human : humanListSave) {
                boss.getCallList().add(genLinkByHuman(human));
                personRepository.save(boss);
            }
        }

    };
}

when i used @query to custom query ,the relationship in the node is incomplete .
when i used findAll in CrudRepository ,the result is correct .

Custom Query like this .

public interface PersonRepository extends CrudRepository<Human,String> {

    @Query("MATCH(b:human)-[r]-(e) return b,collect(r),collect(e)")
    Collection<Human> customQuery();
}

personRepository.customQuery() that will return like

Human{name='employee-0', birth=2022-09-29T14:35:27.692236500, callList=0, beCallList=2}
Human{name='employee-1', birth=2021-09-29T14:35:27.692236500, callList=0, beCallList=2}
Human{name='employee-2', birth=2020-09-29T14:35:27.692236500, callList=0, beCallList=2}
Human{name='employee-3', birth=2019-09-29T14:35:27.692236500, callList=0, beCallList=2}
Human{name='employee-4', birth=2018-09-29T14:35:27.692236500, callList=0, beCallList=2}
Human{name='boss', birth=2022-09-29T14:35:27.969232, callList=2, beCallList=0}

=========
personRepository.findAll()will return like

Human{name='employee-0', birth=2022-09-29T14:35:27.692236500, callList=0, beCallList=2}
Human{name='employee-1', birth=2021-09-29T14:35:27.692236500, callList=0, beCallList=2}
Human{name='employee-2', birth=2020-09-29T14:35:27.692236500, callList=0, beCallList=2}
Human{name='employee-3', birth=2019-09-29T14:35:27.692236500, callList=0, beCallList=2}
Human{name='employee-4', birth=2018-09-29T14:35:27.692236500, callList=0, beCallList=2}
Human{name='boss', birth=2022-09-29T14:35:27.969232, callList=10, beCallList=0}

missing 8 relationship at customQuery .but two query return record is same.
Why did this happen.

When i use Neo4jClient,I also encounter this problem.

@Service
@RequiredArgsConstructor
public class HumanService {
    private final Neo4jClient neo4jClient;
    private final Neo4jMappingContext context;

    public Collection<Human> errorMapping() {
        BiFunction<TypeSystem, MapAccessor, Human> mappingFunctionFor = context.getRequiredMappingFunctionFor(Human.class);
        return neo4jClient.query("MATCH(b:human)-[r]-(e) return b,collect(r),collect(e)")
                .fetchAs(Human.class)
                .mappedBy((t, r) -> mappingFunctionFor.apply(t, r))
                .all();
    }

    public Collection<Human> correctMapping() {
        return neo4jClient.query("MATCH(b:human)-[r]-(e) return b,collect(r),collect(e)")
                .fetchAs(Human.class)
                .mappedBy((t, r) -> context.getRequiredMappingFunctionFor(Human.class).apply(t, r))
                .all();
    }
}

Is this a bug ,or I do something wrong ?

btw.
How can i move code (
@relationship(type = "CALL_TO", direction = Relationship.Direction.INCOMING)
private List beCallList; )
into projection? I need it only for querying

here is my example
https://github.com/Qingsx/neo4j-bug-reproduce.git

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Sep 29, 2022
@meistermeier meistermeier self-assigned this Sep 29, 2022
meistermeier added a commit that referenced this issue Sep 29, 2022
meistermeier added a commit that referenced this issue Sep 29, 2022
Misses dynamic relationships right now.
meistermeier added a commit that referenced this issue Sep 29, 2022
@meistermeier
Copy link
Collaborator

Thanks for reporting this issue. The logic is currently that already hydrated list will not get populated again. The result pattern of the generated queries is different. That's why they are working as expected.
A still WIP regarding dynamic relationships branch will be ready for test in the next hour.
To use it, please add the SDN dependency and version explicitly and add the Spring milestone and snapshot repositories to your Maven pom (or do the equivalent Gradle things).

<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>

@meistermeier
Copy link
Collaborator

Also tagging @liseri and linking #2592 here because I think it might be the very same problem.

@meistermeier meistermeier added this to the 6.3.4 (2021.2.4) milestone Sep 29, 2022
@Qingsx
Copy link
Author

Qingsx commented Sep 30, 2022

Thanks for the anwser ! It works.
As you said,if I remove the following code from the domain.

@Relationship(type = "CALL_TO", direction = Relationship.Direction.INCOMING)
 private List<Link> beCallList;

It works as expected.

Is there any way to move it into projectionDto ? It make my domain entity confusing . Because i need it only when i query, I don't want someone to accidentally use it at save operation.
The point is ,Is there any way to collect relationships by projection ?
Sometime we just interest in relationships (It has many properties !! ) ,like statisticing or something.
Traversaling relationships by rootNode that is too cumbersome.

@Qingsx
Copy link
Author

Qingsx commented Sep 30, 2022

When i change version to 6.3.4-GH-2600-SNAPSHOT.
If there are too many relationships,it will make stackoverflow.
企业微信截图_16645059215503

@repository
public interface XxRepository extends Neo4jRepository<Xx, String> {
Collection findAllBysId(String sId);
}

@Qingsx
Copy link
Author

Qingsx commented Sep 30, 2022

buchong

@meistermeier
Copy link
Collaborator

Obviously the question comes up: About how many relationships are we talking a few hundreds or more than 10.000?

For the other question: Projections are Entity bound. So at least you would have to use the defining entity as the projection base and add only the relationship.

@Qingsx
Copy link
Author

Qingsx commented Oct 1, 2022

Almost a thousand when i tried.

When i used Projections, ProjectionDto like this.

@Data
public class HumanProjectionDto {
    private String name;

    private List<Human> humansList;

//this one Caused by: org.neo4j.driver.exceptions.value.NotMultiValued: RELATIONSHIP is not iterable
 //   private List<Link> relationshipsList;
}

I want to collection bidirection relationship when i query.

    @Query("MATCH(h1:human {name:$name})-[r]-(e)  return h1,collect(r) as relationshipsList,collect(e) as humansList ")
    Collection<HumanProjectionDto> returnProjectionList(String name);

Relationships can't be iterable. I can't collect relationships in projectionDto. And Relationships information lose when i use projection.

I can't find any example or doc to achieve this .
I thought about solutions. but
1.@QueryResult not supported in SDN6,
2.Neo4jClient is too cumbersome to handle this one .
3.bidirection relationship will corrupt the domain and case hydrated issue.

    @Relationship(type = "CALL_TO", direction = Relationship.Direction.OUTGOING)
    private List<Link> callList;
    @Relationship(type = "CALL_TO", direction = Relationship.Direction.INCOMING)
    private List<Link> beCallList;
  1. projectionDto can't collect relationships.

meistermeier added a commit that referenced this issue Oct 7, 2022
@meistermeier
Copy link
Collaborator

I just updated the snapshot so it does not run into a StackOverflow (with our tests and an interpretation of you scenario of what might cause it). Would be great to get feedback. It took some time and is still a WIP, but I wanted to have a solid build that should now avoid following known relationships again and again (StackOverflow).

For the projection part: If you want to create something that is more like a wrapper for arbitrary values (and domain objects), you might get better -depending on the data model- results if you would do the mapping (partial) manually and use the Neo4jClient in this way: https://docs.spring.io/spring-data/neo4j/docs/current/reference/html/#neo4j-client.result-objects.mapping-functions. Maybe this helps you to handle the results easier.
Also taking the wrapper out of the set of possible projections of the entity, would prevent "accidental saves", you were mentioning..

@meistermeier meistermeier added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged labels Oct 7, 2022
@Qingsx
Copy link
Author

Qingsx commented Oct 8, 2022

I got your point .
It works now,thank you very much .
Looking forward to a solid build.
I will close this issue .

@Qingsx Qingsx closed this as completed Oct 8, 2022
@meistermeier
Copy link
Collaborator

Thank you very much for the fast feedback on this topic. I will reopen this issue for tracking purposes because there is still work to do on the branch before we can merge it.

@meistermeier meistermeier reopened this Oct 8, 2022
@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Oct 8, 2022
meistermeier added a commit that referenced this issue Oct 11, 2022
Misses dynamic relationships right now.

Closes #2600
@michael-simons michael-simons mentioned this issue Jan 4, 2023
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: feedback-provided Feedback has been provided
Projects
None yet
Development

No branches or pull requests

3 participants