0
votes

For me:

neo4jSession.query("MATCH (n:Widget) WHERE (n.partNumber STARTS WITH '001') RETURN n.partNumber AS id, n.name AS description, n.urn AS urn LIMIT 10", Collections.emptyMap());

works.

This query does not work:

    String query = "MATCH (n:Widget) " +
    "WHERE (n.partNumber STARTS WITH {queryString}) " +
    "RETURN n.partNumber AS id, n.name AS description, n.urn AS urn " +
    "LIMIT {limit}";
Map<String, Object> params = ImmutableMap
    .<String, Object>builder()
    .put("queryString", queryString)
    .put("limit", limit)
    .build();
return (List) neo4jOperations.queryForObjects(Object.class, query, params);

It returns an empty list. I've also tried with my actual domain object:

 return (List) neo4jOperations.queryForObjects(Widget.class, query, params);

with the same result.

I'm using OGM 2.0.2, neo4j 2.3.2, and Spring Data Neo4j 4.1.1 BUT I've tried this without neo4jOperations using Neo4jSession by itself with the same results. Oh, also I'm using a remove instance of neo4j with the HTTP driver.

Is there a bug in OGM?

MORE INFO:

Over the wire, I BELIEVE the messages look like this:

{      "statements":[  
      {  
         "statement":"MATCH (n:Widget) WHERE (n.partNumber STARTS WITH {queryString}) RETURN n.partNumber AS id, n.name AS description, n.urn AS urn LIMIT {limit}",
         "parameters":{  
            "queryString":"001",
            "limit":10
         },
         "resultDataContents":[  
            "graph"
         ],
         "includeStats":false
      }    ] }




{      "statements":[  
      {  
         "statement":"MATCH (n:Widget) WHERE (n.partNumber STARTS WITH '001') RETURN n.partNumber AS id, n.name AS description, n.urn AS urn LIMIT 10",
         "parameters":{  

         },
         "resultDataContents":[  
            "rest"
         ],
         "includeStats":true
      }    ] }

EVEN MORE INFORMATION:

I've tried this with Widget as both @QueryResult AND as @NodeEntity (w/ getters and setters).

@QueryResult
public class TypeaheadData {
  public Object id;
  public String description;
  public String uid;
}

AND

@NodeEntity
public class TypeaheadData {
  public Object id;
  public String description;
  public String uid;

  public TypeaheadData() {
  }

  public Object getId() {
    return id;
  }

  public void setId(Object id) {
    this.id = id;
  }

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public String getUid() {
    return uid;
  }

  public void setUid(String uid) {
    this.uid = uid;
  }
}

I've ALSO inspected the response over the wire and in both cases it looks like this:

{  
   "results":[  
      {  
         "columns":[  
            "id",
            "description",
            "uid"
         ],
         "data":[  
            {  
               "graph":{  
                  "nodes":[  

                  ],
                  "relationships":[  

                  ]
               }
            },
            {  
               "graph":{  
                  "nodes":[  

                  ],
                  "relationships":[  

                  ]
               }
            },
            {  
               "graph":{  
                  "nodes":[  

                  ],
                  "relationships":[  

                  ]
               }
            },
            {  
               "graph":{  
                  "nodes":[  

                  ],
                  "relationships":[  

                  ]
               }
            },
            {  
               "graph":{  
                  "nodes":[  

                  ],
                  "relationships":[  

                  ]
               }
            },
            {  
               "graph":{  
                  "nodes":[  

                  ],
                  "relationships":[  

                  ]
               }
            },
            {  
               "graph":{  
                  "nodes":[  

                  ],
                  "relationships":[  

                  ]
               }
            },
            {  
               "graph":{  
                  "nodes":[  

                  ],
                  "relationships":[  

                  ]
               }
            },
            {  
               "graph":{  
                  "nodes":[  

                  ],
                  "relationships":[  

                  ]
               }
            },
            {  
               "graph":{  
                  "nodes":[  

                  ],
                  "relationships":[  

                  ]
               }
            }
         ]
      }
   ],
   "errors":[  

   ]
}

IF i remove the Widget @NodeEntity, this is the request sent out:

{  
   "statements":[  
      {  
         "statement":"MATCH (n:Widget) WHERE (n.partNumber STARTS WITH {queryString}) RETURN n.partNumber AS id, n.name AS description, n.urn AS urn LIMIT {limit}",
         "parameters":{  
            "queryString":"001",
            "limit":10
         },
         "resultDataContents":[  
            "row"
         ],
         "includeStats":false
      }
   ]
}

AND having removed the Widget @NodeEntity, the response DOES have the correct data in it, but the mapper throws:

Scalar response queries must only return one column. Make sure your cypher query only returns one item.

2

2 Answers

2
votes

The OGM cannot map a collection of properties into a domain entity. Your query returns:

RETURN n.partNumber AS id, n.name AS description, n.urn AS urn 

but there is nothing to tell the OGM what kind of entity this is, if it is one at all.

Changing this to RETURN n should do the job with neo4jOperations.queryForObjects(Widget.class, query, params);

1
votes

Neo4j OGM can't handle mapping queries that don't return entire node objects. If you request only a subset of a node's properties in a query, you have to use the query method that returns a Result. And then you have to do the mapping yourself.

If you're using spring-data-neo4j, then you can use their @QueryResult annotation mixed with a repository @Query to handle the mapping for you. If you take a look at the code, they've finagled a mapper from from the metadata provided by the Neo4jSession.

The one exception is if you are querying for single properties on nodes, then the queryForObjects function will work.

Seem's like an oversight to me, but who am I to say.