neo4j / neo4j-java-driver

Neo4j Bolt driver for Java
Apache License 2.0
333 stars 155 forks source link

Result fieldNames() empty after execution (fieldNames Map getting replaced) #46

Closed ckramp closed 9 years ago

ckramp commented 9 years ago

Hello, I created a Node with the following statement

CREATE (n:TestNode {name:'test'}) RETURN n;

When trying to retrieve the current Record's fieldNames, the result is empty.

Driver driver = GraphDatabase.driver( "neo4j://localhost" );    
Session session = driver.session();
Result rs = session.run("MATCH (n:TestNode) RETURN n;"); 
System.out.println(rs.fieldNames());

I saw Issue https://github.com/neo4j/neo4j-java-driver/issues/27 describing a similar problem and did some debugging.

I set a breakpoint within the SocketResponseHandler's handleSuccessMessage function and noticed that the collector's (in this case an instance of ResultBuilder) fieldNames Map gets filled with the correct fieldNames, but then another SUCCESS message comes in (within the same execution of session.run(...)) and replaces the Map containing the fieldNames with an empty Map, since that SUCCESS message had no field names attached and thus triggers the replacement.

public void fieldNames( String[] names )
{
    if ( names.length == 0 )
    {
        fieldLookup = Collections.EMPTY_MAP;
    }
...

The problem can also be illustrated through this example of a typical exchange by Nigel Small

 Client: RUN "MATCH (a:Person) RETURN a.name AS name, a.age AS age" {}
 Client: PULL_ALL
 Server: SUCCESS {"fields": ["name", "age"]} <---- fieldNames get set
 Server: RECORD ["Alice", 33]
 Server: RECORD ["Bob", 44]
 Server: RECORD ["Carol", 55]
 Server: SUCCESS {} <----- fieldNames get replaced with an empty map

As pointed out in Issue https://github.com/neo4j/neo4j-java-driver/issues/27, the field names only get sent in one SUCCESS message, so my question is whether this replacement is intended or a bug. If it is intended, is there a way to address stream's current Record object directly instead of using the fieldNames()/get() functions and how would fieldNames() be used, if it is empty after a succesful exchange?

I am looking foward to a reply and would be grateful for any kind of hints or suggestions.

Kind regards, Christian Kramp

zhenlineo commented 9 years ago

Hi Christian,

The fieldNames of the SUCCESS message are not get replaced, as the two SUCCESS messages are independent.

In the example,

 Client: RUN "MATCH (a:Person) RETURN a.name AS name, a.age AS age" {}
 Client: PULL_ALL
 Server: SUCCESS {"fields": ["name", "age"]} <---- a reply to RUN "MATCH..."
 Server: RECORD ["Alice", 33]
 Server: RECORD ["Bob", 44]
 Server: RECORD ["Carol", 55]
 Server: SUCCESS {} <----- a reply to PULL_ALL

As you could see, the first SUCCESS message is an answer to the RUN query, and then the second SUCCESS message is a reply to PULL_ALL. So having the second SUCCESS being empty is not considered to be an error.

Furthermore, we currently have a draft to expand the second empty SUCCESS message to provide more info while using PROFILE and EXPLAIN https://github.com/neo4j/neo4j/pull/5271 (doc: https://github.com/neo4j/neo4j/pull/5271/files#diff-1de199999691d6c1b9b19a1da6bb7073R199)

Then for your question: "is there a way to address stream's current Record object directly instead of using the fieldNames()/get() functions and how would fieldNames() be used, if it is empty after a succesful exchange?"

I am not quite understand what you want to achieve here. If I understand you correctly then you want to get the field names when a Record is still a RecordMessage object (the string received from the server)? If so then you need to capture the first SUCESS message and then parse the field names yourself. But why you want to achieve this? Why not just use the Result rs object?

Driver driver = GraphDatabase.driver( "neo4j://localhost" );    
Session session = driver.session();
Result rs = session.run("MATCH (n:TestNode) RETURN n;");  <-------- this one 
System.out.println(rs.fieldNames());

And you could find a lot of examples to use the Result object in ITs, e.g. https://github.com/neo4j/neo4j-java-driver/blob/master/driver/src/test/java/org/neo4j/driver/integration/StatementIT.java

Best, Zhen

ckramp commented 9 years ago

Hi Zhen,

thank you for taking the time to look into the issue.

To make the issue more clear, I will try to condense it as much as possible.

I have a clean instance of neo4j 3.0.0 and execute the following query to create a sample node:

CREATE (n:TestNode {name:'test'}) RETURN n;

Afterwards I am trying to run this query to get the created node:

MATCH (n:TestNode) RETURN n;

This works flawlessly in the neo4j webinterface.

When using the neo4j-java-driver and calling the fieldNames() function on the Result object now, it returns an empty Iterable.

Driver driver = GraphDatabase.driver( "neo4j://localhost" );    
Session session = driver.session();
Result rs = session.run("MATCH (n:TestNode) RETURN n;");  
System.out.println(rs.fieldNames()); <--- returns '[]'

I would have expected it to return '[n]' instead.

The documentation states the following:

/**
 * Get an ordered sequence of the field names in this result.
 *
 * @return field names
 */
Iterable<String> fieldNames();

From my understanding the Result rs from above should point to the first Record returned by session.run(...) and calling fieldNames() on rs should have the same result as using:

rs.single().fieldNames() <- returns '[n]'

I hope this makes the issue more clear and I am looking forward to your reply.

Kind regards, Christian

zhenlineo commented 9 years ago

Hi Christian,

Thanks a lot for this wonderful explanation. I got this problem now. You are totally correct and your first answer is probably the cause of this bug. We are working on a PR to fix this bug soon.

Thanks a lot again. Please let us know if you have any other problem/suggestion when you are using this driver.

Best, Zhen

zhenlineo commented 9 years ago

https://github.com/neo4j/neo4j-java-driver/pull/50

ckramp commented 9 years ago

Hello Zhen,

Thank you for your work in providing a fix for this problem. The issue has been resolved and fieldNames() is working now.

Kind regards, Christian