# Is this query speed normal?

**URL:** <https://community.neo4j.com/t/is-this-query-speed-normal/27707>\
**Category:** Neo4j Graph Platform\
**Created:** [October 22, 2020, 7:44pm UTC](https://community.neo4j.com/t/is-this-query-speed-normal/27707 "2020-10-22T19:44:11Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![lingvisa](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/lingvisa/32/8150_2.png) [@lingvisa](https://community.neo4j.com/u/lingvisa)\
**Post date:** [October 22, 2020, 7:44pm UTC](https://community.neo4j.com/t/is-this-query-speed-normal/27707/1 "2020-10-22T19:44:11Z")

</div>

New Neo4j version.

My graph db has only 130,000 nodes and 5 labels. I do this simple counting:

`MATCH (m:Product) WHERE m.effect='corloring' AND m.source='web' RETURN count(m) as count`

In the Neo4j Brower, I can notice it below after the query:  
`Started streaming 1 records in less than 1 ms and completed after 107 ms.`

A '107 ms' time seems long for a single query for a tiny graph. I am on a powerful Mac Pro machine. Is this normal for neo4j query? For offline queries, it's fine.

If I build indexes for the 2 properties in the WHERE clause, would be it much faster?

---

<div class="post-metadata">

**Author:** ![jggomez](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/jggomez/32/7793_2.png) [@jggomez](https://community.neo4j.com/u/jggomez)\
**Post date:** [October 22, 2020, 8:34pm UTC](https://community.neo4j.com/t/is-this-query-speed-normal/27707/2 "2020-10-22T20:34:55Z")

</div>

Hi, The query may take a long time the first time but it could be less afterwards. The indexes can improve the query. You could create an compose index.

Try it!!

Thanks

---

<div class="post-metadata">

**Author:** ![mdfrenchman](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/mdfrenchman/32/6094_2.png) [@mdfrenchman](https://community.neo4j.com/u/mdfrenchman)\
**Post date:** [October 22, 2020, 9:09pm UTC](https://community.neo4j.com/t/is-this-query-speed-normal/27707/3 "2020-10-22T21:09:25Z")

</div>

Yes that's normal. Putting an index on the two properties you're using to filter will speed that up.

As @jggomez mentioned, the first time the query runs it'll typically be a lot slower. The second and subsequent runs the query plan is cached so returns much quicker.

Also, to use the query caching, if you are going to pass in the effect or source as more than just "coloring" and "web", you will want to make use of parameters as they will allow for reuse of the qeury via the cache.

Example of what I mean:  
This is better

```auto
MATCH (m:Product) WHERE m.effect = $effect AND m.source = $source 
RETURN count(*)

```

than different queries like this

```auto
MATCH (m:Product) WHERE m.effect = 'coloring' AND m.source = 'web' 
RETURN count(*)

MATCH (m:Product) WHERE m.effect = 'coloring' AND m.source = 'print' 
RETURN count(*)

```

---

<div class="post-metadata">

**Author:** ![lingvisa](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/lingvisa/32/8150_2.png) [@lingvisa](https://community.neo4j.com/u/lingvisa)\
**Post date:** [October 22, 2020, 9:55pm UTC](https://community.neo4j.com/t/is-this-query-speed-normal/27707/4 "2020-10-22T21:55:22Z")

</div>

Does the $effect and $source make a difference? In my code they are already variables, since they are passed into the function via the generic variable name 'property\_name' and 'property\_value'. In this case, property\_name is 'effect' and value is 'coloring'.

Par of the function looks like:

```auto
def create_where_clause(WHERE, channel, properties):
        property_clause = ''
        for property in properties:
            property_clause += " m." + property.name + "='" + property.value + "'" + " AND"

        if property_clause:
            if not WHERE:
                WHERE = "WHERE " + property_clause
            else:
                WHERE += property_clause

        if WHERE and WHERE.endswith(' AND'):
            WHERE = WHERE[:len(WHERE) - 4]

        return WHERE

```

I dont know whether this way is what you meant by using variables. To my understanding, the cypher statement composed this way is still a raw string eventually.

`cypher = match_clasue + where_clasue + return clause + limit_clause`

By the time cypher is executed by tx.run(cypher), it already becomes an explicit cypher query without variables, even though variable names are used to parsing and composing the query statement.

---

<div class="post-metadata">

**Author:** ![mdfrenchman](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/mdfrenchman/32/6094_2.png) [@mdfrenchman](https://community.neo4j.com/u/mdfrenchman)\
**Post date:** [October 22, 2020, 10:16pm UTC](https://community.neo4j.com/t/is-this-query-speed-normal/27707/5 "2020-10-22T22:16:07Z")

</div>

It has to be a variable when it reaches neo4j for it to work as a variable.  
So what you have, as you said, is still an explicit string with no variables and would not take advantage of the query caching.

Although, depending on size, and indexes, you might not need it.

---

<div class="post-metadata">

**Author:** ![lingvisa](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/lingvisa/32/8150_2.png) [@lingvisa](https://community.neo4j.com/u/lingvisa)\
**Post date:** [October 23, 2020, 12:25pm UTC](https://community.neo4j.com/t/is-this-query-speed-normal/27707/6 "2020-10-23T12:25:58Z")

</div>

So, as you can see, my whole cypher is composed based on function arguments, and these arguments can be optionally null. Take the code above as one example, both 'properties' and channel can be None, and 'properties' is a list of property.name and property.value, and whole WHERE clause can be optional. The template of my query composition is:

`cypher = match_clasue + where_clasue + return clause + limit_clause`

This allows me to design generic user interface like:

```auto
@not_all_none
def node_of_relation(*, property: str=None, relation: RelType=None, node_type: NodeType=None, channel: WBChannel=None, limit: int=3) -> List:
    return KG_APP.node_of_relation(property, relation, node_type, channel, limit)

```

Users then can do:

1. Query nodes with a relation specification: any node pairs with this relation. The label is the node type.  
node\_of\_relation(relation=RelType.brandHasTopProd, label=WBChannel.Computer, limit=3)

2. Query nodes(neighbors) with a specification of a property (whatever relations between the node and target nodes). The property is to identify the starting node.  
`node_of_relation(property='id=00001', label=WBChannel.Computer, limit=3`

3. Query nodes with a specific relation specification, and the starting node with the property specification. The property string is parsed as a list of property\_name and property\_value pair.  
`node_of_relation(relation=RelType.competitiveProduct, property='color=White;price=$100')`

Any of the node\_of\_relation could be optional and it is very flexible for users to use. and that's how the way my cypher statement is composed with the template of:

`cypher = match_clasue + where_clasue + return clause + limit_clause`

The problem might be the fact that I can not make use of query caching like this:

```auto
query = (
            "MATCH (p:Computer) "
            "WHERE p.name = $name AND $price"
            "RETURN p.name AS name"
        )
        result = tx.run(cypher, name=$name, price=$price)

```
