GraphStory/graph-kit-php — pull request #9 by ikwattro (2014-10-30 11:46:41+00:00)
Title: optimization following query

While in the demo database, the amount of nodes is not so important, I think having optimized queries in the core of your app will give benefit to users when they look at your code source.

I'll show up a simple modification to the `following` cypher query :

Your current Cypher query is the following :

```
MATCH (user { username:{u}}) WITH user MATCH (user)-[:FOLLOWS]->(users) RETURN users ORDER BY users.username
```

With such query, you will not take advantage from the label index nor the username property index. In fact the query will result in a global graph search for the entire pattern, take a look at the execution plan :

```
ColumnFilter
|
+Sort
|
+Extract
|
+Filter
|
+TraversalMatcher

+------------------+------+--------+-------------+-----------------------------------------------+
| Operator | Rows | DbHits | Identifiers | Other |
+------------------+------+--------+-------------+-----------------------------------------------+
| ColumnFilter | 14 | 0 | | keep columns users |
| Sort | 14 | 0 | | Cached( UNNAMEDS1965503953 of type Any) |
| Extract | 14 | 28 | | UNNAMEDS1965503953 |
| Filter | 14 | 930 | | Property(user,username(1)) == { AUTOSTRING0} |
| TraversalMatcher | 465 | 526 | | users, UNNAMED37, users |
+------------------+------+--------+-------------+-----------------------------------------------+

Total database accesses: 1484
```

If you want to benefit from the labels/property indexes, you need to specify a more accurate starting point. Telling Cypher exactly where he must start is your door to Graph heaven.

The query slightly modified :

```
MATCH (user { username:{u}}) WITH user MATCH (user)-[:FOLLOWS]->(users) RETURN users ORDER BY users.username
```

And a new look at the execution plan :

```
ColumnFilter
|
+Sort
|
+Extract
|
+Filter(0)
|
+SimplePatternMatcher
|
+Filter(1)
|
+NodeByLabel

+----------------------+------+--------+--------------------------+-----------------------------------------------+
| Operator | Rows | DbHits | Identifiers | Other |
+----------------------+------+--------+--------------------------+-----------------------------------------------+
| ColumnFilter | 14 | 0 | | keep columns users |
| Sort | 14 | 0 | | Cached( UNNAMEDS1965503953 of type Any) |
| Extract | 14 | 28 | | UNNAMEDS1965503953 |
| Filter(0) | 14 | 14 | | hasLabel(users:Person(0)) |
| SimplePatternMatcher | 14 | 14 | user, users, UNNAMED69 | |
| Filter(1) | 1 | 60 | | Property(user,username(1)) == { AUTOSTRING0} |
| NodeByLabel | 30 | 31 | user, user | :User |
+----------------------+------+--------+--------------------------+-----------------------------------------------+

Total database accesses: 147
```

You can see immediately that the execution plan does not use global matching anymore and takes advantage of built-in indexing system.

Here is the console link for quick tests : http://console.neo4j.org/?id=9ukiqg