Davlet D
Davlet D

Reputation: 2158

Nested FOREACH in Neo4J Cypher runs infinite

I try to run the following query, where I have 10 chains in database and 10 Countries. and using the array of 20 addresses. Then I loop through those to create a store for each combination of country, chain and address, expecting 10*10*20 combinations - 2000 stores. But instead, query runs until around 200 000 records are created and then halts with unknown error.

MATCH (Chains:Chain), (Countries:Country)
WITH Collect(Chains) as ch, 
     Collect(Countries) as cou, 
['Ullevål','Blindern','Centrum','Kringså','Lysaker','Skøyen','Fornebu','Stortinget','Nationalteatre','KarlJohan',
'Gamle','Grunerløkka','Grønland','Majorstuen','Snarøya','Asker','Sandvika','Drammen','Gothenburg','Stockholm'] as addresses

FOREACH(country in cou |
    FOREACH (c in ch |
            FOREACH (a in addresses |
                CREATE (s:Store {name:c.name+"_"+a, address:a})
                CREATE (s-[:BELONGS_TO]->c)
                CREATE (s-[:IN]->country)               )))

Upvotes: 1

Views: 2396

Answers (1)

Nicole White
Nicole White

Reputation: 7790

If you have the following data...

FOREACH (i IN RANGE(1, 10) | 
    CREATE (:Chain {name:'Chain' + i}), 
           (:Country {name:'Country' + i})
)

...the first part of your query

MATCH (Chains:Chain), (Countries:Country)
WITH COLLECT(Chains) AS ch, COLLECT(Countries) AS cou

will result in length-100 collections for both ch and cou, since the MATCH is retrieving a Cartesian product (10^2 = 100). So that is where your 200,000 records are coming from: 100 * 100 * 20. You can verify this with the following:

MATCH (Chains:Chain), (Countries:Country)
WITH COLLECT(Chains) AS ch, COLLECT(Countries) AS cou
RETURN LENGTH(ch), LENGTH(cou)

which will tell you that each collection is length-100:

LENGTH(ch)  LENGTH(cou)
100         100

You can fix this by using COLLECT(DISTINCT thing) in the first part of your query to collect only the unique things...

MATCH (Chains:Chain), (Countries:Country)
WITH COLLECT(DISTINCT Chains) AS ch, COLLECT(DISTINCT Countries) AS cou
RETURN LENGTH(ch), LENGTH(cou)

...which will tell you that ch and cou are length-10 collections:

LENGTH(ch)  LENGTH(cou)
10          10

Now your nested FOREACH will be creating 10 * 10 * 20 = 2000 records:

MATCH (Chains:Chain), (Countries:Country)
WITH Collect(DISTINCT Chains) as ch, 
     Collect(DISTINCT Countries) as cou, 
     ['Ullevål','Blindern','Centrum','Kringså','Lysaker','Skøyen','Fornebu','Stortinget','Nationalteatre','KarlJohan',
      'Gamle','Grunerløkka','Grønland','Majorstuen','Snarøya','Asker','Sandvika','Drammen','Gothenburg','Stockholm'] as addresses

FOREACH(country in cou |
    FOREACH (c in ch |
        FOREACH (a in addresses |
            CREATE (s:Store {name:c.name+"_"+a, address:a})
            CREATE (s-[:BELONGS_TO]->c)
            CREATE (s-[:IN]->country)               )))

"Added 2000 labels, created 2000 nodes, set 4000 properties, created 4000 relationships, returned 0 rows in 1262 ms."

Upvotes: 5

Related Questions