Processing Results

Throughout this course, we will use the Asynchronous API to retrieve and process results from Neo4j. To use the you will need to to define async methods and use the await keyword to await the results.

Results can also be retrieved synchronously by omitting the Async suffix.

Result Cursors

The IResultCursor implementation returned by calls to session.RunAsync() or tx.RunAsync() provides access to the result as an asynchronous stream of IRecord s along with additional metadata about the query and result.

The records in the result is lazily retrieved and could only be visited once in a sequential order. A Record refers to the keyed set of values specified in the RETURN portion of the statement.

c#
Using Cursors
// Run a query within a Read Transaction
var res = await session.ExecuteReadAsync(async tx => {
    var cursor = await tx.RunAsync(@"
        MATCH(p: Person) -[:ACTED_IN]->(m: Movie)
        WHERE m.title = $title // (1)
        RETURN p.name AS name
        LIMIT 10
    ", new { title = "Arthur" });

    var records = await cursor.ToListAsync();
    return records.Select(x => x["name"].As<string>());
});

In the previous code block, the ToListAsync() method is called on the cursor implementation to convert all results returned by the Neo4j DBMS as a list. The Select() method is then called to retrieve the name value returned in each Record.

Records can be consumed in order using a while loop, or more the commonly used approach is to use the ToListAsync() function as above, which will consume the results in order and return a set.

c#
Accessing Records
// Get single row, error when more/less than 1
IRecord row = await cursor.SingleAsync();
// Materialize list
List<IRecord> rows = await cursor.ToListAsync();

// iteration
while (await cursor.FetchAsync())
{
    IRecord next = cursor.Current;
}

Where a singe result is expected, you can call the SingleAsync() function to retrieve the first record. If zero, or more than one record are retrieve an error will be thrown.

Records

The IRecord interface provides access to an individual record returned by Neo4j.

c#
Accessing Records
// column names
IReadOnlyList<string> columns = row.Keys;
// check for existence
bool movieExists = row.Values.ContainsKey("movie");
// number of columns
int columnCount = row.Values.Count;
// get a numeric value (int, long, float, double)
int count = row["movieCount"].As<int>(0);
// get a bool value
bool isDirector = row["isDirector"].As<bool>();
// get node
INode node = row["movie"].As<INode>();

The .Keys property provides the available keys for the record. These relate to the aliases referenced in the RETURN clause of the Cypher statement.

You can use the square-bracket syntax to access a value, for example row["movie"].As<INode>() will access the movie item defined in the, cast as an implementation of the INode interface.

Result Summary

The meta data IResultCursor accessed by calling .Consume() include:

  • statistics on how many nodes and relationships were created, updated, or deleted as a result of the query,

  • the query type

  • database and server info

  • query plan with and without profile

  • notifications

You can find more detail in the API docs for IResultSummary

For example, to get information about how long the query took to complete, you can use the following property:

c#
Using the Result Summary
IResultSummary summary = await cursor.ConsumeAsync();
// Time in milliseconds before receiving the first result
TimeSpan availableAfter = summary.ResultAvailableAfter; // 10
// Time in milliseconds once the final result was consumed
TimeSpan consumedAfter = summary.ResultConsumedAfter; // 10

Another interesting part of the summary is the SummaryCounters available via the Counters property, which has update counts about a write-statement’s execution. You can check via counters.ContainsUpdates if there were any updates.

c#
Result Counters
ICounters counters = summary.Counters;
// some example counters
// nodes and relationships
bool containsUpdates = counters.ContainsUpdates;
int nodesCreated = counters.NodesCreated;
int labelsAdded = counters.LabelsAdded;
int relsDeleted = counters.RelationshipsDeleted;
int propertiesSet = counters.PropertiesSet;

// indexes and constraints
int indexesAdded = counters.IndexesAdded;
int constraintsRemoved = counters.ConstraintsRemoved;
// updates to system db
bool containsSystemUpdates = counters.ContainsSystemUpdates;
int systemUpdates = counters.SystemUpdates;

Check Your Understanding

1. What implementation is returned by the .RunAsync() method?

  • IResult

  • IResultCursor

  • List<IRecord>

  • List<IResult>

Hint

Results returned by the driver are held in an implementation of result cursor.

Solution

The answer is IResultCursor.

Lesson Summary

You now have all the information required to send Cypher queries to Neo4j and consume the results.

Next, we will look at the Cypher Type System and some of the considerations that you need to make when working with values coming from Neo4j in your Java application.