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.
// 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.
// 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.
// 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:
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.
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.