Search code examples
neo4jclient

Neo4jClient shows strange behavior passing objects


I'm trying to query data and objects from my Neo4j database using Neo4jClient in ASP.NET MVC5.

I have to structure the returned data in specific objects. This works fine for IEnumerates, but does not work for single objects. I can't see any reason for this behavior as Neo4j itself is returning the results in the expected way.

Here a sample:

public class ProjectContainer
{
    public string relationship { get; set; }
    public int accesstype { get; set; }
    public ProjectData data { get; set; }
}

var query = graphClient.Cypher
    .Match("(positionData:Position)<-[relationData:HAS_POSITION]-(projectData:Project)")
    .Where((PositionData positionData) => positionData.uuid == uuid)
    .With("positionData, projectData, { relationship: type(relationData), accesstype:0, data: projectData } as container ")
    .Return((positionData, projectData, container) => new 
    {
        position = positionData.As<PositionData>(),
        projectdata = projectData.As<ProjectData>(),
        projectcontainer = container.As<ProjectContainer>(),
        projects = container.CollectAs<ProjectContainer>()
    });

var pos = query.Results.First();

Neo4j return a single JSON-object for container. pos.projects contains - as expected - an enumeration of 1 element of ProjectContainer including all properties an projectdata in "data". But I don't want an enumeration as it will be always 1 project.

The problems are:

  1. problem: pos.projectscontainer is not null, but only contains null-properties. It should be filled by the same content as projects[0], but this doesn't work.

  2. problem: if I rename the property "data" in "project" (the property itself and in with-clause), even pos.projectcontainer will be null. Why does the name of the property changes the behavior, is "data" any special keyword for the client?

I tested the query from query.Query.DebugQueryText in the Neo4j browser and the result is as expected, even if I name the property "project" or "data":

MATCH (positionData:Position)<-[relationData:HAS_POSITION]-(projectData:Project) WHERE (positionData.uuid = "c3a1eedc-5083-4784-a378-cfd2ba0bec57") WITH positionData, projectData, { relationship: type(relationData), accesstype:0, data: projectData } as container  RETURN positionData AS position, projectData AS projectdata, container AS projectcontainer, collect(container) AS projects

╒══════════════════════════════╤══════════════════════════════╤══════════════════════════════╕
│"projectdata"                 │"projectcontainer"            │"projects"                    │
╞══════════════════════════════╪══════════════════════════════╪══════════════════════════════╡
│{"description":"Project dummy │{"relationship":"HAS_POSITION"│[{"relationship":"HAS_POSITION│
│description","closed":false,"a│,"accesstype":"0","data":{"des│","accesstype":"0","data":{"de│
│ctive":true,"published":false,│cription":"Project dummy descr│scription":"Project dummy desc│
│"title":"Project dummy title",│iption","closed":false,"active│ription","closed":false,"activ│
│"uuid":"e4327251-d0c7-4e24-aa1│":true,"published":false,"titl│e":true,"published":false,"tit│
│2-cf7ade4a512a"}              │e":"Project dummy title","uuid│le":"Project dummy title","uui│
│                              │":"e4327251-d0c7-4e24-aa12-cf7│d":"e4327251-d0c7-4e24-aa12-cf│
│                              │ade4a512a"}}                  │7ade4a512a"}}]                │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘

Thanks for any help. Reiner


Solution

  • You're right that it is an existing issue - so this isn't a solution per se, but a work-around to get the object filled in.

    I don't know of a way to achieve what you want with anonymous types, only with a concrete class type, so if you had a class called Altogether which looked like this:

    public class Altogether
    {
        public PositionData position { get; set; }
        public ProjectData projectdata { get; set; }
        public ProjectContainer projectcontainer { get; set;}
    }
    

    you can then add this line to your current query:

    .With("{position: positionData, projectdata: projectData, projectcontainer: container} as altogether")

    And return that instead, so the whole query looks like:

    var query = graphClient.Cypher
        .Match("(positionData:Position)<-[relationData:HAS_POSITION]-(projectData:Project)")
        .Where((PositionData positionData) => positionData.uuid == uuid)
        .With("positionData, projectData, { relationship: type(relationData), accesstype:0, data: projectData } as container")
        .With("{position: positionData, projectdata: projectData, projectcontainer: container} as altogether") //<-- Add here
        .Return((altogether) => altogether.As<Altogether>()); //<-- Change here
    

    It's not great, but it will fill your objects properly :/