cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

Spring boot reactive neo4j - ReactiveNeo4jClient query returning an empty result

I'm trying to execute a custom cypher query in Spring boot reactive app but always getting an empty output. I have tried executing the query in neo4j browser and I'm able to get an anticipated result. I have found similar kind of example here but it is with Neo4JClient not with Neo4jReactiveClient

When I tried debugging the code in MyDbClient -> getTeamWithPlayers the pointer is not entering into query.mappedBy function.

API

GetMapping("/teamAndPlayes/{teamId}")
public Mono<TeamWithPlayers> updateEventStatus(@RequestHeader("uid") String uid, @PathVariable Long teamId) {
    return myDbClient.getTeamWithPlayers(teamId);
}

ReactiveNeo4jClient Implementation

@Component
@RequiredArgsConstructor
public class MyDbClient {
    private final ReactiveNeo4jClient client;

    public Mono<TeamWithPlayers> getTeamWithPlayers(Long teamId) {
        String query = "Match (team:Team) where id(team) =$teamId match (team)-[:REL{is:'TEAM'}]->(u:User) "
                + "optional Match (u) -[role:ROLE]->(team) "
                + "with team, COLLECT(u) as users, collect({uid: u.uid, role : role.is}) as roles "
                + "return team {id:id(team), .name, .description, .location, .emailAddress, .profileImgURL, .createdOn, users: users, roles: roles}";
        return client.query(query).bind(teamId).to("teamId").fetchAs(TeamWithPlayers.class)
                .mappedBy((TypeSystem ts, Record r) -> {
                    Value team = r.get("team");
                    List<User> users = team.get("users").asList(u -> User.builder()
                            .firstName(u.get("firstName").asString()).lastName(u.get("lastName").asString()).build());
                    List<PlayerRole> roles = team.get("roles").asList(pr -> PlayerRole.builder()
                            .uid(pr.get("uid").asString()).role(pr.get("role").asString()).build());
                    return TeamWithPlayers.builder().id(team.get("id").asLong()).name(team.get("name").asString())
                            .description(team.get("description").asString()).location(team.get("location").asString())
                            .emailAddress(team.get("emailAddress").asString())
                            .createdOn(team.get("createdOn").asLocalDateTime()).users(users).roles(roles).build();
                }).one();
    }
}

Mapping classes
@Data
@Builder
@ToString
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class TeamWithPlayers {
    private Long id;
    private String name;
    private String description;
    private String location;
    private String emailAddress;
    private String profileImgURL;
    private LocalDateTime createdOn;
    List<User> users;
    List<PlayerRole> roles;
}


@Node
@Data
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User implements Entity {

    @Id
    @GeneratedValue
    private Long id;
    private String uid;
    private String firstName;
    private String lastName;
    private String emailAddress;
    private String profileImgURL;
    private LocalDateTime updatedAt;
    private LocalDateTime createdAt;
    private LocalDateTime lastLoggedInAt;
}

@Data
@ToString
@Builder
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class PlayerRole {
    String uid;
    String role;
}

POM dependencies **2.4.2**
<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-neo4j</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

Not sure what wrong in my approach

4 REPLIES 4

Not entering the mapping is a pretty good sign that there aren't any records to process. Your result is empty.
Given this even simpler example but keeping the structure of your HTTP endpoint:

@RestController
public class SomeController {

    private final ReactiveNeo4jClient client;

    public SomeController(@Autowired ReactiveNeo4jClient client) {
        this.client = client;
    }

    @GetMapping("/teamAndPlayes/{name}")
    public Mono<Thing> updateEventStatus(@PathVariable String name) {
        String query = "Match (thing:Thing) where thing.name = $name return thing {.name}";
        return client.query(query).bind(name).to("name").fetchAs(Thing.class)
                .mappedBy((TypeSystem ts, org.neo4j.driver.Record r) -> {
                    Value thing = r.get("thing");
                    return Thing.builder().name(thing.get("name").asString()).build();
                }).one();
    }

    @Data
    @Builder
    public static class Thing {
        private final String name;
    }
}

works fine for a given Thing with a certain name.

Thank you very much Meier.. your below comment helped me think in right direction.

Not entering the mapping is a pretty good sign that there aren't any records to process

I was in assumption that the ReactiveNeo4jClient will be connected to the database (Scoveer) based on the configuration

spring.data.neo4j.database=Scoveer

spring.neo4j.uri=bolt://localhost:11006
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=******
spring.data.neo4j.repositories.type=reactive

But by default it is connecting to neo4j default Db hence I was getting empty result

I have added below code to fix the issue. check .in(database()) the snippet

return client.query(query).in(database()).bind(teamId).to("teamId").fetchAs(TeamWithPlayers.class)
					.mappedBy((TypeSystem ts, Record r) -> {

						.....

					}).one();
private String database() {
		return databaseSelectionProvider.getDatabaseSelection().getValue();
}

@Bean
	DatabaseSelectionProvider databaseSelectionProvider(@Value("${spring.data.neo4j.database}") String database) {
		return () -> {
			String neo4jVersion = System.getenv("NEO4J_VERSION");
			if (neo4jVersion == null || neo4jVersion.startsWith("4")) {
				return DatabaseSelection.byName(database);
			}
			return DatabaseSelection.undecided();
		};
	}

Check here for more detailed example.

@gerrit.meier Why don't we align both spring data repositories and Neo4jClients with same behaviour by default connecting to configure database (spring.data.neo4j.database) ??

In current implementation by default Repositories are connecting to spring.data.neo4j.database and Neo4JClients are connecting default neo4j DB.

Thank you

We have now an issue to track for the DatabaseSelectionProvider support in the (Reactive)Neo4jClient Support for database selection in (Reactive)Neo4jClient · Issue #2159 · spring-projects/spring-data-...

Ok, Thank you for the update!

Nodes 2022
Nodes
NODES 2022, Neo4j Online Education Summit

All the sessions of the conference are now available online