Issue
I want to return aggregate count along with the main attributes using SDN OGM.
This is my data in Neo4j
{
"identity": 777777,
"labels": [
"RootMarker"
],
"properties": {
"lastModifiedDate": 1666934940115,
"p5Latest": true,
"messageIds": [
"fake-900b-49ac-92c7-fake",
"fake-fd73-4058-b07b-fake"
],
"messageId": "fake-fd73-4058-b07b-fake",
"deviceId": "XXXXX",
"domainId": "fake-35d5-11ed-9299-fake",
"resources": 1,
"createdDate": 1666896513598,
"drniId": 111111111111,
"isFull": true,
"resyncId": "fake-46d3-4ab1-bf34-fake",
"status": "resync",
"latest": [
22
]
}
}
My Repo
public interface StackOverFlowRepository extends Neo4jRepository<RootMarkerDTO, Long> {
@Query("MATCH (n:RootMarker {current: true}) RETURN n.domainId as domainId, count(n.domainId) as count ORDER BY n.domainId")
List<TestProjections> getRootMarker();
}
My main objective is return attributes as well this count(n.domainId) as count
Both here below works
@Query("MATCH (n:RootMarker {current: true}) RETURN count(n.domainId) as count ORDER BY n.domainId")
Long itWorks1();
@Query("MATCH (n:RootMarker {current: true}) RETURN n.domainId as domainId ORDER BY n.domainId")
List<RootMarkerDTO> itWorks2();
RootMarkerDTO:
@Node(labels = "RootMarker")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class RootMarkerDTO{
@Id @GeneratedValue
private Long id;
private String domainId;
private String resyncId;
private String status;
private String deviceId;
}
This here do NOT work
@Node(labels = "RootMarker")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class RootMarkerDTO{
@Id @GeneratedValue
private Long id;
private String domainId;
private String resyncId;
private String status;
private String deviceId;
//Here
private Long count;
}
TestProjections
import lombok.Value;
@Value
public class TestProjections {
String domainId;
Long count;
}
Error:
org.springframework.data.neo4j.core.mapping.NoRootNodeMappingException: Could not find mappable nodes or relationships inside Record<{domainId: "78d89740-35d5-11ed-9299-d5f548819a2b", count: 280}> for org.springframework.data.neo4j.core.mapping.DefaultNeo4jPersistentEntity@6d2db15b
I missing something really important about SDN understanding. Any help is appreciated.
EDIT:
I have created this completed replicable setup.
https://github.com/goldman7911/spring-data-understanding
MyRepository there is a method customCount() with a more realistic scenario.
//THAT'S NOT WORKING
@Query("match (r:RootMarker) UNWIND r.messageIds as rx return r.resyncId as resyncId, count(rx) as counter")
List<MyDTO> customCount();
That's is the same return from Neo4j
And the error:
org.springframework.data.neo4j.core.mapping.NoRootNodeMappingException: Could not find mappable nodes or relationships inside Record<{resyncId: "fake-7777-4ab1-7777-fake", counter: 4}> for org.springframework.data.neo4j.core.mapping.DefaultNeo4jPersistentEntity@7fc420b8
EDIT2:
Following @meistermeier propose it works. I just do not understand why upper SDN layers can't handle it. What exactly is not capable of understand that.
public Collection<MyDTO> getRootMarkerByNeo4jClient() throws NoSuchElementException {
Collection<MyDTO> result = neo4jClient.query("match (r:RootMarker) UNWIND r.messageIds as rx return r.resyncId as resyncId, count(rx) as counter")
.fetchAs(MyDTO.class)
.mappedBy((typeSystem, record) -> {
String resyncId = record.get("resyncId").asString();
Long counter = record.get("counter").asLong();
return new MyDTO(resyncId, counter);
}).all();
return result;
}
Solution
A projection in Spring Data Neo4j always refers to an entity.
So adding e.g. the count
to it, will create a bunch of arbitrary properties.
This is something that SDN does not consider matching for the type of the repository or its projections.
For this cases I recommend to use the Neo4jClient
with the custom query and create the instance of the (non-)projection yourself.
neo4jClient.query("<your query>")
.fetchAs(TestProjections.class)
.mappedBy((typeSystem, record) -> {
Long domainId = record.get("domainId").asLong();
Long count = record.get("count").asLong();
return new TestProjections(domainId, count);
});
Answered By - meistermeier
Answer Checked By - Dawn Plyler (JavaFixing Volunteer)