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

Cannot retrieve Boolean value from Relationship in a SDN query projection

Open JapuDCret opened this issue 1 year ago • 1 comments

Setup

So I've got the following structure

  • I've got Products that link to a Group
  • the (directed) relation has properties (for actual values check the very bottom) image

I'm using the following Spring Data repository query

@Repository
interface GroupRepository: ReactiveNeo4jRepository<Group, String> {
    @Query("MATCH (product:Product) -[r:HAS_GROUP]-> (group:Group)" +
        " WHERE group.id = \$groupId" +
        " RETURN p AS product, properties(r)")
    fun findProducts(groupId: String): Flux<ProductToGroupProjectionDTO>
}

and this is the DTO class

data class ProductToGroupProjectionDTO(
    var product: Product? = null,

    var enabled: Boolean? = null,

    var options: Map<String, String?> = emptyMap(),
)

The issue

The repository query works and returns the correct number of projections. But the resulting DTO only has the properties product and options set, enabled is null.

So if we look at the projection

RETURN product, properties(r)

What also does not work to the same effect

RETURN product, r{.*}

Workaround

But what somehow does work is either

RETURN product, properties(r), r.triable AS triable

or

RETURN product, r{.*}, r.triable AS triable

Question now is, why does SDN not automatically map a primitive (nullable) type (=Boolean) to the returned value?

Details

Technical stuff

  • Spring Boot: 3.0.1
  • Spring Data Neo4J: 7.0.0
  • Neo4J Image: neo4j:5.5.0-community

Here is the above mentioned and shown node structure, queried via

MATCH (product:Product)-[relation]->(group:Group)
RETURN product, relation, group
In text
╒════════════════════════════════════════════════════════════════╤═════════════════════════════════════╤═══════════════════════════════════════════════════════════╕
│"product"                                                       │"relation"                           │"group"                                                     │
╞════════════════════════════════════════════════════════════════╪═════════════════════════════════════╪════════════════════════════════════════════════════════════╡
│{"name":"Product 1","id":"50862361-8ae4-4c53-a2d7-f4719e5ebd98"}│{"options.foo":"bar","enabled":false}│{"name":"Group","id":"7c3e7a74-c989-4cb1-b44f-06b02ed01e57"}│
├────────────────────────────────────────────────────────────────┼─────────────────────────────────────┼────────────────────────────────────────────────────────────┤
│{"name":"Product 2","id":"6990700b-6818-4b4c-af8a-3e3c9ca6a132"}│{"options.foo":"bar","enabled":true} │{"name":"Group","id":"7c3e7a74-c989-4cb1-b44f-06b02ed01e57"}│
└────────────────────────────────────────────────────────────────┴─────────────────────────────────────┴────────────────────────────────────────────────────────────┘
In JSON
[
  {
    "product": {
      "identity": 4,
      "labels": [
        "Product"
      ],
      "properties": {
        "name": "Product 1",
        "id": "50862361-8ae4-4c53-a2d7-f4719e5ebd98"
      },
      "elementId": "4"
    },
    "relation": {
      "identity": 3,
      "start": 4,
      "end": 6,
      "type": "HAS_GROUP",
      "properties": {
        "options.foo": "bar",
        "enabled": false
      },
      "elementId": "3",
      "startNodeElementId": "4",
      "endNodeElementId": "6"
    },
    "group": {
      "identity": 6,
      "labels": [
        "Group"
      ],
      "properties": {
        "name": "Group",
        "id": "7c3e7a74-c989-4cb1-b44f-06b02ed01e57"
      },
      "elementId": "6"
    }
  },
  {
    "product": {
      "identity": 5,
      "labels": [
        "Product"
      ],
      "properties": {
        "name": "Product 2",
        "id": "6990700b-6818-4b4c-af8a-3e3c9ca6a132"
      },
      "elementId": "5"
    },
    "relation": {
      "identity": 4,
      "start": 5,
      "end": 6,
      "type": "HAS_GROUP",
      "properties": {
        "options.foo": "bar",
        "enabled": true
      },
      "elementId": "4",
      "startNodeElementId": "5",
      "endNodeElementId": "6"
    },
    "group": {
      "identity": 6,
      "labels": [
        "Group"
      ],
      "properties": {
        "name": "Group",
        "id": "7c3e7a74-c989-4cb1-b44f-06b02ed01e57"
      },
      "elementId": "6"
    }
  }
]

JapuDCret avatar Feb 22 '24 15:02 JapuDCret

Spring Data Neo4j supports projections for entity-based projections. What you are trying to achieve is a mixture of an entity and additional information. For those use cases I recommend to use the Neo4jClient with the custom query and make use of the entity aware mapping: https://docs.spring.io/spring-data/neo4j/reference/appendix/neo4j-client.html#neo4j-client.result-objects.mapping-functions. With this you have to manually aggregate the parts mapped entity and custom property but you get more control over what happens. The Neo4jClient is Spring transaction aware. You can still use it in combination with other repository or Neo4jTemplate calls within the same unit of work.

meistermeier avatar Feb 22 '24 20:02 meistermeier

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

spring-projects-issues avatar Feb 29 '24 20:02 spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

spring-projects-issues avatar Mar 07 '24 20:03 spring-projects-issues