1
votes

Using the community edition of neo4j v2.1.7, I am creating nodes to represent licenses.

Each license has terms and can "inherit" terms from another license. Terms local to a license shadow the same terms in the inherited license.

I am attempting to write a cypher query that will match on local and inherited terms.

This is easier to demonstrate with some code.

I create a license that inherits from another license:

create 
(l1:License)-[:inherits]->(l2:License),
(l1)-[:term]->(:Foo{value:"A"}),
(l1)-[:term]->(:Lar{value:"C"}),
(l2)-[:term]->(:Lar{value:"D"}),
(l2)-[:term]->(:Bar{value:"B"})

I want a query that will find licenses that have or inherit the following terms: Foo = A, Bar = B, Lar = C

So l1 would match since it has the right values for Lar and Foo and inherits Bar with the right value.

l2 wouldn't match since it does not have the right Lar and is missing Foo.

I tried the following, but it seems cumbersome and has at least two issues:

  1. I added "optional match (l1)-[:inherits]->(l2:License)" because I wanted to match on licenses that did not inherit other license terms. However, for some reason I don't grok, the optional match gives me both License nodes in the graph.

  2. How can I test whether a property is in a range? In other words, what if a License has a property that is a date like "20150101" and I want to test for values between 20140101 and 20140101? Since my "where" is testing the existence of paths, I don't see how to test whether a property is greater than or less than another value.

Incorrect cypher query:

match (l1:License)
optional match (l1)-[:inherits]->(l2:License)
where 
( 
   (l1)-[:term]->(:Foo{value:"A"}) OR 
   (
       not((l1)-[:term]->(:Foo{value:"A"}))
       and
       (l2)-[:term]->(:Foo{value:"A"})
   )
)
AND
(
   (l1)-[:term]->(:Bar{value:"B"}) or 
   (   
      not((l1)-[:term]->(:Bar{value:"B"}))
      and 
      (l2)-[:term]->(:Bar{value:"B"})
    )
)
AND
(
   (l1)-[:term]->(:Lar{value:"C"}) or 
   (
      not((l1)-[:term]->(:Lar{value:"C"})) and
      (l2)-[:term]->(:Lar{value:"C"})
   )
)
return count(l1)

Thanks in advance!

1

1 Answers

1
votes

I think you still think too much in relational terms, your query looks like a lot of joins.

I think something like this will suffice.

// find the 3 terms
MATCH (t1:Foo {value:"A"}),(t2:Bar {value:"B"}), (t3:Lar {value:"C"})
UNWIND [t1,t2,t3] as term
// use them as starting point
MATCH path = (l:License)-[:INHERITS*0..]->(l2)-[:TERM]->(term)
RETURN l,l2,term, path