Hiya!

I think there is a lot of circumstances where being able to express things
like this makes sense. Michael and Wes have useful workarounds, but longer
term, I'm growing more and more convinced that we need better sub query
support. Something similar to SQL's EXISTS method would solve it without
the oddity of having predicates introduce identifiers. Something like this
would then be possible:

MATCH (a)
WHERE EXISTS (
  MATCH (a)-->(b:Label)
  WHERE a.prop = b.prop AND b.foo = "BAR"
)

This is not something we have planned for at the moment, but WDYT about
this direction instead of introducing identifiers?

Cheers,

Andrés

On Wed, Oct 29, 2014 at 6:13 AM, S. Kai Chen <[email protected]>
wrote:

> Hi, Michael,
>
> Thanks for the quick response!  And sorry about my own late reply: I had
> to go to a meeting right after I sent the first post and have just gotten
> back right now.
>
> Interesting suggestion with using path expression and collection
> predicates.  How would you include an optional path here?  Is there a way
> to return the 4th circle, which won't match in the path?
>
> I agree union is a clearer way to write it, especially when there are only
> 2 subsets.  However, I'm concerned about situations where one needs to
> include more subsets, sometimes maybe 4 or 5 or more.  With compound
> criteria, it's easier to read when all the conditions are in the where
> clause -- to me at least.
>
> What makes it harder for me is that I'm generating the Cypher queries
> dynamically, based on a tiny query language that the user puts together
> using a web UI (originally through a lot of NOT|AND|OR dropdowns but now
> with a rich editor embellished with auto-complete).  So essentially the
> query comes in the form of such-and-such-class-with-these-properties; it
> gets compiled and then gets translated into Cypher after it passes
> validation.
>
> It's definitely a lot easier to translate that user query into a Cypher
> that actually allows new identifiers in the predicate; a lot more work to
> do the translation in terms of union -- maybe even more than what I might
> spend implementing the identifier in the predicates in Cypher.
>
> Does that make sense or would some examples with the more complex type of
> queries help?
>
> Cheers,
> Kai
>
>
> On Tue, Oct 28, 2014 at 6:54 PM, Michael Hunger <
> [email protected]> wrote:
>
>> You can also do number 3) because you actually don't need "s"
>>
>> match (c:circle) where (c.id=4) or ( (c)-->(:square{id:1}) ) return c
>>
>> Only #4 is trickier but still possible, only not so nice to read :)
>>
>> *I think union is still the better choice here as you combine 2 different
>> use-cases:*
>>
>> match (c:circle)-->(s:square) where s.id < 4 return c,s
>> union match (c:circle{id:4}) optional match (c)-->(s) return c,s;
>>
>> But you can use a path expression as a collection of paths, which you
>> then can use in collection predicates (all, any, single, none) , filter,
>> extract, reduce.
>>
>> match (c:circle)
>> where any(p in (c)-->(:square) where last(nodes(p)).id <http://s.id/> <
>> 4) return c
>>
>>
>>
>> On Wed, Oct 29, 2014 at 2:12 AM, Kai Chen <[email protected]>
>> wrote:
>>
>>> Hi,
>>>
>>> I'm not sure if this is the right place to submit this.  I was going to
>>> open an FR ticket on github but changed my mind because I thought maybe
>>> it's better to have a discussion here first.
>>>
>>> I've run into a couple of places where being able to say something like
>>>     match (n ....) /* criteria doesn't involve binding an identifier n2
>>> */
>>>     where n.prop > threshold or ( (n)-->(n2:label{qualifier:"value"})
>>> and n2.prop < threshold )
>>> would make the query a lot easier to read.  I'm aware that, in the case
>>> of 'OR', I could use a union after using 2 separate match clauses.  And
>>> that's what I've been going along with, until now when I need to
>>> dynamically translate a user query into Cypher.  Here using union can
>>> become very complex, as the relationships can nest arbitrary levels deep.
>>> But if we had a syntax that can bind new identifiers in predicates, it
>>> would be very easy and, more importantly, very readable.
>>>
>>> I've prepared a few simple use cases below.  (See attached image of the
>>> model)
>>>
>>> * Data Set
>>>
>>>   Below creates a set of 7 nodes consisting of 4 circles, 2 squares, and
>>> 1 triangle.
>>>   2 circles point to 2 squares, 1 circle point to the triangle, and
>>> another circle is dangling.
>>>
>>> create (:circle{id:1})-[:uses]->(:square{id:1});
>>> create (:circle{id:2})-[:uses]->(:square{id:2});
>>> create (:circle{id:3})-[:uses]->(:triangle{id:3});
>>> create (:circle{id:4});
>>>
>>> * Verification
>>>
>>> neo4j-sh (?)$ match (c:circle) optional match (c)-[r]->(n) return c,
>>> labels(c), r, n, labels(n);
>>>
>>> +--------------------------------------------------------------------------+
>>> | c              | labels(c)  | r          | n              | labels(n)
>>>    |
>>>
>>> +--------------------------------------------------------------------------+
>>> | Node[10]{id:1} | ["circle"] | :uses[7]{} | Node[11]{id:1} | ["square"]
>>>   |
>>> | Node[12]{id:2} | ["circle"] | :uses[8]{} | Node[13]{id:2} | ["square"]
>>>   |
>>> | Node[14]{id:3} | ["circle"] | :uses[9]{} | Node[15]{id:3} |
>>> ["triangle"] |
>>> | Node[16]{id:4} | ["circle"] | <null>     | <null>         | <null>
>>>   |
>>>
>>> +--------------------------------------------------------------------------+
>>>
>>> * Queries
>>>
>>> 1) Circles that don't point to any Squares
>>>     This is easy and can be supported with the current syntax.
>>>
>>> neo4j-sh (?)$ match (c:circle) where not (c)-->(:square) return c;
>>>
>>>
>>> +----------------+
>>> | c              |
>>> +----------------+
>>>     | Node[14]{id:3} |
>>>     | Node[16]{id:4} |
>>> +----------------+
>>>
>>> 2) Circles that don't point Square(1)
>>>     This can also be accomplished using the current syntax.  So path is
>>> already supported; the only thing missing is to bind an identifier which
>>> would allow filtering with additional predicate expressions.
>>>
>>> neo4j-sh (?)$ match (c:circle) where not (c)-->(:square{id:1}) return c;
>>> +----------------+
>>> | c              |
>>> +----------------+
>>> | Node[12]{id:2} |
>>> | Node[14]{id:3} |
>>> | Node[16]{id:4} |
>>> +----------------+
>>>
>>> 3) Circles that point to Square(1) or with id(4)
>>>     Here it's starting to get hairy.  Union queries may also become
>>> grossly inefficient if the result sets are large.  This is where
>>> identifier-binding in predicates can help make query more efficient and
>>> maybe easier to read also.
>>>
>>> neo4j-sh (?)$ match (c:circle{id:4}) return c
>>> > union match (c:circle)-->(s:square{id:1}) return c;
>>> +----------------+
>>> | c              |
>>> +----------------+
>>> | Node[16]{id:4} |
>>> | Node[10]{id:1} |
>>> +----------------+
>>>
>>>     Would like to say
>>>         match (c:circle) where (c.id=4) or ( (c)-->(s:square{id:1}) )
>>> return c
>>>
>>> 4) (Circle, Square) where Circle is either id(4) or points to Squares
>>> with id < 4
>>>     This is where the union query is beginning to deteriorate in
>>> comprehensibility.  One has to remember to use optional match.  And I don't
>>> know what it would look like, if the optional match is 2 or 3 levels deep.
>>> Now imagine this is a portion of a larger query, where the 'c' nodes are
>>> found by matching in another pattern.  Using the union would require one to
>>> duplicate that code in all the subsets.  Having more shared predicates
>>> would have the same effect.
>>>
>>> neo4j-sh (?)$ match (c:circle)-->(s:square) where s.id < 4 return c,s
>>> > union match (c:circle{id:4}) optional match (c)-->(s) return c,s;
>>> +---------------------------------+
>>> | c              | s              |
>>> +---------------------------------+
>>> | Node[10]{id:1} | Node[11]{id:1} |
>>> | Node[12]{id:2} | Node[13]{id:2} |
>>> | Node[16]{id:4} | <null>         |
>>> +---------------------------------+
>>>
>>>
>>> Hope that was clear.  And sorry for the long post.
>>>
>>> I also would be more than happy to help implement this if it's not too
>>> difficult and someone can point me to the right place to start -- it'd be a
>>> feature that I'd really use a lot.
>>>
>>> Cheers,
>>> Kai
>>>
>>> --
>>> You received this message because you are subscribed to the Google
>>> Groups "Neo4j" group.
>>> To unsubscribe from this group and stop receiving emails from it, send
>>> an email to [email protected].
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>
>>  --
>> You received this message because you are subscribed to a topic in the
>> Google Groups "Neo4j" group.
>> To unsubscribe from this topic, visit
>> https://groups.google.com/d/topic/neo4j/N5k06664XYI/unsubscribe.
>> To unsubscribe from this group and all its topics, send an email to
>> [email protected].
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>  --
> You received this message because you are subscribed to the Google Groups
> "Neo4j" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> For more options, visit https://groups.google.com/d/optout.
>

-- 
You received this message because you are subscribed to the Google Groups 
"Neo4j" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to