neo4j / sdn-rx

Nextgen Spring Data module for Neo4j supporting (not only) reactive data access and immutable support
https://neo4j.github.io/sdn-rx
Apache License 2.0
65 stars 23 forks source link

Mapping self-relationships when using custom query #248

Closed ffaoudi closed 4 years ago

ffaoudi commented 4 years ago

Description During the migration of a project with SDN / OGM to SDN / RX we have a custom query which allows to create a tree with a depth, one of the nodes contains a relationships towards nodes of the same type: A -> B -> B

Using SDR / OGM the mapping is well done and the tree is well constructed. With SDN / RX the mapping is not well constructed from depth 2.

Query is : @Query("MATCH p=(partner:Partner {code: $partnerCode})-[:CHILD_ORGANISATIONS*0..4]->(org:Organisation) " + "RETURN partner, relationships(p), nodes(p)")

How to reproduce? I attach two projects to reproduce the probleme, one with SDN/OGM and another with SDN/RX

project using SDN/OGM project using SDN/RX

Bellow some data to insert in Neo4j database:

CREATE DATABASE test
USE test

CREATE (p:Partner {code:'partner-one', name: 'partner one'})
CREATE (o:Organisation {partnerCode: 'partner-one', code:'org-1', name: 'organisation one'})
CREATE (o:Organisation {partnerCode: 'partner-one', code:'org-2', name: 'organisation two'})
CREATE (o:Organisation {partnerCode: 'partner-one', code:'org-3', name: 'organisation three'})
CREATE (o:Organisation {partnerCode: 'partner-one', code:'org-4', name: 'organisation four'})
CREATE (o:Organisation {partnerCode: 'partner-one', code:'org-5', name: 'organisation five'})
CREATE (o:Organisation {partnerCode: 'partner-one', code:'org-6', name: 'organisation six'})
CREATE (o:Organisation {partnerCode: 'partner-one', code:'org-7', name: 'organisation seven'})

MATCH (p:Partner {code: 'partner-one'}) OPTIONAL MATCH (o:Organisation {code: 'org-1'}) CREATE (p)-[:CHILD_ORGANISATIONS]->(o)
MATCH (o1:Organisation {code: 'org-1'}) OPTIONAL MATCH (o2:Organisation {code: 'org-2'}) CREATE (o1)-[:CHILD_ORGANISATIONS]->(o2)
MATCH (o1:Organisation {code: 'org-1'}) OPTIONAL MATCH (o2:Organisation {code: 'org-6'}) CREATE (o1)-[:CHILD_ORGANISATIONS]->(o2)
MATCH (o1:Organisation {code: 'org-2'}) OPTIONAL MATCH (o2:Organisation {code: 'org-3'}) CREATE (o1)-[:CHILD_ORGANISATIONS]->(o2)
MATCH (o1:Organisation {code: 'org-3'}) OPTIONAL MATCH (o2:Organisation {code: 'org-4'}) CREATE (o1)-[:CHILD_ORGANISATIONS]->(o2)
MATCH (o1:Organisation {code: 'org-3'}) OPTIONAL MATCH (o2:Organisation {code: 'org-5'}) CREATE (o1)-[:CHILD_ORGANISATIONS]->(o2)
MATCH (o1:Organisation {code: 'org-5'}) OPTIONAL MATCH (o2:Organisation {code: 'org-7'}) CREATE (o1)-[:CHILD_ORGANISATIONS]->(o2)

Result Using SDN/RX

 java.lang.IndexOutOfBoundsException: Source emitted more than one item
    at reactor.core.publisher.MonoSingle$SingleSubscriber.onNext(MonoSingle.java:129) ~[reactor-core-3.3.5.RELEASE.jar:3.3.5.RELEASE]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ Handler com.test.sdnrx.controller.TreeController#partnerTree(String) [DispatcherHandler]
    |_ checkpoint ⇢ HTTP GET "/tree/partner-one" [ExceptionHandlingWebHandler]
Stack trace:
        at reactor.core.publisher.MonoSingle$SingleSubscriber.onNext(MonoSingle.java:129) ~[reactor-core-3.3.5.RELEASE.jar:3.3.5.RELEASE]
        at reactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onNext(MonoFlatMapMany.java:242) ~[reactor-core-3.3.5.RELEASE.jar:3.3.5.RELEASE]
        at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:114) ~[reactor-core-3.3.5.RELEASE.jar:3.3.5.RELEASE]
        at org.neo4j.driver.internal.shaded.reactor.core.publisher.StrictSubscriber.onNext(StrictSubscriber.java:89) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.reactor.core.publisher.FluxCreate$IgnoreSink.next(FluxCreate.java:618) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.reactor.core.publisher.FluxCreate$SerializedSink.next(FluxCreate.java:153) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.reactive.InternalRxResult.lambda$createRecordConsumer$3(InternalRxResult.java:95) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.handlers.pulln.BasicPullResponseHandler.handleRecord(BasicPullResponseHandler.java:134) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.handlers.pulln.BasicPullResponseHandler$State$2.onRecord(BasicPullResponseHandler.java:308) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.handlers.pulln.BasicPullResponseHandler.onRecord(BasicPullResponseHandler.java:89) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher.handleRecordMessage(InboundMessageDispatcher.java:97) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.messaging.v1.MessageReaderV1.unpackRecordMessage(MessageReaderV1.java:94) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.messaging.v1.MessageReaderV1.read(MessageReaderV1.java:65) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.async.inbound.InboundMessageHandler.channelRead0(InboundMessageHandler.java:83) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.async.inbound.InboundMessageHandler.channelRead0(InboundMessageHandler.java:35) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:321) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:295) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.async.inbound.MessageDecoder.channelRead(MessageDecoder.java:47) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:321) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:308) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:422) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerC
Some color-syntaxing enrichment can be applied with the following blockcode syntax

```jsonontext.invokeChannelRead(AbstractChannelHandlerContext.java:377) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at org.neo4j.driver.internal.shaded.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[neo4j-java-driver-4.0.1.jar:4.0.1-979f102fbfd27e6393f672da8c7d59f6fabbbe0e]
        at java.base/java.lang.Thread.run(Thread.java:830) ~[na:na]

Using SDN/OGM

{
  "id": 1,
  "code": "partner-one",
  "name": "partner one",
  "organisations": [
    {
      "id": 7,
      "partnerCode": "partner-one",
      "code": "org-1",
      "organisations": [
        {
          "id": 8,
          "partnerCode": "partner-one",
          "code": "org-6",
          "organisations": []
        },
        {
          "id": 2,
          "partnerCode": "partner-one",
          "code": "org-2",
          "organisations": [
            {
              "id": 3,
              "partnerCode": "partner-one",
              "code": "org-3",
              "organisations": [
                {
                  "id": 6,
                  "partnerCode": "partner-one",
                  "code": "org-5",
                  "organisations": []
                },
                {
                  "id": 4,
                  "partnerCode": "partner-one",
                  "code": "org-4",
                  "organisations": []
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
meistermeier commented 4 years ago

You are right, this is currently an intended limitation in SDN-RX to avoid complexity and as a result speed up the mapping process. I assume that your example only reflects a part of the problem because the result could easily be constructed with a Mono<Partner> findByCode(String code) repository method. We will investigate a little bit more on mapping dynamic length relationships in the next days/weeks. Thanks for raising this topic.

meistermeier commented 4 years ago

Since we are in a migration process to the original Spring Data Neo4j and have to archive this repository, the issue also go migrated to: https://jira.spring.io/browse/DATAGRAPH-1344 Please watch or comment on the issue in Jira for further communication.