0

using Neo4j 3.0.4

Want to conditionally create vertices if there is an optional match on a path:

with [
 {customer: 0001, created: 1474434591, version: "1.0.0"},
 {customer: 0001, created: 1474434500, version: "1.0.0"},
 {customer: 0003, created: 1474445743, version: "1.1.0"}
] as lines 
unwind lines as line
/// customers above already exist
MATCH (c:Customer {id: line.customer})
OPTIONAL MATCH (c)-[:HAS]->(co:Configuration) WHERE co.created >= line.created
WITH CASE WHEN co IS NULL THEN
  [1]
END as createInventory,
CASE WHEN co IS NOT NULL THEN 
  [count(co)]
END as throwError, c, line
FOREACH (x in createInventory |
  CREATE (n:Configuration {
   created: line.created,
   version: line.version
  })
  CREATE UNIQUE (c)-[:HAS]->(n)
)
FOREACH (x in throwError |
  CREATE (e:Error:Inventory { message: "Configuration " + line.created + " >= existing (" + x + ")" })
);

The expectation is that only 2 Configuration vertices would be created and associated with a Customer. Instead, all 3 Configuration instances are created. The write statements (i.e. create) are within the same transaction so I assume that individual writes aren't reflected when looping through the OPTIONAL MATCH. In this example, a row is a Configuration and I don't want to create new ones tied to the same Customer if it's created property is older than an existing. Thus the predicate in the OPTIONAL MATCH.

Any way to conditionally create and take into account writes within the same transaction (or Cypher statement)?

Frank Pavageau
  • 11,477
  • 1
  • 43
  • 53
Matthew Campbell
  • 1,864
  • 3
  • 24
  • 51
  • You say you "don't want to create new ones tied to the same Customer if it's created property is older than an existing", but your `co.created >= line.created` test seems to only allow the ones you say you don't want. Is there an error somewhere, or am I misunderstanding? – cybersam Sep 21 '16 at 19:33
  • @cybersam The **created** property in the second element in the lines array is "older" than the first. The first line creates the **HAS** relationship between the customer and the configuration vertices. The optional match should return null on the second line. That was my expectation. – Matthew Campbell Sep 21 '16 at 19:39

1 Answers1

2

Cypher never "loops" back up to prior clauses.

You might get what you expected by inserting this clause right after your UNWIND clause:

WITH
  line.customer AS lc,
  REDUCE(s = NULL, x IN COLLECT(line) | CASE WHEN s IS NULL OR x.created > s.created THEN x ELSE s END) AS line

This WITH clause gets, per distinct line.customer value, the line having the highest created value, dropping any other lines. (The reason this is "per customer" is because of the use of the COLLECT aggregation function within that WITH clause).

By the way, it makes no sense for your query to use CREATE UNIQUE instead of CREATE, since the n node that you created just before is brand new and therefore the specified pattern could not have existed before.

cybersam
  • 63,203
  • 6
  • 53
  • 76
  • @cypersam, Implicit group-by with **WITH** when the last part of the result set is an aggregation. Cool. Your solution plays nice even with LOAD. – Matthew Campbell Sep 22 '16 at 04:54