Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I get a "Cartesian Product" warning?

I am still trying to understand why I get a cartesian product warning for a certain format for a query in neo4j and not for another. This is how I set up my database:

CREATE (q:Form {version: "1.0"})
CREATE (q:Question {text: "Sector de la empresa", active: true})

I then tried the following query:

MATCH
(f:Form {version: "1.0"}),
(q:Question {text: "Sector de la empresa"})
CREATE (f)-[:asks]->(q)
RETURN f, q

However, I get the following warning:

This query builds a cartesian product between disconnected patterns.
If a part of a query contains multiple disconnected patterns,
this will build a cartesian product between all those parts.
This may produce a large amount of data and slow down query processing.
While occasionally intended, it may often be possible to reformulate the
query that avoids the use of this cross product, perhaps by adding a
relationship between the different parts or by using OPTIONAL MATCH
(identifier is: (q))

When I use the following query, it does not give me this warning:

MATCH (f:Form {version: "1.0"})
WITH f
(q:Question {text: "Sector de la empresa"})
CREATE (f)-[:asks]->(q)
RETURN f, q

nor when I use this query:

MATCH (f:Form {version: "1.0"})
MATCH (q:Question {text: "Sector de la empresa"})
CREATE (f)-[:asks]->(q)
RETURN f, q

I used this following article as a resource, but it still didn't fully answer my question: Why does neo4j warn: "This query builds a cartesian product between disconnected patterns"?

Why do I get a cartesian product for some formats of a query and not others? Also, I do not fully understand what a cartesian product warning is.

like image 428
Aly Avatar asked Jun 10 '16 14:06

Aly


People also ask

How to avoid Cartesian product in Neo4j?

To connect them to each other you either need two foreaches (one nested in the other), or to UNWIND both lists back to rows. That would get you a cartesian product as a result, but without doing the extra work from the back-to-back matches, then you create your relationships.

What is the use of Cartesian product?

Most computers display images as a raster of points called pixels that can be addressed by their coordinates. These coordinates are ordered pairs and hence elements of a Cartesian product.

What is the Cartesian product of the table?

A Cartesian product is the result of joining every row in one table with every row in another table. This occurs when there is no WHERE clause to restrict rows. While this is legitimate in some cases, most occurrences of a Cartesian product are mistakes.

What is optional match in neo4j?

An OPTIONAL MATCH matches patterns against your graph database, just like a MATCH does. The difference is that if no matches are found, OPTIONAL MATCH will use a null for missing parts of the pattern. OPTIONAL MATCH could be considered the Cypher equivalent of the outer join in SQL.


2 Answers

If you are MATCHing on two different labels without any relationships between them, then you'll get this warning. The reason is because if you do:

MATCH (a:Foo), (b:Bar)

It's Neo4j's job to find every possible combination of those two nodes. So for the first match of a it will return a row for every match of b, for the second match of a it will again return a row for every match of b, and so on. So you'll get (number of Foo nodes) x (number of Bar nodes) total rows in your result. As your database grows this is really bad for performance.

I can see that you're filtering on version for Form and text for Question, so that would help. That may even give you just one Form node and one Question node. So as long as you have an index on the Form(version) and Question(text) the query should be quite quick. Neo4j can't tell (or at least, isn't currently implemented to be able to tell) how many rows are going to be returned, so it gives a warning saying that your query could be potentially slow.

like image 131
Brian Underwood Avatar answered Sep 28 '22 05:09

Brian Underwood


They are all cartesian

Having read your question, for a second there, my cypher-world imploded - all three queries should involve a cartesian product.

Having checked (on both the console and a local DB - both version 3.3.0), turns out I'm sane - they do all involve a cartesian product:

The execution plan of the first query, showing a cartesian product

The execution plan of the second query, showing a cartesian product

The execution plan of the third query, showing a cartesian product

Why there is only a warning in the first case (still in version 3.3.0 I have no clue - you simply need to run the planner to figure this out, and if this isn't firing the warning what does? Some dumb cypher logic?

Cypher basics

Cypher queries are made of parts, each can be either update (write) or read.

As far as read parts are concerned, this is what happens:

  • Neo4j picks a starting point which it reckons will yield the least 'hits'. It will go through each of these hits...
  • ...traversing the graph using the node/relationship pattern.
  • It does so repeatedly until no more patterns are matched.

If you have something like this:

(a {name:'Bill'})-->(b:Dog)

The plan might look something like this.

  • For each node (AKA AllNodeScan):
    • Filter based on the predicate (name == 'bill')
    • Get all outgoing --> relationships
    • For each relationship:
      • Get the end node
      • Filter based on predicate (:Dog)

The important thing is that whilst to find (a) we need to scan each node. But we simply traverse the graph to find the (b)s - no AllNodeScan for the latter.

(There are variants of AllNodeScan, see Starting Node Operators)

When your query is something like this:

MATCH (f:Form {version: "1.0"}), (q:Question {text: "Sector de la empresa"})

Neo is forced to do an AllNodeScan for both f and q - There is no pattern to traverse between them. This can potentially create a result set of an f * q size, which could be huge.

like image 29
Izhaki Avatar answered Sep 28 '22 04:09

Izhaki